Skip to content

14.2 · Cross-service business flows

บทนี้ trace 6 flow ที่ business logic กระจายอยู่มากกว่า 1 repo จริง — ไม่ใช่แค่ frontend เรียก backend เดียว (นั่นคือ pattern ปกติที่ไม่ถือว่า "cross-service" ในความหมายของบทนี้) แต่เป็นกรณีที่ side-effect ของ transaction เดียวกระจายไปเขียน/อ่านข้าม repo ที่เป็นเจ้าของข้อมูลคนละชุด

14.2.1 · เบิกเงินช่วยเหลือภัยพิบัติ — cancel เขียนกลับเข้า schema vtrc ของ legacy core

Employee ──POST /disaster/create_disaster──► benefit-api (schema `benefit`)

Approver ──POST /approveDisaster/saveApprove──►  benefit-api
                                                     ├─ axios ──► centralize server (identity ไม่ยืนยัน 100%, port 3100)
                                                     └─ paymentService.addPayments()  (schema `benefit`)

เมื่อ cancel เอกสาร docType===1 (WdHospitals) ──►  PaymentsService.cancelWelfare()/cashPayment()
                                                     └─ เปิด transaction ที่สอง บน connection `vtrc`
                                                            └─ raw SQL insert เข้า vtrc.WDHospitals + vtrc.WDHistories
                                                                  (คนละ transaction จาก schema `benefit` — ไม่มี 2-phase commit)

เจ้าของข้อมูลที่แท้จริงกลายเป็นสองที่พร้อมกันbenefit.wdHospitals (schema ใหม่ ที่ benefit-api ดูแล) และ vtrc.WDHospitals (schema เดิม ที่ vtrc-api ดูแล) มีข้อมูลทับซ้อนกัน การ sync ทำผ่าน raw SQL แบบ manual เฉพาะ flow cancel/update bank detail เท่านั้น ไม่มี job หรือ mechanism ที่ reconcile สอง schema แบบ periodic

หลักฐาน — benefit-api/02-core-pipeline §Step 4, benefit-api/06-scorecard CORR-BENEFIT-01

14.2.2 · ค่าธรรมเนียมการศึกษา (StudyLeave) — cancel ยิง HTTP ตรงไป vtrc-api

Employee ──ยื่นเรื่อง──► benefit-api (docType===3, StudyLeave)

เมื่อ cancel ──► PaymentsService ──axios.post──► https://vtrcapi.redcross.or.th/  endpoint cancelStudyEducation


                                              vtrc-api อัปเดตสถานะฝั่งตัวเอง (schema vtrc)

ต่างจาก flow 14.2.1 ที่เขียนข้าม schema ตรง — flow นี้ผ่าน HTTP call จริง (coupling แบบ API ไม่ใช่ database) แต่เป็น HTTP call แบบ fire-without-compensation เดียวกับที่พบใน approveDisaster — ไม่มี timeout/retry/circuit breaker กำหนดเอง ถ้า vtrc-api รับ request แล้ว crash ก่อนตอบกลับ ฝั่ง benefit-api ไม่มีทางรู้ว่าฝั่ง vtrc-api cancel สำเร็จจริงหรือไม่

หลักฐาน — benefit-api/02-core-pipeline §Flow อื่นที่ pattern คล้ายกัน

14.2.3 · สร้างภาพประกาศรับสมัครงานด้วย AI — recruitment-apiai-recruitment-api แชร์ database

เจ้าหน้าที่ (vtrc-backoffice) ──เปิดฟอร์มสร้างประกาศ──► ai-recruitment-api

                                                              ├─ SELECT ... FROM recruitment_ai.dbo.jobReqPosition
                                                              │       (database เดียวกับที่ recruitment-api เขียนผ่าน
                                                              │        connection ชื่อ `ai` — DB_AI_NAME=recruitment_ai)
                                                              ├─ OpenAI GPT-4o (เนื้อหา) + DALL-E/gpt-image-1 (ภาพ)
                                                              ├─ remove.bg (ลบพื้นหลัง)
                                                              └─ puppeteer render → base64 image กลับไปที่ back-office

เจ้าหน้าที่ตัดสินใจใช้ผลลัพธ์ ──► บันทึกกลับเข้า recruitment-api (JobReqAdvertisement)

นี่คือ coupling แบบ shared-database ที่ยืนยันได้ 100% — ไม่มี network call ตรงระหว่าง recruitment-api กับ ai-recruitment-api (ไม่พบ axios/HTTP call ระหว่างสองตัวในการสำรวจ) ทั้งสองอ่าน/เขียนตาราง jobReqPosition ในฐานข้อมูลเดียวกันโดยตรง ต่างฝั่งต่าง ORM (recruitment-api ใช้ TypeORM, ai-recruitment-api ใช้ mssql driver ตรงไม่มี ORM) — ถ้า recruitment-api เปลี่ยน schema ของ jobReqPosition (เช่น rename column) โดยไม่แจ้ง ai-recruitment-api จะเกิด runtime error ที่ทีมสองฝั่งไม่รู้ตัวล่วงหน้า เพราะไม่มี API contract หรือ shared type ระหว่างกัน

หลักฐาน — ai-recruitment-api/index §Database, recruitment-api/index §Database topology

14.2.4 · ออกสัญญาจ้าง — recruitment-api → workforce-api (identity ไม่ยืนยัน) → HRMI

ผู้สมัครผ่านคัดเลือก ──► recruitment-api ContractService

                                ├─ axios.get(`${workforceEndPoint}/fetchEmployeeCandidateRC`)
                                ├─ axios.get(`${workforceEndPoint}/fetchMappingTableRc`)
                                │       (workforceEndPoint = WORKFORCE_SERVER/workforce-api/api/restful
                                │        WORKFORCE_SERVER=https://vtrcapi.redcross.or.th — mount บน domain เดียวกับ vtrc-api
                                │        แต่ไม่ยืนยันว่าเป็นส่วนหนึ่งของ vtrc-api หรือ service แยกที่ share domain)
                                ├─ getWorkforceHRMiMapping() — map รหัสภายในเป็นรหัส HRMI (fallback หลายชั้น)
                                ├─ save ContractHd/ContractEmployment/ContractHistory (schema recruitment)
                                └─ ส่ง email แจ้ง HRMI (ไม่ใช่ API call อัตโนมัติ — ทีม HR ต้องบันทึกเข้า HRMI ด้วยมือ)

จุดอ่อนสำคัญ — การ "export ไป HRMI" ในความเป็นจริงคือการส่งอีเมล ไม่มี API contract ที่ยืนยันว่าข้อมูลถูกบันทึกเข้า HRMI จริง — ถ้าเจ้าหน้าที่ HRMI พลาดไม่ได้บันทึกตามอีเมล ข้อมูลพนักงานใหม่จะขาดหายจาก HRMI โดยที่ recruitment-api มองว่า flow เสร็จสมบูรณ์แล้ว (status เปลี่ยนเป็น hired)

หลักฐาน — recruitment-api/02-core-pipeline §Contract

14.2.5 · การประเมินผลงาน — pms-api sync จาก dbWorkforce แบบ cron ไม่ใช่ real-time

ทุก 1 นาที: pms-api UsagePermissionService.handleCron (@Cron('*/1 * * * *'))

                                ├─ ดึง employee trigger queue จาก connection `workforce` (dbWorkforce)
                                └─ อัปเดตข้อมูลองค์กร/ตำแหน่งใน schema `pms`


EvaluateFormModule/ConfirmEvaluationModule/SalaryAdjustment
        ใช้ข้อมูลองค์กร/ตำแหน่ง/salary range จาก schema `pms`
        (freshness ขึ้นกับ cron ข้างบนทำงานถูกต้องต่อเนื่อง)


SalaryAdjustToHRMIModule ──export Excel──► ทีม HR นำเข้า HRMI ด้วยมือ (เหมือน 14.2.4 — ไม่ใช่ API sync)

ความเสี่ยง silent drift — ถ้า cron UsagePermissionService.handleCron หยุดทำงาน (crash, deploy ผิด, DB connection หลุด) ไม่มี alert/health-check ใดแจ้งเตือน ข้อมูลองค์กรใน pms-api จะ drift จาก dbWorkforce โดยไม่มีใครรู้จนกว่าจะมีคนสังเกตความผิดปกติของผลประเมิน/เงินเดือนที่คำนวณผิด — เป็นความเสี่ยงเดียวกันในเชิงโครงสร้างกับ 14.2.4 (export สุดท้ายอาศัยมนุษย์นำเข้าด้วยมือ)

หลักฐาน — pms-api/02-core-pipeline §workforce sync

14.2.6 · จองประชุม — meeting-vtrc-api เป็นตัวกลางที่ต้องพึ่ง vtrc-api และ cu-central-api พร้อมกัน

พนักงาน ──สร้างการประชุม──► meeting-vtrc-api addNewMeeting
                                    │  (auth bridge: decode JWT local → GraphQL fetchEmployeeProfileForOtherService ไปที่ vtrc-api)

                            insert Meeting+Guest+OtherGuest+Attached (4 bulkCreate แยกกัน ไม่มี transaction ครอบ)

ทุก 15 นาที: cron email.corn.ts ──► ดึง ScheduleJob ACTIVE
                                    ├─ sendMail(): PDF (puppeteer) + GraphQL fetchListEmployeeForTwoWay ไปที่ cu-central-api
                                    │              (แปลง empCode เป็นชื่อผู้เข้าร่วม)
                                    └─ sendNoti(): GraphQL mutation sendNotificationFrom3Party ไปที่ vtrc-api
                                                   (ฝากให้ vtrc-api เป็นคนยิง push notification จริง — meeting-vtrc-api ไม่ยิง FCM เอง)

นี่คือ flow เดียวใน 6 flow ที่พึ่งพา legacy core 2 ทางพร้อมกันในรอบเดียว (vtrc-api สำหรับ auth+notification, cu-central-api สำหรับ employee directory) — ถ้า vtrc-api ล่ม การประชุมจะสร้างได้ (auth bridge อาจ fail แต่ insert หลักไม่ผ่าน vtrc-api) แต่การส่งอีเมล/notification ผ่าน cron จะค้าง เพราะ sendNoti() ต้องพึ่ง vtrc-api ตอบกลับ

หลักฐาน — meeting-vtrc-api/02-core-pipeline

สรุปรูปแบบ coupling ที่พบซ้ำในทั้ง 6 flow

รูปแบบพบใน flowความเสี่ยงร่วม
เขียนข้าม schema แบบ raw SQL ไม่มี 2-phase commit14.2.1data drift ถ้า transaction หนึ่งสำเร็จอีกอันไม่สำเร็จ
HTTP call แบบ fire-without-compensation (ไม่มี retry/timeout เอง)14.2.1, 14.2.2, 14.2.4, 14.2.6caller ไม่รู้ผลจริงถ้า network ล้มกลางทาง
Export สุดท้ายอาศัยมนุษย์ทำต่อ (email → นำเข้าด้วยมือ)14.2.4, 14.2.5human error ทำให้ HRMI ไม่ sync กับระบบต้นทาง โดยระบบต้นทางคิดว่า "เสร็จแล้ว"
Shared database ไม่มี API contract คุ้มกัน14.2.3schema change ฝั่งหนึ่งพังอีกฝั่งแบบไม่มี compile-time check
Cron sync ที่ไม่มี health-check/alert14.2.5silent drift ที่ตรวจจับได้แค่ตอนผลลัพธ์ผิดปกติ

ดู debt ID ที่เกี่ยวข้องทั้งหมดใน 14.3 Scorecard