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- จาก commandreturn-request-lv40+ version V1 - ✅
ApproveDocumentRepo- จาก commandapprove-document - ✅
GetDocumentStatusRepo- จาก queryget-document-status - ✅
ListDocumentsRepo- จาก querylist-documents - ✅
ValidateDocumentLv30Repo- จาก commandvalidate-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.tssuffix สำหรับ logic files เพื่อระบุประเภท - ใช้
.flow.tssuffix สำหรับ flow files - ใช้
.test.tssuffix สำหรับ 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 รูปแบบเพื่อความยืดหยุ่น:
Level 1: Action-Level Import (Recommended)
// ✅ 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
- Maintainability: แต่ละ flow เป็นอิสระ แก้ไขได้โดยไม่กระทบกัน
- Testability: Test แต่ละ flow แยกกันได้
- Reusability: Flow functions สามารถใช้ร่วมกันได้
- Code Organization: task.ts กลายเป็น thin coordinator
- 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
- document-process-api - มี command/ และ query/ folders
- feed-registration-api - มี command/ และ query/ folders
- process-setting-api - มี command/ และ query/ folders
📝 APIs Need Command Folder
- example-api - มีแค่ query/
- artwork-api - มีแค่ query/
- check-animal-feed-registration-api - มีแค่ query/
🔍 APIs ที่ต้องตรวจสอบ
- registration-process-api
- 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 ทำให้:
- Alignment ที่ดี ระหว่าง Abstraction Layer และ Data Layer
- Repository มี Cohesion สูง - methods ที่เกี่ยวข้องอยู่รวมกัน
- Navigation ง่าย - 1:1 mapping ระหว่างชั้น พร้อม naming ที่สม่ำเสมอ
- Maintainability ดี - แต่ละ Action เป็นอิสระ พร้อม naming ที่ชัดเจน
- Team Collaboration ง่าย - ownership ชัดเจน และ convention เดียวกัน
- Code Readability สูง - naming convention ทำให้อ่านโค้ดเข้าใจง่าย
- Refactoring Safety - naming pattern ช่วยใน automated refactoring
Structure และ Naming Convention นี้เหมาะสมกับ Domain-Driven Design และรองรับการ scale ของทีมและ codebase ได้ดี