Tier Flow Mapping — Non-Technical ↔ Technical
ไฟล์นี้ช่วย bridge ระหว่างภาษาธุรกิจ (overview) กับภาษา engineering (fullsystem)
อ่านคู่กับlegacy-tier-flow-overview.mdและlegacy-fullsystem-tier-flows.md
ภาพรวม: แต่ละสถานการณ์ตรงกับ Flow ไหน?
| สถานการณ์ (ภาษาคน) | Flow # | ชื่อ Flow (Technical) | Trigger |
|---|---|---|---|
| ลูกค้าซื้อของแล้ว Tier ขึ้น | Flow 1 | Sales Transaction → Tier Upgrade | Kafka event จาก POS |
| ลูกค้าคืนสินค้าแล้ว Tier ลง | Flow 2 | Refund / Void → Tier Downgrade | Kafka event จาก POS |
| ระบบคำนวณยอดสะสมใหม่แบบ batch | Flow 3 | Batch Accum Recalculate | Temporal cron (scheduled) |
| ครบรอบ 2 ปี — คง Tier หรือลด Tier | Flow 4 | Annual Maintain Tier | Temporal cron (daily check) |
| ตรวจสอบ Tier หลังครบรอบ | Flow 5 | Reconcile Member Tier | ต่อจาก Flow 4 อัตโนมัติ |
| Admin ปรับยอดด้วยมือ | Flow 6 | Manual Adjustment (Admin) | REST API |
| ได้/ยกเลิกบัตร Co-Brand | Flow 7 | Co-Brand Card → Tier Change | File import / Admin / Kafka |
| พนักงานออกบริษัท | Flow 8 | Staff Exit Import | Admin CSV upload |
| Import สมาชิกใหม่เข้าระบบ | Flow 9 | Member Import | Admin CSV upload |
รายละเอียดแต่ละ Flow
Flow 1 — ลูกค้าซื้อของแล้ว Tier ขึ้น
ภาษาคน:
ทุกครั้งที่ลูกค้าซื้อของ ระบบจะรับข้อมูลทันที คำนวณยอดสะสม 24 เดือนใหม่ ถ้าถึง threshold ของ Tier สูงกว่า → ขึ้น Tier ทันที
Technical:
- Trigger: Kafka event
SalesTransactionCompletedDomainEventจาก External Sales System - Entry point:
SalesTransactionController→SalesTransactionService.createSalesTransaction() - Core logic:
MemberEntity.addNewSalesTransaction()→calculateAccumulateSpending()→calculateTierToUpgrade() - Mutation:
updateMemberTier()→ บันทึก DB + emitMemberTierUpgradedDomainEvent - Downstream: Engagement Service, Notification Service รับ event ต่อ
Flow 2 — ลูกค้าคืนสินค้าแล้ว Tier ลง
ภาษาคน:
เมื่อลูกค้าคืนสินค้าหรือยกเลิกรายการ ระบบหักยอดสะสมออก ถ้ายอดต่ำกว่า threshold ของ Tier ปัจจุบัน → ลด Tier ทันที (เฉพาะกรณีที่รายการนั้นเกิดขึ้นหลังจากวันที่ได้ Tier ปัจจุบัน)
Technical:
- Trigger: Kafka event
SalesTransactionRefundedDomainEventหรือSalesTransactionVoidedDomainEvent - Entry point:
SalesTransactionController→RefundSalesTransactionService.createRefundSalesTransaction() - Core logic:
MemberEntity.addNewRefundSalesTransaction()→calculateAccumulateSpending()→calculateTierAdjustment() - เงื่อนไขพิเศษ: ตรวจว่า
salesTxn.completedAtอยู่หลังtierStartedAtก่อนถึงจะ downgrade - Mutation:
updateMemberTier()→ emitMemberTierDowngradedDueToRefundDomainEvent
Flow 3 — ระบบคำนวณยอดสะสมใหม่แบบ batch
ภาษาคน:
ระบบมี job ที่รันตามกำหนดเวลา เพื่อคำนวณยอดสะสมของสมาชิกใหม่ทั้งหมด ป้องกันกรณีที่ยอดอาจคลาดเคลื่อนจาก real-time
Technical:
- Trigger: Temporal Cron Schedule หรือ Admin API
- มี 2 sub-path:
- 3A — อัปเดตเฉพาะ
accumulateSpending(rolling window boundary เปลี่ยน) ไม่เปลี่ยน Tier - 3B — คำนวณใหม่ทั้งหมดจาก DB aggregation (8 parallel queries) + อาจเปลี่ยน Tier
- 3A — อัปเดตเฉพาะ
- Entry point:
updateAccumulateSpendingWorkflow/recalculateSpendingTierWorkflow(Temporal) - Core logic (3B):
MemberEntity.processSpendingAndTierUpdate()— path แยกจากupdateMemberTier() - Scale: ประมวลผลเป็น chunk 500 → 100 members ต่อรอบ
Flow 4 — ครบรอบ 2 ปี: คง Tier หรือลด Tier
ภาษาคน:
ทุก Tier มีอายุ 2 ปี เมื่อครบกำหนด ระบบจะดูว่าลูกค้าซื้อของในรอบนี้มากพอไหม ถ้าพอ → คง Tier เดิม ถ้าไม่พอ → ลด Tier ลง แล้วเริ่มรอบใหม่ 2 ปี
Technical:
- Trigger: Temporal Schedule ตรวจ
tierEndedAt < nowทุกวัน - Entry point:
maintainTierWorkflow→MaintainTierService.processCycleEndedMembers() - Core logic:
MemberEntity.maintainTier()→calculateNewMinimumTier()→calculateNewTierFromAccumulateMaintainSpending() - ใช้
accumulateMaintainSpending(ยอดในรอบ) เทียบกับtier.maintainSpending - Tier ใหม่เริ่ม:
tierStartedAt = Jan 1,tierEndedAt = Dec 31 ปีถัดไป - Scale: chunk 10,000 → 100 members ต่อรอบ
- หลังเสร็จ: trigger Flow 5 อัตโนมัติ
Flow 5 — ตรวจสอบ Tier หลังครบรอบ (Reconcile)
ภาษาคน:
หลังจากกระบวนการต่ออายุ Tier เสร็จ ระบบจะตรวจซ้ำอีกรอบ เพื่อจับกรณีที่ลูกค้าควรได้ Tier สูงกว่าแต่ยังไม่ได้รับ แล้วปรับให้ถูกต้อง
Technical:
- Trigger: ต่อจาก
maintainTierWorkflowอัตโนมัติ - Entry point:
reconcileMemberTierWorkflow→ReconcileMemberTierService.processReconciliationForMembers() - Core logic:
MemberEntity.adjustTier()→calculateTierAdjustment()→calculateNewTierStartDateFromSalesTransactionPeriod() - ข้อสำคัญ: Flow นี้ ขึ้น Tier เท่านั้น ไม่ลง Tier
- ถ้า Tier ปัจจุบันเป็น INVITATION type → ข้ามไป ไม่ปรับ
Flow 6 — Admin ปรับยอดด้วยมือ
ภาษาคน:
Admin สามารถเพิ่มหรือหักยอดสะสมให้ลูกค้าได้โดยตรงผ่านหน้า Backoffice ซึ่งอาจทำให้ Tier เปลี่ยนได้เช่นกัน
Technical:
- Trigger: REST API
POST /admin/members/:id/adjust-transaction - Entry point:
AdjustSalesTransactionService.createWithTransaction() - ไม่มี logic ของตัวเอง — delegate ต่อ:
- ADD → เรียก
SalesTransactionService(เหมือน Flow 1 ทุกอย่าง) - DEDUCT → เรียก
RefundSalesTransactionService(เหมือน Flow 2 ทุกอย่าง)
- ADD → เรียก
- Audit: emit
MemberUpdatedLogDomainEventเพิ่มเติม
Flow 7 — ได้/ยกเลิกบัตร Co-Brand
ภาษาคน:
บัตรเครดิต Co-Brand บางใบมีสิทธิ์กำหนด “Tier ขั้นต่ำ” ให้ลูกค้า ถ้าได้บัตรที่ให้ Tier สูงกว่าปัจจุบัน → ขึ้น Tier ทันที ถ้ายกเลิกบัตร → คำนวณ Tier ใหม่จากยอดสะสม อาจลงได้
Technical:
- มี 3 sub-path:
- 7A — Import file จากธนาคาร (CSV):
MemberCoBrandCardImportService→MemberEntity.updateMemberLinkCoBrand() - 7B — Admin cancel/inactivate บัตรเดี่ยว:
MemberCoBrandCardService→MemberEntity.coBrandCardCanceled()/coBrandCardInactivated() - 7C — Kafka event
CoBrandInactivatedDomainEvent(ระงับทั้ง brand): loop ทุก member ที่ถือบัตรนั้น
- 7A — Import file จากธนาคาร (CSV):
- Core logic:
calculateNewMinimumTier()→calculateNewTier()→updateMemberTier() - Events:
MemberTierUpgradedCoBrandDomainEvent,MemberTierDowngradedCoBrandCanceledDomainEvent,MemberTierDowngradedCoBrandInactiveDomainEvent
Flow 8 — พนักงานออกบริษัท
ภาษาคน:
พนักงานที่ได้ Tier พิเศษ (CRYSTAL) จากสถานะพนักงาน เมื่อออกจากบริษัทจะถูกปรับ Tier ใหม่ตามยอดสะสมปกติ
Technical:
- Trigger: Admin upload CSV →
POST /admin/staff-exit/import - Entry point:
ImportStaffExitService→ TemporalimportStaffExitWorkflow - Core logic:
MemberEntity.processStaffExit()→ ลบstaffProfile→calculateNewTier()→updateMemberTier() - Validation: ตรวจว่า Tier ปัจจุบันต้องเป็น CRYSTAL ก่อน และ cross-check ชื่อ/staffId
- Event:
MemberTierAssignedDueToLeftTheCompanyDomainEvent
Flow 9 — Import สมาชิกใหม่เข้าระบบ
ภาษาคน:
Admin สามารถ import รายชื่อสมาชิกพร้อม Tier ที่กำหนดมาให้ผ่านไฟล์ CSV ระบบจะสร้างสมาชิกใหม่หรืออัปเดตสมาชิกที่มีอยู่แล้ว
Technical:
- Trigger: Admin upload CSV →
POST /admin/members/import - Entry point:
ImportMemberService→ TemporalimportMemberWorkflow - มี 2 sub-path:
- สมาชิกใหม่:
MemberEntity.create()— ยอดสะสมเริ่มต้น = 0, Tier ตาม CSV - สมาชิกเดิม:
MemberEntity.importUpdate()— ป้องกัน downgrade (INVITATION→NORMAL ถูก block)
- สมาชิกใหม่:
- ข้อควรระวัง:
yearsToMaintainhardcoded เป็น 2 ใน import path (ไม่อ่านจาก config) - Events:
MemberTierAssignedDomainEvent,MemberTierUpdatedDomainEvent
Core Functions ที่ทุก Flow ใช้ร่วมกัน
สำหรับคนที่อยากเข้าใจว่า “ถ้าแก้ตรงนี้ จะกระทบอะไรบ้าง”
ชื่อ Function ใช้ใน Flow
─────────────────────────────────────────────────────────────
calculateAccumulateSpending() 1, 2, 5, 6, 7, 8
calculateAccumulateMaintainSpending() 1, 2 (และทุก flow ที่เรียก updateMemberTier)
calculateTierToUpgrade() 1, 6(ADD)
calculateTierAdjustment() 2, 5, 6(DEDUCT)
calculateNewTier() 7, 8
calculateNewTierFromAccumulateMaintainSpending() 4
calculateNewMinimumTier() 4, 7, 9(สมาชิกเดิม)
updateMemberTier() ← CRITICAL 1, 2, 4, 5, 6, 7, 8, 9(สมาชิกเดิม)
processSpendingAndTierUpdate() 3B (path แยก ไม่ผ่าน updateMemberTier)
create() 9(สมาชิกใหม่)
กฎง่ายๆ:
- แก้
calculateAccumulateSpending()→ กระทบ Flow 1, 2, 5, 6, 7, 8 - แก้
updateMemberTier()→ กระทบเกือบทุก Flow ยกเว้น 3B และ 9(ใหม่) - Flow 3B และ Flow 9(ใหม่) ใช้ path แยก ต้องระวังเป็นพิเศษ
Domain Events ที่ระบบ Downstream รับต่อ
| Event | เกิดจาก Flow | ความหมาย |
|---|---|---|
MemberTierAssignedDomainEvent |
9 | สมัครสมาชิกใหม่ / import |
MemberTierUpgradedDomainEvent |
1, 5 | Tier ขึ้นจากยอดซื้อ |
MemberTierDowngradedDueToRefundDomainEvent |
2 | Tier ลงจากการคืนสินค้า |
MemberTierMaintainedAfterReviewedDomainEvent |
4 | คง Tier เดิมหลังครบรอบ |
MemberTierDowngradedAfterReviewedDomainEvent |
4 | Tier ลงหลังครบรอบ |
MemberTierUpgradedCoBrandDomainEvent |
7A | Tier ขึ้นจากบัตร Co-Brand |
MemberTierDowngradedCoBrandCanceledDomainEvent |
7B | Tier ลงจากยกเลิกบัตร |
MemberTierDowngradedCoBrandInactiveDomainEvent |
7B, 7C | Tier ลงจากระงับบัตร |
MemberTierAssignedDueToLeftTheCompanyDomainEvent |
8 | พนักงานออกบริษัท |
MemberTierUpdatedDomainEvent |
ทุก Flow ที่มี tier change | Generic event ทุกครั้งที่ Tier เปลี่ยน |
Downstream ที่รับ event: Engagement Service, Notification Service