บทที่ 20 · Part 4 — Model Behavior in Code
Services, Policies and Specifications
วาง business decision ที่ไม่เข้ากับ Entity ใด พร้อมแยก Domain Service จาก application orchestration
Services, Policies and Specifications
เมื่อทีมไม่รู้ว่าวาง rule ที่ไหน มักสร้าง TransferService ซึ่ง validate, load database, call provider,
คำนวณ fee, ตรวจ risk และส่ง notification จนชื่อ Service ไม่บอก model DDD แยก Domain Service,
Application Service, Policy และ Specification ตามชนิดของความรู้ ไม่ใช่ suffix เดียว
จบบทนี้คุณจะ
แยก domain operation ที่ไม่เข้ากับ Entity/Value Object จาก application orchestration model policy ที่เปลี่ยนตาม owner/version และใช้ Specification เมื่อ predicate มีชื่อ/reuse/composition จริง โดยไม่สร้าง service class ครอบทุก logic
Domain Service
Domain Service เป็น stateless business operation ที่เป็นส่วนของ Ubiquitous Language แต่ไม่เป็น responsibility ธรรมชาติของ Entity/Value เดียว เช่นการเลือก route จาก candidate providers ภายใต้ cost/reliability policy
type RouteSelector struct{}
func (RouteSelector) Select(policy RoutingPolicy, candidates []ProviderQuote) (Route, error) {
// pure domain decision; no HTTP/database
}
ถ้า operation เป็นเพียง CRUD orchestration หรือ logging ไม่ใช่ Domain Service
Application Service
Application Service ประสาน use case:
authorize principal
load aggregates/facts
invoke domain decision
save transactionally
call/publish through ports after correct boundary
translate result to caller
มันมี error/transaction/idempotency responsibility แต่ไม่ควรถือ business rule ที่ Domain Expert ต้องพูดถึง Microsoft แยก Domain/Application Services ใน Azure Tactical DDD
Policy เป็น First-class Model
Policy encapsulates decision ที่อาจเปลี่ยนตาม strategy, product, jurisdiction หรือเวลา:
type EligibilityPolicy struct {
version string
effectiveFrom time.Time
jurisdiction string
}
Behavior อาจอยู่ใน policy object หรือใช้ policy facts กับ service สำคัญคือ provenance/owner
Engineer ไม่เป็น Policy Authority โดยค่าเริ่มต้น
transfer limits, KYC, retention และ approval thresholds ต้องมาจาก accountable owner/regulator พร้อม source/version/jurisdiction/effective date Code ต้อง trace version ไม่สร้างเลขเอง
Specification
Specification ตั้งชื่อ business predicate ที่ reuse/compose และอธิบายได้ เช่น
TransferRequiresManualReview หรือ BeneficiaryIsEligible
ใช้เมื่อ:
- predicate เป็น Ubiquitous Language
- หลาย behavior ต้องใช้เหมือนกัน
- composition AND/OR/NOT เพิ่มความชัด
- rule ต้อง test independently
ไม่ใช้ห่อทุก boolean เช่น AmountIsPositiveSpecification ถ้า Money constructor พอ
Domain Predicate กับ Database Query
Specification ใน model กับ query filter ไม่จำเป็นต้อง object เดียว Domain predicate อาจใช้ rich values ขณะที่ database query optimize index ถ้าฝืนให้ Specification สร้าง SQL persistence concern อาจรั่วเข้ามา
แยก:
Domain: TransferRequiresReview(policy, transfer)
Query: ReviewQueue.FindPending(criteria)
และมี tests เพื่อให้ semantics สอดคล้องเท่าที่ contract ต้องการ
วาง Rule ด้วย Responsibility Test
ถาม:
- Rule ปกป้อง invariant ของ Entity/Aggregate เดียวหรือไม่ → method
- เป็น calculation/decision ข้าม Values โดยไม่มี natural owner หรือไม่ → Domain Service
- เปลี่ยนตาม named business policy/version หรือไม่ → Policy
- เป็น predicate ที่ reuse/compose ใน language หรือไม่ → Specification
- เป็นการประสาน I/O/transaction/actor หรือไม่ → Application Service
แบบฝึกปฏิบัติ: Eligibility Decision
ออกแบบ:
Application Service: Assess Transfer Eligibility
Domain facts:
Policy object + provenance:
Domain Service, if justified:
Specifications:
Aggregate behavior:
External facts and ACL:
Decision event:
ทดสอบ policy version เปลี่ยน, decision expiry, missing evidence, different jurisdiction และ replay ว่า result เดิมอธิบายได้ด้วย policy version ใด
รายการตรวจสอบ
- Domain Service เป็น business operation แบบ stateless
- Application Service orchestration ไม่มี hidden domain rules
- Policy มี owner/source/version/time/context
- Specification มี language/reuse/composition value
- Database query ไม่ถูกบังคับใช้ domain object เดียว
- Rule อยู่ใกล้ concept ที่รับผิดชอบที่สุด
- Generic
SomethingServiceถูกท้าทาย
สรุปบทนี้
Service, Policy และ Specification แยกชนิดความรู้ที่มักปนใน service class ใหญ่ Domain Service ถือ operation ที่เป็นภาษา Application Service ประสาน use case Policy ทำ decision provenance ชัด และ Specification ตั้งชื่อ predicate ที่มีคุณค่าจริง
อ่านเพิ่มเติม
- DDD Reference — Service และ Specification
- Azure Tactical DDD — Domain/Application Service
- Specification — Eric Evans และ Martin Fowler