Action-Based Structure Design for Data Layer

Action-Based Structure Design for Data Layer

Overview

การออกแบบ Data Layer โดยใช้ Action เป็นหลัก ที่ align กับ Abstraction Layer (shared-api-core) โดยแยก command/query และ Repository ทำหน้าที่เป็น Data Access Provider สำหรับ Action นั้นๆ

Core Principles

1. Action-Centric Organization

  • Action = Business operation ที่มีความหมายเดียวกันทั้ง Abstraction Layer และ Data Layer
  • Repository = Data Access Provider ที่ให้บริการทุก data operation ที่ Action นั้นต้องการ
  • 1:1 Mapping ระหว่าง Abstraction Layer Action กับ Data Layer Action

2. Command/Query Segregation

  • Command: Write operations (Create, Update, Delete)
  • Query: Read operations (Get, List, Search)
  • แยกตามประเภทของ Action ไม่ใช่ประเภทของ method ใน Repository

Folder Structure Standard

Abstraction Layer Structure (Reference)

shared-api-core/
├── document-process-api/
│   ├── command/
│   │   ├── return-request-lv40/          # Business Action
│   │   ├── approve-document/             # Business Action  
│   │   └── validate-document-lv30/       # Business Action
│   └── query/
│       ├── get-document-status/          # Business Action
│       ├── list-documents/               # Business Action
│       └── search-documents/             # Business Action

Data Layer Structure (Implementation)

store-prisma/src/
├── document-process-api/
│   ├── command/                          # Write Actions Group
│   │   ├── return-request-lv40/          # 🎯 Action Folder
│   │   │   ├── repository.ts             # ✅ Data Access Provider
│   │   │   │   ├── returnRequest()       #    Main command method
│   │   │   │   └── checkStatus()         #    Supporting query method
│   │   │   ├── return-request/           # 📁 Method Logic Folder
│   │   │   │   ├── task.ts               #    Business orchestration (thin)
│   │   │   │   ├── flows/                # 📁 Business workflow functions
│   │   │   │   │   ├── getAllActivity.flow.ts       # Workflow orchestration
│   │   │   │   │   ├── managePackSize.flow.ts       # Workflow orchestration  
│   │   │   │   │   └── manageAttachment.flow.ts     # Workflow orchestration
│   │   │   │   ├── db.logic.ts           #    Database operations (DAF)
│   │   │   │   └── data.logic.ts         #    Data transformations
│   │   │   ├── check-status/             # 📁 Method Logic Folder
│   │   │   │   ├── task.ts               #    Simple task coordination
│   │   │   │   ├── db.logic.ts           #    Database operations
│   │   │   │   └── data.logic.ts         #    Data transformations
│   │   │   ├── __tests__/                # ✅ Action Tests
│   │   │   │   ├── repository.test.ts
│   │   │   │   ├── return-request.test.ts
│   │   │   │   ├── check-status.test.ts
│   │   │   │   └── flows/                # Flow function tests
│   │   │   │       ├── getAllActivity.test.ts
│   │   │   │       ├── managePackSize.test.ts
│   │   │   │       └── manageAttachment.test.ts
│   │   │   └── index.ts                  # ✅ Repository Export Only
│   │   ├── approve-document/             # 🎯 Another Action
│   │   │   ├── repository.ts
│   │   │   ├── approve/
│   │   │   │   ├── task.ts
│   │   │   │   ├── flows/                # Flow functions (if needed)
│   │   │   │   ├── db.logic.ts
│   │   │   │   └── data.logic.ts
│   │   │   ├── validate/
│   │   │   └── index.ts                  # ✅ Repository Export Only
│   ├── query/                            # Read Actions Group
│   │   ├── get-document-status/          # 🎯 Action Folder
│   │   │   ├── repository.ts             # ✅ Data Access Provider
│   │   │   ├── get-status/               # 📁 Method Logic Folder
│   │   │   │   ├── task.ts               #    Simple query coordination
│   │   │   │   ├── db.logic.ts           #    Database operations
│   │   │   │   └── data.logic.ts         #    Data transformations
│   │   │   └── index.ts                  # ✅ Repository Export Only
│   │   ├── list-documents/               # 🎯 Another Action
│   │   │   ├── repository.ts
│   │   │   ├── list-all/
│   │   │   │   ├── task.ts
│   │   │   │   ├── flows/                # Flow functions (if complex)
│   │   │   │   ├── db.logic.ts
│   │   │   │   └── data.logic.ts
│   │   │   ├── filter-by-status/
│   │   │   └── index.ts                  # ✅ Repository Export Only
│   └── index.ts                          # ✅ API-Level Repository Re-exports

Key Design Decisions

1. Repository as Data Access Provider

// Repository provides ALL data access methods for the Action
export class ReturnRequestLv40V1Repo implements Repository {
  private readonly client: PrismaClient;
  constructor(
    client: PrismaClient,
    private readonly telemetryService: TelemetryMiddlewareService,
  ) {
    this.client = client;
    this.telemetryService = telemetryService;
  }
  
  // Main command operation - Delegate to task for complex business logic
  async returnRequest(
    context: UnifiedHttpContext,
    props: ReturnRequestInput,
  ): Promise<Result<ReturnRequestOutput, BaseFailure>> {
    const reqInput: ReturnRequestTaskInput = {
      context,
      telemetryService: this.telemetryService,
      client: this.client,
      props,
    };
    const res = await returnRequestTask(reqInput);
    return res;
  }
  
  // Supporting query operation - Delegate to task (consistent style)
  async checkStatus(
    context: UnifiedHttpContext,
    props: CheckStatusInput,
  ): Promise<Result<CheckStatusOutput, BaseFailure>> {
    const reqInput: CheckStatusTaskInput = {
      context,
      telemetryService: this.telemetryService,
      client: this.client,
      props,
    };
    const res = await checkStatusTask(reqInput);
    return res;
  }
}

2. Method-Specific Logic Folders

return-request-lv40/
├── repository.ts                         # Interface implementation
├── return-request/                       # Complex business logic
│   ├── task.ts                          # Main business orchestration  
│   ├── flows/                           # 📁 Separated flow functions
│   │   ├── getAllActivity.flow.ts       # Business workflow orchestration
│   │   ├── managePackSize.flow.ts       # Business workflow orchestration  
│   │   └── manageAttachment.flow.ts     # Business workflow orchestration
│   ├── db.logic.ts                      # Database operations (DAF functions)
│   └── data.logic.ts                    # Data transformations (pure functions)
└── check-status/                        # Supporting method logic
    ├── task.ts                          # Simple task coordination
    ├── db.logic.ts                      # Database operations
    └── data.logic.ts                    # Data transformations

3. Comprehensive Naming Conventions

Naming Convention Standards

Action Folder Naming

Command Actions (Write Operations)

  • Pattern: {command-name}
  • Rules:
    • ใช้ kebab-case
    • ใช้ชื่อ command ตามที่กำหนดใน business requirement
    • อาจมี version identifier ตามความจำเป็น
  • Examples:
    • return-request-lv40 - command name พร้อม level identifier
    • approve-document - command name สำหรับ approve document
    • create-application - command name สำหรับสร้าง application
    • update-status-process-class - command name สำหรับ update status
    • validate-document-lv30 - command name พร้อม level identifier
    • requestReturn - camelCase ไม่ใช่ kebab-case
    • lv40-return-request - ลำดับคำไม่ถูกต้อง

Query Actions (Read Operations)

  • Pattern: {query-name}
  • Rules:
    • ใช้ kebab-case
    • ใช้ชื่อ query ตามที่กำหนดใน business requirement
    • มักเริ่มด้วย get/list/find/search แต่ไม่บังคับ
  • Examples:
    • get-document-status - query name สำหรับดึง document status
    • list-documents - query name สำหรับดึงรายการ documents
    • find-registration-data - query name สำหรับค้นหา registration
    • search-documents-by-criteria - query name สำหรับค้นหา documents
    • get-list-process-type - query name สำหรับดึงรายการ process types
    • documentStatus - camelCase ไม่ใช่ kebab-case
    • getAllDocuments - camelCase และไม่เป็น kebab-case

File and Class Naming

Repository Class Naming

  • Pattern: {CommandName}{Version}Repo
  • Rules:
    • PascalCase
    • Command/Query name แปลงเป็น PascalCase
    • เพิ่ม version identifier ถ้ามี
    • ลงท้ายด้วย “Repo”
  • Examples:
    • ReturnRequestLv40V1Repo - จาก command return-request-lv40 + version V1
    • ApproveDocumentRepo - จาก command approve-document
    • GetDocumentStatusRepo - จาก query get-document-status
    • ListDocumentsRepo - จาก query list-documents
    • ValidateDocumentLv30Repo - จาก command validate-document-lv30
    • ReturnRequestRepository - ยาวเกินไป
    • returnRequestRepo - camelCase ไม่ใช่ PascalCase

Method Naming in Repository

  • Pattern: {actionVerb}{Entity}
  • Rules:
    • camelCase
    • ตรงกับ interface ใน Abstraction Layer
    • สื่อความหมายชัดเจน
  • Examples:
    • returnRequest() - main command method
    • checkStatus() - supporting query method
    • approveDocument()
    • getDocumentDetail()
    • listApplications()
    • return_request() - snake_case ไม่ใช่ camelCase
    • doReturn() - ไม่สื่อความหมาย

Task Function Naming

  • Pattern: {methodName}Task
  • Rules:
    • camelCase
    • ตรงกับ method name + “Task”
    • เป็น named export function
  • Examples:
    • returnRequestTask - สำหรับ returnRequest method
    • approveDocumentTask - สำหรับ approveDocument method
    • getDocumentStatusTask - สำหรับ getDocumentStatus method
    • ReturnRequestTask - PascalCase สำหรับ class ไม่ใช่ function
    • taskReturnRequest - ลำดับคำผิด

DAF (Database Access Function) Naming

  • Pattern: {verb}{Entity}DAF
  • Rules:
    • camelCase
    • เริ่มด้วย database verb (get/create/update/delete/find/list)
    • ตามด้วย entity name
    • ลงท้ายด้วย “DAF”
  • Examples:
    • getApplicationDAF - ดึง application record
    • updateStatusDAF - อัพเดท status
    • createDocumentDAF - สร้าง document record
    • deleteRegistrationDAF - ลบ registration
    • findApplicationsByStatusDAF - ค้นหา applications ตาม status
    • getApplication - ไม่มี DAF suffix
    • applicationGet - ลำดับคำผิด

Data Logic Function Naming

  • Pattern: {operation}{Entity}{Direction?}
  • Rules:
    • camelCase
    • เริ่มด้วย operation (transform/validate/format/parse)
    • ตามด้วย entity name
    • เพิ่ม direction ถ้าจำเป็น (ToOutput/ToInput/FromRaw)
  • Examples:
    • transformApplicationToOutput - แปลง raw data เป็น output format
    • validateDocumentInput - validate input data
    • formatStatusDisplay - format status สำหรับแสดงผล
    • parseRequestData - parse request data
    • transformRawToEntity - แปลง raw database data
    • applicationTransform - ลำดับคำผิด
    • transformApp - entity name ไม่ชัดเจน

Method Folder Naming

  • Pattern: {method-name} (kebab-case)
  • Rules:
    • ตรงกับ method name แต่เป็น kebab-case
    • ใช้เฉพาะ method ที่มี complex logic
  • Examples:
    • return-request/ - สำหรับ returnRequest method
    • check-status/ - สำหรับ checkStatus method
    • approve-document/ - สำหรับ approveDocument method
    • get-document-detail/ - สำหรับ getDocumentDetail method
    • returnRequest/ - camelCase ไม่ใช่ kebab-case
    • return_request/ - snake_case ไม่ใช่ kebab-case

Interface and Type Naming

  • Pattern: {MethodName}{Type}
  • Rules:
    • PascalCase
    • เริ่มด้วย method name ใน PascalCase
    • ลงท้ายด้วยประเภท (Input/Output/TaskInput/Result)
  • Examples:
    • ReturnRequestInput - input type สำหรับ returnRequest
    • ReturnRequestOutput - output type สำหรับ returnRequest
    • ReturnRequestTaskInput - task input type
    • CheckStatusOutput - output type สำหรับ checkStatus
    • returnRequestInput - camelCase ไม่ใช่ PascalCase
    • ReturnRequestInputType - มี Type suffix ซ้ำซ้อน

File Naming Standards

TypeScript File Naming (camelCase with Logic Suffix)

  • Pattern: {fileName}.{type}.ts
  • Rules:
    • ใช้ camelCase สำหรับชื่อไฟล์หลัก
    • ใช้ .logic.ts suffix สำหรับ logic files เพื่อระบุประเภท
    • ใช้ .flow.ts suffix สำหรับ flow files
    • ใช้ .test.ts suffix สำหรับ test files
  • Examples:
    • repository.ts - repository implementation
    • task.ts - task function
    • db.logic.ts - database access functions (logic file type)
    • data.logic.ts - data transformation functions (logic file type)
    • getAllActivity.flow.ts - flow function
    • repository.test.ts - test file
    • dbLogic.ts - ไม่ได้ระบุว่าเป็น logic file type
    • dataLogic.ts - ไม่ได้ระบุว่าเป็น logic file type

Test File Naming

  • Pattern: {targetFileName}.test.ts (camelCase)
  • Rules:
    • ใช้ camelCase สำหรับชื่อไฟล์
    • ชื่อไฟล์ที่ test ตามด้วย .test.ts
    • อยู่ใน tests folder ของ action
  • Examples:
    • repository.test.ts - test repository class
    • returnRequest.test.ts - test return-request method logic
    • task.test.ts - test task function
    • db.logic.test.ts - test DAF functions
    • getAllActivity.test.ts - test flow function
    • repositoryTest.ts - ไม่มี .test. pattern
    • test-repository.ts - kebab-case และลำดับคำผิด

Static Import Strategy (2-Level Support)

รองรับการ import ใน 2 รูปแบบเพื่อความยืดหยุ่น:

// ✅ Most performant - direct import to specific action
import { ReturnRequestLv40V1Repo } from 
  '@feedos-frgm-system/api-data/document-process-api/command/return-request-lv40';

import { ApproveDocumentRepo } from 
  '@feedos-frgm-system/api-data/document-process-api/command/approve-document';

import { GetDocumentStatusRepo } from 
  '@feedos-frgm-system/api-data/document-process-api/query/get-document-status';

Level 2: API-Level Import (Convenience)

// ✅ Convenient - import multiple repositories from API level
import { 
  ReturnRequestLv40V1Repo,
  ApproveDocumentRepo,
  GetDocumentStatusRepo,
  ListDocumentsRepo 
} from '@feedos-frgm-system/api-data/document-process-api';

Export Structure Implementation

Action-Level Index Files (Primary Export)

// ✅ command/return-request-lv40/index.ts
// Export Repository class only - no internal types
export { ReturnRequestLv40V1Repo } from './repository';

// 🔒 Internal files remain private:
// - return-request/task.ts (not exported)
// - return-request/flows/*.flow.ts (not exported)
// - db.logic.ts, data.logic.ts (not exported)

API-Level Index Files (Convenience Re-export)

// ✅ document-process-api/index.ts
// Direct re-exports for performance (no nested barrels)

// Command Repository exports
export { ReturnRequestLv40V1Repo } from './command/return-request-lv40';
export { ApproveDocumentRepo } from './command/approve-document';
export { ValidateDocumentLv30Repo } from './command/validate-document-lv30';

// Query Repository exports  
export { GetDocumentStatusRepo } from './query/get-document-status';
export { ListDocumentsRepo } from './query/list-documents';
export { SearchDocumentsRepo } from './query/search-documents';

// ❌ NO nested barrel exports (avoid command/index.ts, query/index.ts)
// ✅ Direct re-exports only for better tree shaking

Layer Separation Compliance

Application/Service Layer Usage

// ✅ Use interface types from Abstraction Layer
import type { 
  ReturnRequestInput,
  ReturnRequestOutput,
  Repository 
} from '@feedos-frgm-system/shared-api-core/document-process-api/command/return-request-lv40';

// ✅ Use Repository implementation from Data Layer
import { ReturnRequestLv40V1Repo } from 
  '@feedos-frgm-system/api-data/document-process-api/command/return-request-lv40';

class DocumentService {
  constructor(
    private readonly returnRequestRepo: Repository // ✅ Interface from Abstraction
  ) {}
  
  async processReturn(input: ReturnRequestInput): Promise<ReturnRequestOutput> {
    return await this.returnRequestRepo.returnRequest(context, input);
  }
}

Presentation Layer (DI Configuration)

// ✅ Import concrete classes for dependency injection setup
import { ReturnRequestLv40V1Repo } from 
  '@feedos-frgm-system/api-data/document-process-api';
import type { Repository } from 
  '@feedos-frgm-system/shared-api-core/document-process-api/command/return-request-lv40';

// DI Container Configuration
const container = new Container();
container.bind<Repository>('ReturnRequestRepo')
  .to(ReturnRequestLv40V1Repo); // ✅ Concrete class for instantiation

Implementation Patterns

1. Flow Function Organization

Problem: Large task.ts Files

จากการวิเคราะห์โค้ดปัจจุบัน พบว่า task.ts มีขนาดใหญ่ (437 lines) เนื่องจากมี Flow functions ที่ทำ business orchestration ซับซ้อน

Solution: Separate Flow Functions

// ❌ Before: All in task.ts (437 lines)
export async function returnRequestTask(input: TaskInput) {
  // Main task logic...
  
  // Flow functions embedded in same file
  async function getAllActivityFlow() { /* 50+ lines */ }
  async function managePackSizeFlow() { /* 80+ lines */ }  
  async function manageAttachmentFlow() { /* 70+ lines */ }
}

// ✅ After: Separated flow functions
// task.ts (reduced to ~150 lines)
export async function returnRequestTask(input: TaskInput) {
  // Import and use flow functions
  const activities = await getAllActivityFlow(flowInput);
  const packSize = await managePackSizeFlow(flowInput);
  const attachments = await manageAttachmentFlow(flowInput);
}

Flow Function Structure

method-folder/
├── task.ts                              # Main coordinator (thin layer)
├── flows/                               # 📁 Business workflow functions
│   ├── {workflowName}.flow.ts           # Individual workflow logic (camelCase)
│   ├── {anotherWorkflow}.flow.ts        # Additional workflow functions
│   └── {thirdWorkflow}.flow.ts          # More workflow functions
├── db.logic.ts                          # Database operations (logic file type)
└── data.logic.ts                        # Data transformations (logic file type)

Note: Flow functions ไม่ต้อง index.ts เพราะเป็น internal implementation ที่ไม่ได้ export ออกไปนอก action folder

Flow Function File Naming

  • Pattern: {workflowName}.flow.ts (camelCase)
  • Examples:
    • getAllActivity.flow.ts - จาก getAllActivityFlow
    • managePackSize.flow.ts - จาก managePackSizeFlow
    • manageAttachment.flow.ts - จาก manageAttachmentFlow

Flow Function Name (ใน Code)

  • Pattern: {workflowName}Flow
  • Examples:
    • getAllActivityFlow - function name ในไฟล์ get-all-activity.flow.ts
    • managePackSizeFlow - function name ในไฟล์ manage-pack-size.flow.ts
    • manageAttachmentFlow - function name ในไฟล์ manage-attachment.flow.ts#### Flow Function Implementation
// flows/getAllActivity.flow.ts
export interface GetAllActivityFlowInput {
  client: PrismaClient;
  telemetryService: TelemetryMiddlewareService;
  context: UnifiedHttpContext;
  applicationId: string;
}

export async function getAllActivityFlow(
  input: GetAllActivityFlowInput
): Promise<Result<ActivityData[], BaseFailure>> {
  const { client, context, applicationId } = input;
  
  // Complex business workflow orchestration
  // 1. Get activities from multiple sources
  // 2. Apply business rules
  // 3. Transform and validate
  // 4. Return structured result
}

Benefits of Flow Separation

  1. Maintainability: แต่ละ flow เป็นอิสระ แก้ไขได้โดยไม่กระทบกัน
  2. Testability: Test แต่ละ flow แยกกันได้
  3. Reusability: Flow functions สามารถใช้ร่วมกันได้
  4. Code Organization: task.ts กลายเป็น thin coordinator
  5. Readability: แต่ละไฟล์มี focus ชัดเจน

2. Repository Pattern

// repository.ts - Action's Data Access Provider
import { Repository } from '@feedos-frgm-system/shared-api-core/{domain}/{type}/{action}';

export class {ActionName}Repo implements Repository {
  constructor(
    private readonly client: PrismaClient,
    private readonly telemetryService: TelemetryMiddlewareService
  ) {}
  
  // Method implementations based on Service Layer interface
  async method1(context, props): Promise<Result<Output, Failure>> {
    // Complex logic: delegate to task
    return await method1Task(input);
  }
  
  async method2(context, props): Promise<Result<Output, Failure>> {
    // Simple logic: direct implementation
    try {
      const raw = await this.client.ENTITY.findUnique(...);
      return Result.ok(transformData(raw));
    } catch (error) {
      return Result.fail(new CommonFailures.GetFail(error.message));
    }
  }
}

2. Task Pattern (For Complex Methods)

// {method-name}/task.ts - Business Logic Orchestration
export interface {MethodName}TaskInput {
  context: UnifiedHttpContext;
  telemetryService: TelemetryMiddlewareService;
  client: PrismaClient;
  props: {MethodName}Input;
}

export async function {methodName}Task(
  input: {MethodName}TaskInput
): Promise<Result<{MethodName}Output, BaseFailure>> {
  const { context, telemetryService, client, props } = input;
  
  // 1. Validation
  // 2. Business logic coordination  
  // 3. Database operations via DAF
  // 4. Data transformations
  // 5. Return result
}

3. Database Access Functions (DAF)

// {method-name}/db.logic.ts - Pure Database Operations
export async function get{Entity}DAF(
  client: PrismaClient, 
  id: string
): Promise<RawEntity | null> {
  return await client.{entity}.findUnique({
    where: { ID: id },
    select: { /* required fields */ }
  });
}

export async function update{Entity}StatusDAF(
  client: PrismaClient,
  data: UpdateData
): Promise<RawEntity> {
  return await client.{entity}.update({
    where: { ID: data.id },
    data: { STATUS: data.status, UPDATED_AT: new Date() }
  });
}

4. Data Logic (Pure Functions)

// {method-name}/data.logic.ts - Pure Data Transformations
export function transform{Entity}ToOutput(raw: RawData): OutputData {
  return {
    id: raw.ID,
    status: raw.APPLICATION_DOC_STATUS,
    currentActorId: raw.CURRENT_ACTOR_ID,
    // ... other transformations
  };
}

export function validate{Entity}Input(input: InputData): ValidationResult {
  const errors: string[] = [];
  
  if (!input.id) errors.push('ID is required');
  if (!input.status) errors.push('Status is required');
  
  return {
    isValid: errors.length === 0,
    errors
  };
}

Current Status Analysis

✅ APIs with Correct Structure

  1. document-process-api - มี command/ และ query/ folders
  2. feed-registration-api - มี command/ และ query/ folders
  3. process-setting-api - มี command/ และ query/ folders

📝 APIs Need Command Folder

  1. example-api - มีแค่ query/
  2. artwork-api - มีแค่ query/
  3. check-animal-feed-registration-api - มีแค่ query/

🔍 APIs ที่ต้องตรวจสอบ

  1. registration-process-api
  2. smartflow-api

Benefits of Action-Based Structure

1. Clear Alignment

  • Abstraction Layer ↔ Data Layer: 1:1 mapping ทำให้ navigate ง่าย
  • Mental Model: Developer ไม่ต้อง switch context เวลาเปลี่ยนชั้น
  • Consistent Naming: ชื่อ folder และ class เหมือนกันทุกชั้น

2. Repository Cohesion

  • Single Responsibility: Repository รับผิดชอบ Action เดียว
  • High Cohesion: Methods ที่เกี่ยวข้องกันอยู่รวมกัน
  • Logical Grouping: Supporting methods อยู่ในที่เดียวกับ main method

3. Maintainability

  • Easy Navigation: รู้ว่าจะหาอะไรที่ไหน
  • Independent Evolution: แต่ละ Action พัฒนาแยกกันได้
  • Clear Testing: Test แยกตาม Action boundary

4. Team Collaboration

  • Domain Ownership: Team สามารถดูแล Action ของตัวเองได้
  • Parallel Development: พัฒนา Action ต่างๆ พร้อมกันได้
  • Code Review Efficiency: Review เฉพาะ Action ที่เกี่ยวข้อง

Import/Export Strategy

Repository-Only Export Principle

Data Layer ควร export เฉพาะ Repository classes เท่านั้น เพื่อ:

  • รักษา Layer Separation ที่ชัดเจน
  • ซ่อน Implementation Details (task functions, DAF functions)
  • ลด Bundle Size และปรับปรุง Tree Shaking
  • หลีกเลี่ยง Type Coupling ระหว่าง layers

Best Practices

2. Repository Design

  • ใช้ Repository เป็น thin layer ที่ implement Abstraction Layer interface
  • Delegate ทุก method ไป task functions เพื่อความสม่ำเสมอ
  • Repository ทำหน้าที่ prepare TaskInput และ call task function เท่านั้น
  • Always use Result pattern สำหรับ error handling

2. Method Organization

  • ทุก method มี task function เพื่อความสม่ำเสมอ
  • สร้าง method folder สำหรับ task, db.logic, data.logic และ flow functions
  • แยก Flow functions ออกจาก task.ts เป็นไฟล์แยกสำหรับ business orchestration
  • ใช้ DAF pattern สำหรับ database operations ใน db.logic.ts
  • แยก data transformations เป็น pure functions ใน data.logic.ts

3. Testing Strategy

// Action-level integration tests
describe('ReturnRequestLv40V1Repo', () => {
  describe('returnRequest', () => {
    it('should return request successfully', async () => {
      // Test main command flow
    });
  });
  
  describe('checkStatus', () => {
    it('should return current status', async () => {
      // Test supporting query
    });
  });
});

// Method-level unit tests  
describe('returnRequestTask', () => {
  it('should orchestrate business logic correctly', async () => {
    // Test task logic
  });
});

4. Import/Export Strategy

// ✅ Action index.ts - Export Repository only
export { ReturnRequestLv40V1Repo } from './repository';
// ❌ NO internal types exported
// ❌ NO task functions exported

// ✅ API index.ts - Direct re-exports for convenience
export { ReturnRequestLv40V1Repo } from './command/return-request-lv40';
export { ApproveDocumentRepo } from './command/approve-document';
export { GetDocumentStatusRepo } from './query/get-document-status';
// ❌ NO nested barrel exports (export * from './command')

// ✅ Usage Examples
// Action-level import (most performant)
import { ReturnRequestLv40V1Repo } from './command/return-request-lv40';
// API-level import (convenient)
import { ReturnRequestLv40V1Repo } from './document-process-api';

Conclusion

การใช้ Action-Based Structure พร้อม Comprehensive Naming Convention ทำให้:

  1. Alignment ที่ดี ระหว่าง Abstraction Layer และ Data Layer
  2. Repository มี Cohesion สูง - methods ที่เกี่ยวข้องอยู่รวมกัน
  3. Navigation ง่าย - 1:1 mapping ระหว่างชั้น พร้อม naming ที่สม่ำเสมอ
  4. Maintainability ดี - แต่ละ Action เป็นอิสระ พร้อม naming ที่ชัดเจน
  5. Team Collaboration ง่าย - ownership ชัดเจน และ convention เดียวกัน
  6. Code Readability สูง - naming convention ทำให้อ่านโค้ดเข้าใจง่าย
  7. Refactoring Safety - naming pattern ช่วยใน automated refactoring

Structure และ Naming Convention นี้เหมาะสมกับ Domain-Driven Design และรองรับการ scale ของทีมและ codebase ได้ดี