บทที่ 21 · Part 4 — Model Behavior in Code
Factories, Repositories and Modules
ใช้ creation, persistence และ module boundaries เพื่อปกป้อง model โดยไม่สร้าง abstraction ครบชุดทุกจุด
Factories, Repositories and Modules
Tactical DDD มักถูกสอนเป็นรายการให้สร้าง Factory, Repository และ Module ให้ครบ แต่แต่ละ pattern มีแรงเฉพาะ Factory ปกป้องการสร้างที่ซับซ้อน Repository ให้ illusion ของ collection สำหรับ Aggregate และ Module จัด model ตาม concept หาก constructor/SQL/package เดิมชัดอยู่แล้ว abstraction เพิ่มอาจไม่คุ้ม
จบบทนี้คุณจะ
ใช้ Factory เมื่อ creation มี invariant/meaningful process ออกแบบ Repository ตาม Aggregate จาก consumer side แยก write model จาก read query และจัด Module ด้วย Ubiquitous Language พร้อมรู้ เมื่อใดไม่ควรใช้ pattern
Factory
Factory มีค่าหากการสร้าง valid Aggregate:
- ต้องคำนวณ/เลือก subtype
- มีหลาย objects ที่ต้องสัมพันธ์ถูก
- ต้อง snapshot policy/terms
- constructor ยาวจนซ่อน business meaning
func OpenTransferIntent(
id TransferIntentID,
beneficiary Beneficiary,
amount Money,
terms TermsVersion,
) (*TransferIntent, error) {
// establish invariants or fail
}
ชื่อ Factory operation ใช้ language เช่น Open, Issue, Schedule ไม่ใช่ CreateObjectFactory
ถ้า Value Object validate 2 บรรทัด constructor ธรรมดาพอ
Factory ไม่ควร call provider/database เอง Application Service เตรียม facts แล้วส่งเข้า
Repository
Repository ให้ interface คล้าย collection ของ Aggregate และซ่อน mapping:
type TransferIntents interface {
ByID(ctx context.Context, id TransferIntentID) (*TransferIntent, error)
Save(ctx context.Context, intent *TransferIntent) error
}
consumer/model/application side เป็นเจ้าของ abstraction ไม่ให้ MySQL method names กำหนด contract Save ต้องระบุ optimistic version/transaction expectations และ adapter มี integration tests จริง
Repository ไม่ใช่ Generic CRUD
หลีกเลี่ยง Repository[T] ที่มี FindAll, UpdateField, Delete เหมือนทุก model เพราะ Aggregate
behavior/identity/query ต่างกัน Repository ต่อ Aggregate root ไม่ต่อ table
Read model สำหรับ Portal อาจใช้:
type TransferHistoryQueries interface {
ListForCustomer(ctx context.Context, customerID string, page Page) ([]TransferRow, error)
}
ไม่ต้อง hydrate Aggregates เพื่อ display
Unit of Work และ Transaction
Repository หลายตัวใน local transaction อาจประสานด้วย transaction/application boundary แต่ business process ข้าม provider/contexts ไม่ใช่ database Unit of Work ใช้ outbox/reconciliation ในบทต่อไป
Repository ไม่ทำให้ Data Authority ชัดเอง
ต้องมี context ownership, database grants/query scope และ prohibition ต่อ cross-context writes Portal repository ที่เขียน Ledger table ยังละเมิด boundary แม้ interface สวย
Module
Module จัด concepts ให้ cohesive และ language บอกเรื่อง:
transferintent/
eligibility/
fundsreservation/
paymentexecution/
reconciliation/
ดีกว่า models/, services/, helpers/ เมื่อ outer structure ต้องสื่อ capability แต่ไม่ใช่กฎว่า
ทุก context ต้อง package เหมือนกัน Module boundary ดูจาก public API/import/change locality
ไม่ใช่ folder name เพียงอย่างเดียว
Choosing Table
| Force | Pattern | Omit when |
|---|---|---|
| valid creation ซับซ้อน | Factory | constructor ชัด |
| Aggregate/persistence decoupling | Repository | simple CRUD/query และ model ไม่ได้ต้องปกป้อง |
| atomic local changes | Unit of Work | transaction เดียวตรงไปตรงมา |
| conceptual cohesion | Module | splitting ทำให้ navigation แย่กว่าเดิม |
แบบฝึกปฏิบัติ: Transfer Persistence Boundary
ส่ง:
- Factory ของ Transfer Intent และ invariants ที่ establish
- Repository interface พร้อม concurrency semantics
- separate history query interface
- module tree ด้วย domain names
- SQL adapter test cases: not found, version conflict, transaction rollback
- patterns deliberately omitted พร้อมเหตุผล
รายการตรวจสอบ
- Factory สร้าง valid object หรือ fail
- Creation ไม่มี hidden I/O
- Repository ตาม Aggregate และ consumer-owned
- Save มี transaction/concurrency contract
- Read query ไม่ hydrate Aggregate โดยไม่จำเป็น
- Module ใช้ Ubiquitous Language
- Pattern ที่ไม่คุ้มถูกละไว้
สรุปบทนี้
Factory, Repository และ Module ช่วย creation, persistence illusion และ conceptual cohesion แต่ไม่ใช่เครื่องหมายว่าทำ DDD ครบ เลือกเมื่อปกป้อง model/meaning ได้จริงและคง datastore/query capabilities ที่สำคัญไว้
อ่านเพิ่มเติม
- DDD Reference — Factory, Repository และ Module
- Repository — Martin Fowler
- Data Mapper และ Unit of Work