Skip to content

Lộ trình xây dựng (Roadmap) ​

Tài liệu này là nguồn sự thật cho toàn bộ site — mọi trang khác đều bám theo nội dung ở đây. Mục tiêu: xây dựng 2 hệ thống mô phỏng nghiệp vụ ngân hàng lõi bằng Java/Spring, dựa trên tìm hiểu sâu về nghiệp vụ banking và các mẫu thiết kế hệ thống tài chính/phân tán liên quan.

Tổng quan ​

Thời gian3–6 tháng
Cấu trúc2 sub-project song song, tách theo domain
Nguyên tắcĐọc tài liệu gốc trước → hiểu bản chất → rồi mới code
Đầu ra2 repo GitHub + design doc + tài liệu này (public trên VitePress)

Hai domain trong banking có tư duy thiết kế khác nhau khá rõ, nên được tách thành hai sub-project độc lập thay vì gộp chung:

  • Sub-project B — Core Banking: domain "trạng thái" (state-centric). Trọng tâm là tính toàn vẹn dữ liệu, ACID, double-entry ledger.
  • Sub-project A — Interbank Payment Gateway: domain "luồng đi" (flow-centric). Trọng tâm là messaging, orchestration, resilience.

Sơ đồ tổng quan ​

Sơ đồ tổng quát 6 tháng: Core Banking, Payment Gateway, Tích hợpBa giai đoạn hai tháng mỗi giai đoạn: Tháng 1-2 Core Banking, Tháng 3-4 Payment Gateway, Tháng 5-6 Tích hợp và hoàn thiện.Tháng 1-2Core BankingAccount, ledger,transaction serviceTháng 3-4Payment GatewayISO 20022, Saga,EOD batchTháng 5-6Tích hợpKết nối 2 hệ thống,test, docsTuần 1-2Đọc double-entry,JPA, transactionTuần 3-5Code Account,Ledger, TransactionTuần 6-8Locking, ADR, test,devlog cập nhậtTuần 9-10Đọc ISO 20022,Saga, Spring BatchTuần 11-14Code parsing,orchestrationTuần 15-17EOD batch, ADR,idempotency testTuần 18-19Kết nối 2 hệthống, SecurityTuần 20-22Unit, integrationtest đầy đủTuần 23-26Hoàn thiện docs

Giai đoạn 1 — Tháng 1–2: Core Banking (Sub-project B) ​

Bắt đầu từ đây vì đây là domain nền tảng, dễ tiếp cận hơn với người đã có nền Spring Boot/JPA, và lót đường tư duy cho phần Saga phức tạp hơn ở giai đoạn sau.

Đọc trước ​

  • Double-entry bookkeeping — nguyên lý kế toán kép
  • Spring Data JPA — query methods, relationships, transaction boundaries
  • Transaction management: @Transactional, isolation levels, propagation

Xây dựng ​

  • Account Service — tạo/tra cứu tài khoản
  • Ledger Service — sổ cái double-entry (mỗi giao dịch ghi Nợ/Có cân bằng)
  • Transaction Service — xử lý giao dịch nội bộ, đảm bảo tính nhất quán số dư

Học sâu ​

  • ACID và ý nghĩa thực tế trong hệ thống tài chính
  • Optimistic vs pessimistic locking — khi nào dùng cái nào
  • Audit trail design — vì sao ngân hàng không bao giờ "sửa" bản ghi, chỉ "thêm" bản ghi bù trừ

DDD Refactor — hoàn thành 2026-09-16 ​

Refactor tầng domain của Core Banking theo hướng DDD, thực hiện qua 2 bước liên tiếp trong ngày:

  • Money value object — bọc BigDecimal + Currency, add()/subtract() throw CurrencyMismatchException khi lệch loại tiền; LedgerEntry/Account chuyển sang dùng Money/Currency thay vì BigDecimal/String thô, giúp LedgerService.validateBalanced() phát hiện ngay giao dịch trộn lẫn nhiều loại tiền tệ thay vì cộng sai lặng lẽ.
  • Transaction aggregate root — thêm entity Transaction (implements Persistable<UUID>) làm aggregate root cho nhóm LedgerEntry, enforce bất biến Nợ=Có ngay tại tầng domain thay vì chỉ ở service; bổ sung migration V4__create_transactions_table.sql và test TransactionTest.

Quyết định thiết kế đầy đủ: ADR-008. Nhật ký chi tiết từng bước: Devlog — Giai đoạn 1.

Outbox Pattern + Kafka Event Publishing — hoàn thành 2026-09-17 ​

Thông báo sự kiện Transaction (mới/hoàn tác) cho các hệ thống khác qua Kafka, không đánh đổi rủi ro Dual Write giữa ghi DB và gửi message:

  • Outbox Pattern — Transaction tự raise domain event, LedgerService lưu vào bảng outbox_events trong cùng transaction DB với dữ liệu nghiệp vụ; tiến trình OutboxEventPublisher (@Scheduled) đọc và gửi lên Kafka sau, đảm bảo tính nguyên tử giữa ghi nghiệp vụ và ghi sự kiện.
  • Audit/Compliance Consumer — ghi 1 dòng compliance_records cho mỗi LedgerEntry, idempotent qua bảng processed_events (chống xử lý trùng do Kafka at-least-once delivery).
  • 3 PoC Consumer — Fraud Detection, Notification, Reporting, mỗi consumer 1 groupId riêng trên cùng topic transaction-posted-topic, chứng minh kiến trúc publish/subscribe hoạt động cho nhiều hệ thống độc lập.

Quyết định thiết kế đầy đủ: ADR-009. Nhật ký chi tiết từng bước: Devlog — Giai đoạn 1.

Redis Cache cho Account Balance — hoàn thành 2026-09-17 ​

Cache-aside cho AccountService.getBalance(), không đánh đổi invariant chống overdraft đã có ở Pessimistic Locking:

  • Cache-aside — AccountBalanceCache (StringRedisTemplate) cache balance theo account, TTL cấu hình được (mặc định 3600s); withdraw() luôn tính thẳng từ DB qua computeBalanceFromDb(), không bao giờ đọc cache khi kiểm tra invariant.
  • Invalidation AFTER_COMMIT — AccountBalanceCacheEvictionListener evict cache qua @TransactionalEventListener(phase = AFTER_COMMIT), tái sử dụng TransactionPostedEvent/TransactionReversedEvent đã có từ tính năng Kafka, thu hẹp cửa sổ race so với evict trước khi commit.

Quyết định thiết kế đầy đủ: ADR-010. Nhật ký chi tiết từng bước: Devlog — Giai đoạn 1.

Oracle Dual-Profile Support — hoàn thành 2026-09-17 ​

Chứng minh khả năng vận hành trên hạ tầng CSDL doanh nghiệp (Oracle) song song với PostgreSQL, cùng 1 codebase:

  • Dual-profile — db/migration/postgresql/ và db/migration/oracle/ tách riêng hoàn toàn (V1-V7 mỗi bộ), Flyway locations cấu hình theo Spring Profile (application.properties / application-oracle.properties); Oracle Database Free qua Docker, port 1522.
  • Viết lại 7 migration theo dialect Oracle — UUID → RAW(16), UPDATE ... FROM → correlated subquery / MERGE INTO, JSONB → JSON, partial index → function-based index.
  • Phát hiện & fix bug Money — chạy test trên Oracle lộ ra Money chưa tự chuẩn hóa scale (Oracle NUMBER không giữ scale cố định như Postgres NUMERIC); cố định scale = 2 cho mọi currency trong Money.java.
  • Kết quả: 45/45 test pass nguyên vẹn trên cả 2 database, không cần sửa test theo profile.

Quyết định thiết kế đầy đủ: ADR-011 (dual-profile), ADR-012 (fix Money scale). Nhật ký chi tiết từng bước: Devlog — Giai đoạn 1.

Kubernetes Deployment — hoàn thành 2026-09-17 ​

Verify Pessimistic Locking (chống overdraft) giữ vững khi nhiều instance ứng dụng chạy song song, qua deployment K8s thật:

  • REST Controller — thêm AccountController (trước đó không có endpoint HTTP nào), bắt buộc phải có để verify locking qua nhiều Pod (nhiều JVM riêng biệt) thay vì gọi thẳng service trong cùng JVM như test hiện có.
  • Dockerfile + fix Kafka listener — multi-stage build; thêm listener Kafka thứ 2 (PLAINTEXT_HOST, qua host.docker.internal) vì localhost bên trong container không phải máy host.
  • Deploy K8s — Namespace/ConfigMap/Secret/Deployment/Service (NodePort) trên Docker Desktop Kubernetes, data layer (Postgres/Oracle/Kafka/Redis) vẫn giữ ở docker-compose ngoài cluster.
  • Verify thực nghiệm: scale 3 replicas, bắn 5 request withdraw đồng thời — 1 thành công, 4 bị từ chối HTTP 409, xác nhận qua kubectl logs rằng request thực sự phân tán qua cả 3 Pod (không phải do K8s tình cờ route về 1 chỗ).

Quyết định thiết kế đầy đủ: ADR-013. Nhật ký chi tiết từng bước: Devlog — Giai đoạn 1.


Giai đoạn 2 — Tháng 3–4: Interbank Payment Gateway (Sub-project A) ​

Đọc trước ​

  • ISO 20022 message structure — pain.001 (khởi tạo lệnh chuyển tiền), pacs.008 (chuyển tiền liên ngân hàng)
  • Saga pattern — orchestration vs choreography
  • Spring State Machine / Spring Batch docs

Xây dựng ​

  • Message parsing & validation cho pain.001 / pacs.008
  • Saga orchestration cho luồng thanh toán liên ngân hàng
  • EOD (End-of-Day) batch settlement bằng Spring Batch
  • Exception/ops handling — xử lý giao dịch lỗi, retry, dead-letter

Học sâu ​

  • Distributed transaction problem — vì sao 2PC không hợp với hệ thống phân tán quy mô lớn
  • Compensating transactions — "hoàn tác" trong hệ phân tán khác gì rollback thông thường
  • Idempotency — vì sao một message bị gửi lại 2 lần không được phép tạo ra 2 giao dịch

Giai đoạn 3 — Tháng 5–6: Tích hợp & hoàn thiện ​

  • Kết nối 2 sub-project: Payment Gateway gọi vào Core Banking để cập nhật số dư thực tế
  • Bổ sung Spring Security cho API (authentication/authorization)
  • Viết test kỹ: unit test + integration test
  • Viết README / design doc — giải thích các quyết định kiến trúc
  • Tổng hợp lý do đằng sau các quyết định kiến trúc chính: vì sao chọn Saga thay vì 2PC, vì sao thiết kế ledger theo kiểu double-entry, vì sao tách 2 sub-project...

Phương pháp học ​

  • Học trước, code sau: mỗi module chính đều bắt đầu bằng việc đọc tài liệu gốc (Spring docs, ISO 20022 spec), không phải đọc để "biết" mà đọc để hiểu vì sao thiết kế như vậy.
  • Ghi chép tay song song: dùng sổ tay chia 2 phần —
    • Khái niệm học được: kiến thức tổng quát, có thể tái dùng về sau
    • Nhật ký dự án: quyết định kiến trúc, vấn đề gặp phải, cách giải quyết — đây chính là nguyên liệu cho design doc
  • Ưu tiên hiểu sâu hơn tốc độ: timeline 3–6 tháng là ước lượng, không phải deadline cứng.

Nhật ký tiến độ ​

Phần này sẽ được cập nhật theo thời gian khi triển khai từng giai đoạn. Chi tiết theo từng giai đoạn xem tại Devlog.

  • [x] Giai đoạn 1: Core Banking
  • [ ] Giai đoạn 2: Interbank Payment Gateway
  • [ ] Giai đoạn 3: Tích hợp & hoàn thiện

Cập nhật lần cuối:

Simulated Banking & Fintech Systems — Java & Spring Boot