D-Pro — Luồng nghiệp vụ toàn hệ thống 26/08/2026
Bản đồ quyết định

Luồng nghiệp vụ
toàn hệ thống D-Pro

Bản đồ chức năng và các luồng nghiệp vụ đầu–cuối, dựng từ mã nguồn đang chạy chứ không phải từ thiết kế trên giấy. Mục đích: nhìn được chỗ nào nối vào chỗ nào để ra quyết định kiến trúc — nhất là ba quyết định đang mở ở cuối tài liệu.

Đo ngày: 26/08/2026
Bounded context: 36
Kafka topic: 27
Màn hình: 44 / 10 cụm

Cách đọc tài liệu này

Mọi con số đến từ hệ thống đang chạy: bounded context là hợp của src/Core/*src/Application/*, topic lấy từ mã nguồn, số màn hình lấy từ navigation.ts. Sơ đồ vẽ quan hệ thật, kể cả những quan hệ đáng lẽ không nên có — chúng được tô riêng, vì đó chính là thứ cần quyết định.

01 — Bản đồ

Bản đồ chức năng — 41 bounded context

Nhóm theo vai trò trong nhà máy, không nhóm theo tầng kỹ thuật. Màu nền phân biệt bốn khối: điều hành sản xuất (MES), thương mại, kế toán, và nền tảng.

Sơ đồ 1 — Bản đồ chức năng toàn hệ thống
flowchart TB subgraph MES["ĐIỀU HÀNH SẢN XUẤT — MES"] direction LR PROD["Production
lệnh SX · công đoạn · chấm công"] PLAN["Planning
MRP · CRP · xếp lịch"] BOM["BOM
định mức đa cấp"] ROUTE["Routing
quy trình công nghệ"] QUAL["Quality
kiểm · NCR · SPC"] MAINT["Maintenance
bảo trì"] TOOL["Tooling
khuôn · đồ gá"] SUB["Subcontracting
gia công ngoài"] SHIFT["Shift"] CAPA["CAPA"] TRACE["Traceability
truy xuất lô"] ECO["ECO
thay đổi kỹ thuật"] CALIB["Calibration"] SENSOR["Sensors
OEE từ máy"] end subgraph COM["THƯƠNG MẠI"] direction LR PROC["Procurement
đơn mua"] SALES["Sales
đơn bán"] PRICE["Pricing
bảng giá · hạn mức"] RET["Returns
trả hàng 2 chiều"] INV["Inventory
tồn kho · chuyển kho"] WH["Warehouse
vị trí kệ · kiểm kê"] end subgraph ACC["KẾ TOÁN"] direction LR LEDGER["Ledger
sổ cái · kỳ · bút toán"] AR["AR
phải thu"] AP["AP
phải trả · 3 chiều"] FA["FixedAssets
TSCĐ · khấu hao"] PAY["Payroll
tiền lương"] FIN["Finance
giá thành"] EINV["EInvoice
hoá đơn điện tử"] end subgraph PLAT["NỀN TẢNG"] direction LR AUTH["Auth
vai trò · phân hệ · nhà máy"] MD["MasterData
SP · máy · trạm · NV"] SITES["Sites
nhà máy"] APPR["Approval
ma trận · định tuyến"] NOTIF["Notification"] DOC["Documents"] REP["Reporting"] ANA["Analytics"] IMPORT["CatalogImport"] end MES --> ACC COM --> ACC PLAT -.-> MES PLAT -.-> COM PLAT -.-> ACC style MES fill:#0F131A,stroke:#10B981 style COM fill:#0F131A,stroke:#06B6D4 style ACC fill:#0F131A,stroke:#F59E0B style PLAT fill:#0F131A,stroke:#E1AC36

Quy tắc chọn Event Sourcing

  • 30 BC dùng ES — thực thể có vòng đời nghiệp vụ, và lý do đổi trạng thái cũng là dữ liệu cần giữ
  • 6 là state store thuần: Auth · Documents · MasterData · Pricing · Sites · Warehouse
  • Chọn sai theo hướng ES cho một bảng danh mục thì mỗi lần sửa tên sản phẩm lại đẻ một sự kiện vô nghĩa
  • Chọn sai theo hướng state store cho một chứng từ thì mất hẳn lý do vì sao nó tới trạng thái hiện tại — mà đó là thứ kiểm toán hỏi

Cách các BC nói chuyện

  • Chính thức: Outbox → Kafka → Projection. 30 topic
  • Hook trong tiến trình: khi cần kết quả ngay (xuất vật tư lúc bắt đầu công đoạn)
  • Đọc chéo đồng bộ: 6 BC đang làm — xem mục 7, đây là chỗ cần quyết định
02 — Order to Cash

Bán hàng → giao hàng → thu tiền

Điểm đáng chú ý: doanh thu ghi nhận lúc XUẤT HOÁ ĐƠN, không phải lúc giao hàng. Ghi ở cả hai chỗ là đúp số, và theo chuẩn Việt Nam doanh thu gắn với hoá đơn GTGT. Giao hàng chỉ ghi giá vốn.

Sơ đồ 2 — Chu trình bán hàng đến thu tiền
sequenceDiagram autonumber actor NV as Nhân viên bán hàng participant SO as Sales participant PR as Pricing participant APR as Approval participant INV as Inventory participant AR as AR participant EI as EInvoice participant GL as Ledger NV->>SO: Lập đơn bán (Draft) SO->>PR: Tra giá (khách riêng > nhóm > chung) PR-->>SO: Đơn giá + chiết khấu NV->>SO: Xác nhận đơn SO->>PR: Kiểm hạn mức công nợ alt Vượt hạn mức PR-->>SO: CREDIT_LIMIT_EXCEEDED SO->>APR: Nộp duyệt vượt APR-->>SO: Đã duyệt → cho xác nhận end NV->>SO: Giao hàng (từng phần được) SO->>INV: Trừ kho theo mã SP (ref "SO …") INV->>GL: Nợ 632 / Có 155 — giá vốn NV->>SO: Xuất hoá đơn SO->>AR: Tạo hoá đơn phải thu AR->>GL: Nợ 131 / Có 511 — doanh thu AR->>GL: Nợ 131 / Có 3331 — thuế GTGT NV->>EI: Lập hoá đơn điện tử EI->>EI: Ký số → cơ quan thuế cấp mã EI->>EI: Gửi người mua (từ đây KHÔNG huỷ được) NV->>AR: Phiếu thu, gán vào hoá đơn AR->>GL: Nợ 1121 / Có 131

Bốn chỗ đã hỏng trong im lặng ở luồng này

  • Hoàn thành lệnh SX không nhập kho thành phẩm → không bán được cái vừa làm; giá vốn = 0 mà doanh thu vẫn đủ
  • Hạn mức công nợ đọc read model → hai đơn 30tr xác nhận cách nhau vài giây đều lọt
  • Người duyệt do phía gọi tự khai → duyệt thay người khác chỉ bằng đổi một trường
  • Đơn bán seed bằng SQL không kèm sự kiện → hiện đủ trên lưới mà không thao tác được gì

Cả bốn đều để lại hệ thống mà tổng vẫn đúng và bảng cân đối vẫn cân. Đó là lý do make e2e đối chiếu từng khoản chứ không kiểm API trả 200.

03 — Procure to Pay

Mua hàng → nhận hàng → trả tiền

Điểm cốt lõi: nhận hàng ghi vào tài khoản trung gian 3388, không ghi thẳng 331. Lúc nhận hàng chưa có hoá đơn NCC — chưa biết số hoá đơn, chưa có thuế đầu vào, số tiền có thể còn lệch. Số dư 3388 chính là chỉ tiêu “hàng đã nhận chưa có hoá đơn”.

Sơ đồ 3 — Chu trình mua hàng đến trả tiền
sequenceDiagram autonumber actor MH as Mua hàng participant PO as Procurement participant APR as Approval participant INV as Inventory participant AP as AP participant GL as Ledger MH->>PO: Lập đơn mua + dòng hàng MH->>PO: Nộp duyệt PO->>APR: Định tuyến theo NGƯỠNG TIỀN APR-->>PO: Đã duyệt MH->>PO: Nhận hàng (từng phần theo dòng) PO->>INV: Nhập kho kèm ĐƠN GIÁ từ dòng đơn mua INV->>GL: Nợ 152 / Có 3388 — hàng chưa có hoá đơn Note over INV,GL: Trước 18/08 hook chỉ truyền SỐ LƯỢNG
→ nhập kho giá 0 → ZERO_AMOUNT → mua hàng không lên sổ MH->>AP: Nhận hoá đơn NCC AP->>AP: Đối chiếu 3 chiều
đơn mua ↔ SỐ ĐÃ NHẬN ↔ hoá đơn alt Không khớp AP-->>MH: MATCH_REQUIRED MH->>AP: Duyệt vượt + BẮT BUỘC nêu lý do end AP->>GL: Nợ 3388 + Nợ 133 / Có 331 MH->>AP: Phiếu chi (phải gán HẾT vào hoá đơn) AP->>GL: Nợ 331 / Có 1121

Vì sao đối chiếu theo SỐ ĐÃ NHẬN chứ không theo số đã đặt

Đặt 100 mới nhận 40 thì hoá đơn 100 là sai dù khớp đơn mua. Đây chính là điểm khác giữa đối chiếu 3 chiều và 2 chiều — và là chốt kiểm soát chi tiền quan trọng nhất của phần mua hàng.

04 — Plan to Make

Kế hoạch → sản xuất → nhập kho thành phẩm

Đây là luồng dài nhất và cũng là nơi sinh ra nhiều lỗi âm thầm nhất, vì nó chạm cả kho, giá thành lẫn sổ cái.

Sơ đồ 4 — Từ kế hoạch tới thành phẩm nhập kho
flowchart TD A["Kế hoạch sản xuất
Planning"] -->|"Chạy MRP: nổ BOM Active × SL"| B["Nhu cầu vật tư
so tồn khả dụng"] B --> C{"Đủ vật tư?"} C -->|"Thiếu"| D["Shortage → đơn mua"] C -->|"Đủ"| E["CRP — kiểm năng lực trạm"] E --> F["Xếp lịch hữu hạn
hết năng lực thì đẩy sang ngày sau"] F --> G["Phát hành kế hoạch"] G --> H["Lệnh sản xuất — Planned"] H -->|"Release"| I["Chụp Routing Active
thành công đoạn của lệnh"] I -->|"Hook tự nộp duyệt"| APR["Approval"] I -->|"Start"| J{"Chốt duyệt
đọc EVENT STORE"} J -->|"Chưa duyệt"| K["APPROVAL_REQUIRED — chặn"] J -->|"Đã duyệt / không khai ma trận"| L["Bắt đầu công đoạn 1"] L --> M["Nổ BOM lọc theo MÃ CÔNG ĐOẠN
× SỐ NHẬN VÀO công đoạn"] M --> N["Quy đổi đơn vị BOM → đơn vị kho"] N -->|"Không quy đổi được"| O["TỪ CHỐI xuất"] N --> P["Trừ kho ref 'MO …/op…'"] P --> Q["Nợ 621 / Có 152"] L --> R["Ghi sản lượng: đạt + phế"] R --> S["Hoàn thành công đoạn"] S -->|"Bán thành phẩm chảy sang bước sau"| L S -->|"Bước cuối"| T["Hoàn thành lệnh"] T --> U["Nhập kho thành phẩm
đơn giá = giá vốn vật tư ĐÃ XUẤT"] T --> V["Tính giá thành
vật tư + nhân công + chi phí chung"] style J fill:#1C222E,stroke:#E1AC36 style O fill:#3B1219,stroke:#EF4444 style K fill:#3B1219,stroke:#EF4444 style M fill:#1C2B1E,stroke:#10B981

Ba cái bẫy đã sập ở luồng này

  • Vật tư nổ theo số KẾ HOẠCH thay vì số nhận vào — bước cắt hỏng 2 thì bước may vẫn lĩnh chỉ cho 200 trong khi chỉ 198 cái mũ đi tới. Sai lệch cộng dồn theo phế phẩm phía trên, giá thành bị thổi lên (vá 26/08)
  • Đơn giá nhập kho đọc read model — chấm công xảy ra vài giây trước khi kết thúc lệnh nên chưa kịp chiếu; giá thành ra 2.000.000 thay vì 2.760.000 mà vẫn là con số trông bình thường
  • Làm tròn SỐ GIỜ thay vì thành tiền — 230 phút → 3,8333 giờ × 120.000 = 459.996 thay vì 460.000

Vì sao giờ máy là GIỜ CHUẨN

Giờ máy = SetupMinutes + MinutesPerUnit × GoodQty, không lấy hiệu số CompletedAt − StartedAt. Mốc thời gian là thời gian trôi qua: công đoạn bắt đầu chiều thứ Sáu xong sáng thứ Hai sẽ thành 72 giờ máy. Cùng công thức với CRP và xếp lịch.

05 — Record to Report

Từ nghiệp vụ tới báo cáo tài chính

Mọi nghiệp vụ đổ về sổ cái qua bút toán tự động — đi qua Kafka, không dùng hook trong tiến trình. Hook chạy trong tiến trình gọi nên lỗi chỉ còn cách nuốt hoặc đánh hỏng nghiệp vụ gốc; qua Kafka có sẵn thử lại và DLQ, và bút toán trễ vài giây không ảnh hưởng xưởng.

Sơ đồ 5 — Bút toán tự động và trình tự cuối kỳ
flowchart TD subgraph SRC["Nghiệp vụ sinh bút toán"] I1["Nhập kho mua hàng"] --> R1["PURCHASE_RECEIPT
152 / 3388"] I2["Xuất vật tư cho SX"] --> R2["PRODUCTION_ISSUE
621 / 152"] I3["Xuất kho bán"] --> R3["SALES_COGS
632 / 155"] I4["Xuất hoá đơn AR"] --> R4["131 / 511 · 131 / 3331"] I5["Hoá đơn NCC"] --> R5["3388 + 133 / 331"] I6["Duyệt bảng lương"] --> R6["622·627·641·642 / 334 · 338x · 3335"] I7["Trích khấu hao"] --> R7["627·641·642 / 214"] I8["Gia công ngoài"] --> R8["154 / 152 · 155 / 154"] end SRC -->|"Outbox → Kafka"| CONS["Consumer định khoản
tra ledger_account_mappings"] CONS --> GUARD{"LedgerPostingGuard
tài khoản hợp lệ? kỳ đang mở?"} GUARD -->|"Không"| FAIL["ledger_posting_failures
NGHIỆP VỤ KHÔNG BIẾN MẤT"] GUARD -->|"Có"| POST["gl_postings — chỉ thêm"] FAIL -->|"sửa nguyên nhân → retry"| POST POST --> CLOSE["TRÌNH TỰ CUỐI KỲ"] subgraph CLOSE2["Năm bước, KHÔNG hoán đổi được"] direction TB S1["1. Duyệt bảng lương
622 · 627 có số"] S2["2. Trích khấu hao E3
627 có số"] S3["3. Khấu trừ GTGT F1
3331 ↔ 133"] S4["4. Kết chuyển CPSX E2b
621·622·627 → 154 → 155"] S5["5. Kết chuyển kết quả E1
doanh thu · chi phí → 911 → 421"] S1 --> S2 --> S3 --> S4 --> S5 end CLOSE --> CLOSE2 S5 --> RPT["B01 Bảng cân đối
B02 Kết quả kinh doanh
B03 Lưu chuyển tiền tệ"] style FAIL fill:#3B2A12,stroke:#F59E0B style CLOSE2 fill:#0F131A,stroke:#F59E0B

Thứ tự năm bước là nguồn của BA lỗi đã ghi

Duyệt lương sau E2b thì toàn bộ chi phí nhân công của kỳ không bao giờ vào giá thành — và bảng cân đối vẫn cân. Nên phải chặn. Nhưng cách chặn hiện tại là mỗi bước tự truy vấn journal_entries để suy ra “bước sau đã chạy chưa”, và chính cách suy đó đã sai ba lần. Xem mục 8.

06 — Nhân sự

Nhân sự và tiền lương — trạng thái thật

D-Pro không có HRM. Nó có phân hệ tính lương, và hai thứ đó khác nhau. Sơ đồ dưới tô đỏ phần chưa có.

Sơ đồ 6 — Tiền lương hiện tại và khoảng trống HRM
flowchart LR subgraph HAVE["ĐÃ CÓ — tính lương đúng luật"] direction TB P1["Chính sách lương
hiệu lực theo thời gian"] P2["Biểu thuế luỹ tiến TỪNG PHẦN
bậc cuối không trần"] P3["Hai mốc trần bảo hiểm
BHXH·BHYT theo lương cơ sở
BHTN theo lương tối thiểu VÙNG"] P4["Ba loại BH tách riêng
3383 · 3384 · 3386"] P5["Bốn mục đích lao động
622 · 627 · 641 · 642"] P6["Bảng kê BHXH
Tờ khai 05/KK-TNCN + XML"] P7["Đối chiếu lương ↔ sổ cái F5"] end subgraph GAP["CHƯA CÓ — quản trị nhân sự"] direction TB G1["Hợp đồng lao động
lương gõ tay MỖI THÁNG"] G2["MST cá nhân · số sổ BHXH
→ tờ khai thiếu dữ liệu bắt buộc"] G3["Người phụ thuộc
là một CON SỐ, không phải danh sách"] G4["Chấm công hành chính
chỉ có chấm công theo CÔNG ĐOẠN SX"] G5["Phép năm · ốm · thai sản"] G6["Trần tăng ca 200/300 giờ"] end ENTRY["PayrollEntry
11 SỐ GÕ TAY mỗi người mỗi tháng"] GAP -.->|"nếu có, sẽ tự điền"| ENTRY ENTRY --> HAVE style HAVE fill:#0F2318,stroke:#10B981 style GAP fill:#3B1219,stroke:#EF4444 style ENTRY fill:#3B2A12,stroke:#F59E0B

Vấn đề gốc: 11 số gõ tay × số người × 12 tháng

PayrollEntry nhận ContractSalary · ActualEarnings · OvertimePay · Dependents… tất cả gõ tay. Nghĩa là: ai tăng lương thì phải nhớ gõ số mới; hệ thống không lưu hợp đồng lao động nói gì — mà đó là thứ thanh tra lao động hỏi đầu tiên. Với xưởng gia công vài trăm đến vài nghìn công nhân thì không vận hành được.

Cách sửa: thêm LabourContract (Event Sourcing — vòng đời ký · phụ lục tăng lương · chấm dứt) và một hàm thuần PayrollProposal.Build(...) điền sẵn PayrollEntry. Giữ nguyên chữ ký PayrollEntry nên bảng lương hiện tại không đổi một dòng, và giữ đường gõ tay làm lối thoát — đúng cách CalculateProductionCost vẫn cho truyền tay overheadCost.

07 — Ràng buộc

Các BC nối vào nhau thế nào

Ba kiểu nối, và chỉ kiểu thứ ba là vấn đề. Sơ đồ tô đỏ những quan hệ đồng bộ — chúng là thứ chặn mọi phương án tách module.

Sơ đồ 7 — Ba kiểu nối giữa các bounded context
flowchart TB subgraph K1["1⃣ QUA KAFKA — nới lỏng, có thử lại và DLQ"] direction LR A1["Aggregate"] --> A2["events + outbox
CÙNG transaction"] A2 --> A3["OutboxPublisher
poll 500ms"] A3 --> A4["Kafka — 30 topic"] A4 --> A5["Projection → read model"] A4 --> A6["Consumer định khoản → sổ cái"] end subgraph K2["2⃣ HOOK TRONG TIẾN TRÌNH — khi cần kết quả NGAY"] direction LR B1["StartOperation"] --> B2["IProductionMaterialIssuer"] B3["ReceiveGoods"] --> B4["IGoodsReceiptInventoryHook"] B5["ShipGoods"] --> B6["IShipmentInventoryHook"] B7["CompleteOrder"] --> B8["IProductionOutputReceiver"] B2 -.->|"lỗi thì vào"| B9["inventory_hook_failures
hiện ra, không nuốt"] end subgraph K3["3⃣ ĐỌC CHÉO ĐỒNG BỘ — 6 BC đang làm"] direction LR C1["Payroll"] -->|"HasResultClosingAsync"| CX["journal_entries"] C2["FixedAssets"] -->|"HasResultClosingAsync"| CX C3["Inventory"] -->|"HasActiveEntryInEventStore"| CX C4["AR"] --> CX C5["AP"] --> CX C6["Ledger"] --> CX C1 -->|"đọc giờ công"| CY["production_labor_bookings"] end style K1 fill:#0F2318,stroke:#10B981 style K2 fill:#0F131A,stroke:#06B6D4 style K3 fill:#3B1219,stroke:#EF4444
Kiểu nốiDùng khiVượt được ranh giới service?
Kafka Bút toán tự động, dựng read model, thông báo Được — đã là hợp đồng qua mạng rồi
Hook Cần kết quả ngay trong cùng lệnh (giá vốn xuất kho) Khó — nhưng đều là best-effort có hàng đợi, chịu được
Đọc chéo đồng bộ Chốt kiểm soát thứ tự và chống ghi trùng Không — đây là chỗ phải sửa trước
08 — Vấn đề gốc

Kỳ kế toán: thứ tự được SUY RA, không được KHAI RA

Bảng accounting_periods chỉ có is_closed boolean. Nhưng thực tế một kỳ đi qua sáu pha. Thứ tự đó không được khai ở đâu — mỗi bước tự truy vấn journal_entries để đoán bước sau đã chạy chưa.

Sơ đồ 8 — Hiện tại: suy ra — Đề xuất: khai ra
flowchart LR subgraph NOW["HIỆN TẠI — mỗi bước tự suy"] direction TB N0["accounting_periods
is_closed: bool"] N1["Duyệt lương"] -->|"query journal_entries
lọc reference_type"| N0 N2["Khấu hao E3"] -->|"query journal_entries"| N0 N3["Kết chuyển E2b"] -->|"query journal_entries"| N0 NB1["Lọc theo source thay vì reference_type"] NB2["Bút toán ĐÃ ĐẢO vẫn chặn"] NB3["Khấu trừ GTGT chặn luôn E2b và lương"] end subgraph NEW["ĐỀ XUẤT — pha tường minh"] direction TB M0["AccountingPeriod — aggregate có PHA"] M1["Open"] --> M2["PayrollClosed"] --> M3["Depreciated"] M3 --> M4["VatSettled"] --> M5["CostTransferred"] --> M6["ResultClosed"] --> M7["Closed"] MQ["Mọi bước chỉ hỏi MỘT câu:
kỳ đang ở pha nào"] M0 --- MQ end NOW -->|"sửa"| NEW style NOW fill:#3B1219,stroke:#EF4444 style NEW fill:#0F2318,stroke:#10B981

Ba lỗi, một nguyên nhân

Lỗi đã xảy raVì sao
Lọc theo source thay vì reference_typeMỗi chỗ tự suy, mỗi chỗ suy một kiểu
Bút toán đã đảo vẫn chặn — kế toán làm đúng hướng dẫn khắc phục rồi nhận y nguyên thông báo đóSuy từ dữ liệu chỉ-thêm, không suy từ trạng thái
Khấu trừ GTGT (F1) chặn luôn E2b, khấu hao và duyệt lươngHai nghiệp vụ khác nhau cùng ghi source='Closing'

Cả ba là cùng một lỗi. Làm pha tường minh thì không còn chỗ để suy sai — sáu BC kia thôi đọc chéo, vì pha của kỳ là một sự kiện nhỏ đổi chậm, đi qua Kafka được.

09 — Quyết định

Ba quyết định đang mở

Sơ đồ 9 — Thứ tự phụ thuộc giữa ba quyết định
flowchart TD D1["PHA KỲ KẾ TOÁN
AccountingPeriod thành aggregate có pha"] D2["HRM
hợp đồng LĐ · người phụ thuộc · chấm công · phép"] D3["TÁCH SERVICE?
modulith rút được ra vs microservice"] D4["ĐA TIỀN TỆ
tỷ giá · TK 413 · đánh giá lại cuối kỳ"] D1 -->|"gỡ bỏ đọc chéo đồng bộ"| D2 D1 -->|"điều kiện tiên quyết"| D3 D2 -->|"schema hr.* + architecture test
= module rút được ra ĐẦU TIÊN"| D3 D4 -.->|"độc lập — nhưng chặn khách XUẤT KHẨU"| D2 style D1 fill:#1C222E,stroke:#E1AC36 style D4 fill:#3B2A12,stroke:#F59E0B

Pha kỳ kế toán — làm TRƯỚC

  • Sửa ba lỗi có thật, không phải phòng xa
  • Màn Cuối kỳ hiện đúng bước tiếp theo thay vì suy từ số liệu
  • Điều kiện tiên quyết của mọi phương án tách

HRM — làm module rút được ra đầu tiên

  • Schema riêng hr.* + role DB riêng → cô lập dữ liệu cá nhân (NĐ 13/2023)
  • Cần vai trò HR mới — hiện lương nằm dưới Finance, mà nhân sự không được xem sổ cái
  • Tách hr-profile khỏi hr-salary: tổ trưởng xem bảng công của tổ nhưng không xem lương
  • Architecture test đỏ nếu HR chạm schema khác → biến “composable” thành thứ được ép

Tách service — chưa, nhưng giữ đường

Codebase đã ở rất gần mức “modulith rút được ra”: mỗi BC đã có Registry riêng, sự kiện riêng, Kafka topic riêng, và src/Worker đã chứng minh mã chạy tách tiến trình được. Thiếu đúng hai thứ: schema riêngbỏ đọc chéo đồng bộ.

Làm xong ① và ② thì việc rút một service ra trở thành bước cơ học. Điều kiện đảo ngược — sẽ tách thật khi có ít nhất một trong ba: HRM bán riêng cho khách không dùng ERP · có đội khác sở hữu với nhịp phát hành khác · yêu cầu pháp lý bắt dữ liệu nhân sự nằm hạ tầng tách biệt.

Đa tiền tệ — khoảng trống lớn nhất chưa nói tới

Đo được: 0 bảng có cột tỷ giá, mọi hoá đơn đều VND, không có TK 413 chênh lệch tỷ giá. Nhưng ngành mặc định vừa chọn là gia công giày OEM xuất khẩu — khách hàng trả bằng USD/EUR/JPY. Không có tỷ giá thì không xuất được một hoá đơn nào cho khách thật.

Nó độc lập với ba quyết định trên, nhưng chạm vào lõi kế toán nên càng để lâu càng đắt.

Và một khoảng trống pháp lý: gia công xuất khẩu

0 bảng hải quan. Gia công cho nước ngoài ở Việt Nam chạy theo loại hình E21/E52: nhập nguyên liệu miễn thuế → xuất thành phẩm → báo cáo quyết toán nguyên vật liệu với hải quan (mẫu 15/BCQT). Đó là nghĩa vụ pháp lý, không phải tiện ích. Không có nó thì xưởng gia công không dùng D-Pro cho việc chính của họ được.