บทที่ 6 · Part 2 — Choosing Code Architecture

Architecture Decision Heuristics

ประเมิน complexity, change, consistency, integration, risk และทีม เพื่อเลือก architecture แบบมี exit criteria

Architecture Decision Heuristics

หลังจำแนก Subdomain ทีมมักสร้างสูตรว่า Core ใช้ Hexagonal, Supporting ใช้ Layered และ Generic ใช้ CRUD สูตรนี้ดูตัดสินใจง่ายแต่ละเลย module ที่เป็น Generic และมี security risk สูง หรือ Supporting integration ที่เปลี่ยนตาม provider บ่อย Architecture ต้องตอบแรง หลายแกน ไม่ใช่ label เดียว

จบบทนี้คุณจะ

ประเมิน business complexity, change, consistency, integration volatility, risk, ownership, codebase size และ operational independence เลือก starting pattern พร้อม บันทึก trade-off และ signal ที่บังคับให้ทบทวนโดยไม่อ้าง decision matrix เป็นกฎสากล

[!IMPORTANT] วิธีอ่านหลักฐานในบทนี้ คำที่เป็น Definition/Pattern มีแหล่งต้นทางอยู่ใกล้ claim ส่วน decision table, starting structure, checklist และ lab เป็น Heuristic/Trade-off หรือ Course Convention สำหรับฝึกตัดสินใจ ไม่ใช่มาตรฐานสากล เว้นแต่บทจะระบุแหล่งและขอบเขตไว้ชัดเจน

ประเมินแรงผลักดันของการออกแบบ

ใช้คำถาม 8 แกนก่อนพูดชื่อ pattern:

Forceต่ำสูง
Business-rule complexityvalidate แล้วบันทึกstate machine, policy หลายชุด, exception มาก
Rate of changeschema/config เปลี่ยนน้อยexperiment และ policy เปลี่ยนทุก sprint
Consistencyfield เดียว atomicหลาย invariant ต้องเห็น state เดียวกัน
Integration volatilitydependency คงที่vendor/lifecycle/error เปลี่ยนและมี ambiguous result
Security/compliance riskข้อมูลทั่วไปเงิน, identity, tenant isolation, audit
Ownershipทีมเดียวชัดหลายทีมเปลี่ยน contract พร้อมกัน
Codebase sizeflow เดียวอ่านจบหลาย capability และ dependency cycle
Operational independencescale/failure profile เดียวต้อง isolate, scale หรือ release แยก

อย่าให้คะแนนตัวเลขแล้วรวมเป็นผลลัพธ์อัตโนมัติ ตารางมีไว้เปิดบทสนทนา ตัวอย่าง module ที่ rule complexity ต่ำแต่ provider volatility สูงอาจยังใช้ Transaction Script ภายใน พร้อม Ports & Adapters ที่ integration boundary โดยไม่ต้องสร้าง Rich Domain Model

ตัดสินใจมากกว่า 1 ระดับ

หนึ่ง module มีหลาย decision พร้อมกันได้:

  • Domain logic pattern: Transaction Script หรือ Domain Model
  • Application boundary: function โดยตรง, Service Layer หรือ ports/use cases
  • Persistence: SQL ใกล้ use case, Repository/Data Mapper หรือ specialized query
  • Integration: direct library call หรือ adapter/Anti-Corruption Layer
  • Code organization: ไฟล์เดียว, feature slice หรือ roles ภายใน slice
  • Deployment: modular monolith หรือ service แยก

เช่น Notification Preference อาจใช้ Transaction Script, feature slice และ SQL ตรง ขณะที่คุยกับ email provider ผ่าน adapter เพราะ dependency ภายนอก unstable การเลือก adapter ไม่ได้บังคับให้ domain model ซับซ้อน

Heuristic สำหรับเริ่มต้น

ตารางต่อไปเป็น heuristic ของคอร์ส ไม่ใช่ mapping จาก Evans หรือ Fowler:

SituationStarting architectureสิ่งที่ยังต้องพิสูจน์
CRUD/use case เรียบง่าย ทีมเดียวTransaction Script ใน business sliceduplication และ rule leakage ยังต่ำ
Flow ชัดแต่มี transport/persistence หลายแบบlocal Layered Architecturelayer มี responsibility จริง
State/rule/lifecycle ซับซ้อนRich Domain Model + application boundaryAggregate ไม่ใหญ่และ invariant จริง
External provider เปลี่ยนหรือผลลัพธ์กำกวมPorts & Adapters รอบ integrationport เป็นของ consumer ไม่ใช่ vendor clone
หลาย capability ใน repository เดียวslice-first modular structurepublic contract และ import direction
ต้อง scale/isolate/release แยกจริงพิจารณา separate deployabledistributed cost และ ownership พร้อม

Learning Domain-Driven Design เชื่อม business logic patterns กับลักษณะ Subdomain แต่การรวม security, team และ operations เป็น synthesis ที่ architect ต้องทำตามบริบท

บันทึกเหตุผลและเกณฑ์เปลี่ยนแนวทาง

Decision ที่ดีต้องบอกทั้งเหตุผลวันนี้และหลักฐานที่จะทำให้เปลี่ยน:

Module: Notification Preference
Business outcome: customer controls marketing channels
Forces: low rule complexity, medium compliance impact, one team
Decision: Transaction Script inside notification slice
Accepted cost: some SQL/application coupling
Guardrails: security alerts are a separate policy; tenant-scoped queries
Revisit when: three use cases duplicate the same rule, lifecycle branches,
              or another channel needs the same invariant
Owner / date: Customer Platform / 2026-08-08

Exit criteria ป้องกัน 2 ด้าน: ไม่ over-engineer วันนี้ และไม่ปล่อย pattern เดิมต่อไป เพราะ sunk cost สัญญาณควรวัดได้ เช่น change เดียวแตะ 5 package, rule เดียวถูก copy 3 จุด, incident เกิดจาก vendor type รั่ว หรือทีมต้อง release ร่วมกันทุกครั้ง

เปรียบเทียบ 3 Module

การตั้งค่าการแจ้งเตือน

Rules น้อย transaction เดียว integration ไม่สำคัญต่อ decision path เริ่มจาก Transaction Script และ concrete store ได้ แต่แยก security notification จาก marketing เพราะความหมายต่างกัน

การอนุมัติ Refund

มี refundable amount, previous refunds, state transition, maker-checker, concurrency และ provider ambiguity เหมาะกับ Rich Domain Model สำหรับ invariant ร่วมกับ application port และ datastore concurrency controls

Adapter สำหรับ Payment Provider

Business rule ภายในอาจน้อย แต่ error/lifecycle/API version ผันผวน จึงลงทุนที่ translation boundary, contract tests, idempotency และ observability มากกว่า Entity hierarchy

Risk ไม่ได้สั่งให้ใช้ pattern ใด pattern หนึ่ง

เมื่อเงินหรือสิทธิ์ได้รับผลกระทบ สิ่งที่ต้องมีคือ authoritative decision path, concurrency control, audit, deny-by-default และ owner ของ policy Rich Domain Model อาจช่วยแสดง invariant แต่ไม่ทดแทน database/security controls

แบบฝึกปฏิบัติ: Architecture Decision Matrix

ให้เลือก 4 module: notification, refund, provideradapter, portalquery แล้วกรอกคะแนน Low/Medium/High/Unknown พร้อม evidence ต่อทั้ง 8 force จากนั้นเขียน:

  1. starting domain logic pattern
  2. integration/persistence seams ที่จำเป็น
  3. structure วันนี้
  4. สิ่งที่ตั้งใจไม่ทำ
  5. three reevaluation signals
  6. owner และ review date

คะแนนแบบฝึกหัด:

  • ผ่าน เมื่อ decision อ้าง force มากกว่า Subdomain type
  • ดี เมื่อ module เดียวเลือก pattern ต่างระดับกันและมี exit criteria วัดได้
  • ควรทำใหม่ เมื่อ matrix รวมคะแนนเป็นสูตรหรือเลือก architecture จากความนิยม

รายการตรวจสอบ

  • ประเมิน complexity, change, consistency, integration และ risk แยกกัน
  • Subdomain type เป็น input ไม่ใช่ selector เดียว
  • Domain logic, application, structure และ deployment เป็นคนละ decision
  • Decision ระบุ accepted cost และสิ่งที่ไม่ทำ
  • Exit criteria สังเกตได้จาก code/change/incident
  • Policy ที่กระทบเงินหรือสิทธิ์มี owner และวันที่

สรุปบทนี้

Architecture ที่พอดีเริ่มจาก forces แล้วเลือก pattern ทีละระดับ Heuristic ช่วยตั้งคำถาม แต่ไม่ควบคุมคำตอบ เอกสาร decision พร้อม exit criteria ทำให้เริ่มเรียบง่ายได้โดยไม่หลง ติดอยู่กับโครงสร้างเดิมเมื่อ domain เปลี่ยน

อ่านเพิ่มเติม