Skip to content

11.2 · Cross-service coupling — the "distributed monolith" pattern

ข้อค้นพบเชิง architecture ที่สำคัญที่สุดของ manual ฉบับนี้ — VTRC ไม่ได้เป็น microservices ในความหมายที่ตำรานิยาม แต่เป็น distributed monolith: หลาย service ที่ deploy แยกกัน แต่แชร์ physical database กันตรง ๆ โดยไม่ผ่าน API ของ service เจ้าของข้อมูล

นิยามที่ใช้ในบทนี้ — "cross-service coupling" หมายถึงกรณีที่ service A เปิด connection (TypeORM/Sequelize/raw driver) ไปที่ database ที่ service B เป็นเจ้าของ/เขียนหลัก แทนที่จะเรียก API/GraphQL ของ B


ตารางรวมทุกเส้น coupling ที่พบ

#จาก (caller)ไปที่ (database ที่ไม่ได้เป็นเจ้าของ)วิธีเข้าถึงระดับความเสี่ยงอ้างอิง
1benefit-apivtrc schema (MSSQL)raw SQL string interpolationสูง — SQL injection surface, ไม่มี parameterizationSEC-BENEFIT-02 (Critical), Volume 6 benefit-api/03-persistence
2benefit-apihrmi (MSSQL)raw SQL cross-database queryกลาง — read-mostly แต่ไม่มี access control ที่ layer ตัวเองVolume 6 benefit-api/03-persistence
3workflow-apivtrc (MSSQL, TypeORM connection vtrc)ORM connection ตรงกลาง-สูง — service ที่ไม่ใช่เจ้าของ schema เขียน/อ่านผ่าน entity ของตัวเองCORR-WORKFLOW-01 (High), Volume 6 workflow-api/03-persistence-integrations
4workflow-apihrmi (dbHRMI_Center, read)ORM connection ตรงกลาง — read-only ลด blast radius แต่ยังเป็นการข้าม HR data owner boundaryเดียวกันกับ #3
5workflow-apihrmi_nbc (dbHRMI_Center_NBC, read)ORM connection ตรงกลางเดียวกันกับ #3
6workflow-apibenefit (read) + benefit_writeORM connection ตรง สองทาง (อ่าน + เขียน)สูง — เขียนเข้า database ของ benefit-api ตรง โดย benefit-api ไม่รู้ตัว (audit log เขียนเข้า benefit_write.healthCheckLogs)เดียวกันกับ #3
7pms-apidbWorkforceORM connection ตรง จาก 10 moduleสูง — จำนวน module ที่พึ่งพา DB นอกทำให้ refactor/ย้าย schema ยากมากVolume 6 pms-api/03-persistence
82way-apipms schema (cron NotificationPmsTaskService)cross-schema SQL query pms.dbo.tbnotify[2way].dbo.userlineกลาง — cron job ข้าม platform group (2way → Domain Microservices) โดยไม่ผ่าน API ของ pms-apiVolume 5 2way-api/03-persistence-integrations
9chat-center-api2way database (connection main)TypeORM connection ตรง พร้อม synchronize: trueสูงสุดในบทนี้ — auto-schema-alter ไปกระทบ database ที่ 2way-api เป็นเจ้าของ ถ้า chat-center-api deploy entity ใหม่ TypeORM จะพยายามแก้ schema ของ database ที่ตัวเองไม่ได้เป็นเจ้าของCORR-CHAT-02, Volume 6 chat-center-api/03-persistence-integrations
10chat-center-apidbHRMI_Center (connection center)raw SQL cross-schema ไปหา dbHRMI_Center.dbo.temp_pee_na_tableกลางเดียวกันกับ #9
11sso-apidbHRMI_Center.dbo.temp_pee_na_tableraw SQL cross-schema (ตารางเดียวกันกับที่ chat-center-api อ่าน)กลาง — สอง repo คนละกลุ่มพึ่งพา temp table ตัวเดียวกันที่ชื่อขึ้นต้นด้วย temp_ (มักหมายถึง scratch table ที่ไม่ควรถูก treat เป็น stable interface)Volume 6 sso-api/03-persistence-integrations
12recruitment-api (connection ai) + ai-recruitment-api (raw mssql driver)recruitment_aiสอง repo เขียนคนละ column set ของ jobReqPosition แบบคู่ขนานกับ recruitment.dbo.jobReqPositionสูง — ไม่มี sync mechanism ที่ยืนยันได้ ข้อมูลสองชุดอาจ driftVolume 6 ai-recruitment-api/03-persistence
13centralize-apidbHRMI_Center + dbHRMI_Center_NBCTypeORM connection ตรง (center/nbc)ต่ำ — ตัวนี้คือ "official" bridge ที่ตั้งใจให้เข้าถึง HR data โดยตรง (pure read facade ไม่มี mutation) แต่ยัง caller จาก vtrc-api ที่ระบบใช้จริง (END_POINT_CENTRALIZEcu-central-api) ไม่ใช่ centralize-api — UNCONFIRMED ว่า production เรียก centralize-api เส้นนี้จริงหรือเปล่าVolume 3 centralize-api/02-rest-service-entities, canonical-truths.md debt E-08
14pms-apivtrc-api (env VTRC_SERVER ประกาศไว้)UNVERIFIED — เจอ env var แต่ไม่เจอ caller จริงในโค้ดต่ำ (ถ้าไม่ได้ใช้จริง)Volume 6 pms-api/04-auth-integrations

Pattern ที่ "สุขภาพดี" — เทียบไว้เป็น baseline

ไม่ใช่ทุก integration เป็น distributed monolith — มีบาง service ที่เรียกกันผ่าน API ตามหลัก microservices จริง:

จากไปที่วิธี
meeting-vtrc-apivtrc-apiREST/GraphQL call สำหรับ token verification + push notification (ไม่แตะ MariaDB ตรง)
2way-meeting-backoffice2way-vtrc-apiREST call (ไม่แตะ MSSQL ตรง)
benefit-apivtrc-commonAPI call (ไม่แตะ vtrc_common DB ตรง)
recruitment-api/ai-recruitment-api/benefit-api/pms-api authใช้ shared JWT secret แทน DB call ข้าม service — ยังเป็นความเสี่ยงด้าน security (ดู Volume 12) แต่ไม่ใช่ data coupling

Contrast นี้แสดงว่าทีมงานรู้จัก pattern API-mediated call อยู่แล้ว — cross-service DB coupling ที่พบใน #1-13 ด้านบนน่าจะมาจาก deadline pressure หรือ perf shortcut (เลี่ยง network round-trip) มากกว่าไม่รู้ pattern ที่ถูกต้อง


ทำไม pattern นี้อันตราย

1. ไม่มี migration ใดที่ปลอดภัยฝ่ายเดียว

ถ้า benefit-api ต้องการเปลี่ยน schema ตาราง WdHospitals (benefit DB) ต้องตรวจสอบก่อนว่า workflow-api (ผ่าน benefit_write) อ่าน/เขียน column ไหนอยู่ — ไม่มี contract ที่บังคับ backward compatibility เพราะ workflow-api ไม่ได้ผ่าน API ของ benefit-api

2. Access control กระจุกอยู่ที่ layer เดียวที่ bypass ได้

benefit-api มี Passport JWT strategy คุม endpoint ของตัวเอง แต่ workflow-api ที่เขียนตรงเข้า benefit_write ไม่ผ่าน guard นั้นเลย — ถ้า workflow-api ถูก compromise (และตัวมันเองไม่มี JWT guard ทั่วระบบ — SEC-WORKFLOW-01) ผู้โจมตีเขียนข้อมูลเข้า benefit DB ได้โดยไม่ผ่าน validation ของ benefit-api เลย

3. synchronize: true ข้าม service boundary คือความเสี่ยงเชิงโครงสร้างที่รุนแรงสุด

กรณี chat-center-api2way database (#9) ไม่ใช่แค่ "อ่าน/เขียนข้อมูลของคนอื่น" — TypeORM synchronize: true หมายความว่าทุกครั้งที่ deploy chat-center-api และมี entity เปลี่ยน TypeORM จะรัน ALTER TABLE/CREATE TABLE บน database ที่ 2way-api เป็นเจ้าของ โดย 2way-api ไม่มีทางรู้หรือ approve การเปลี่ยนแปลงนั้นล่วงหน้า

4. Cascade delete/update ไม่มีทางทำงานข้าม engine

pms-apidbWorkforce (#7), workflow-api → 4 connection ข้าม engine (#3-6) — ไม่มี foreign key ข้าม physical database ในทั้ง MSSQL/MariaDB ดังนั้นทุก "delete" ต้อง manual cascade เอง (เหมือนที่ v1 Volume 8.9 บันทึกไว้สำหรับ legacy 3 service — v2 พบว่า pattern นี้ขยายไปทั่วทั้ง 25 repo ไม่ใช่แค่ legacy)


สรุป blast radius ของ pattern นี้

                         ┌─────────────┐
              ┌─────────►│  benefit    │◄────────┐
              │          └─────────────┘          │
     ┌────────┴────┐                      ┌───────┴──────┐
     │ workflow-api │──────┬───────────────►│  vtrc (MSSQL) │
     └────────┬─────┘      │               └──────────────┘
              │             │
              ▼             ▼
       ┌─────────────┐  ┌─────────────┐
       │ dbHRMI_Center│  │dbHRMI_Center│
       │             │  │_NBC          │
       └─────────────┘  └─────────────┘

     ┌─────────────┐        ┌─────────────┐
     │  2way-api   │◄──────►│chat-center-api│ (synchronize:true ⚠)
     └──────┬──────┘        └─────────────┘


     ┌─────────────┐
     │  pms (MSSQL)│
     └─────────────┘

     ┌─────────────┐        ┌─────────────┐
     │ pms-api     │───────►│ dbWorkforce │ (10 modules)
     └─────────────┘        └─────────────┘

     ┌──────────────┐  ┌──────────────┐
     │recruitment-api│◄►│ai-recruitment-│ (jobReqPosition คู่ขนาน)
     └──────────────┘  │      api      │
                        └──────────────┘

workflow-api เป็น service ที่มี blast radius กว้างที่สุด — connection เดียวเปิดไปได้ถึง 4 database ของ 3 owner ต่างกัน (vtrc-api, HRMI, benefit-api) จาก TypeORM config เดียว


ผลกระทบต่อ identity model จาก coupling เหล่านี้ ดู 11.3 Identity across systems