Backend

System Design — Bài 13: Microservices hay Modular Monolith? Chọn ranh giới và tính chi phí

SSite Admin
10 tháng 10, 2026 6 phút đọc 0 lượt xem

Tách code thành mười service không bảo đảm mười đội có thể phát hành độc lập. Nếu mỗi thay đổi vẫn phải release cùng lúc, bạn đang chịu chi phí mạng mà chưa nhận được lợi ích mong muốn. Hãy bắt đầu từ ranh giới nghiệp vụ, ownership dữ liệu và mục tiêu cụ thể của việc tách.

Khóa học System Design: mục lục 21 bài — bạn đang đọc bài 13.

Microservices — được gì, mất gì

Microservices có thể cho đội sở hữu một miền nghiệp vụ và triển khai độc lập. Một phần xử lý ảnh có thể scale riêng với phần quản lý tài khoản. Ranh giới tốt giúp giảm phối hợp khi thay đổi và cô lập một số loại lỗi.

Đổi lại là deployment, monitoring, tracing, hợp đồng API và dữ liệu phân tán. Nếu một thay đổi nhỏ vẫn phải release đồng thời năm service, lợi ích độc lập chưa đạt được dù code nằm ở năm repository.

Các thành phần discovery, gateway và load balancer hỗ trợ vận hành, nhưng không giải việc chọn biên nghiệp vụ. Kubernetes cũng không tự tạo authorization hay thiết kế recovery. Hãy xem cấu trúc đội, nhịp thay đổi và ownership dữ liệu trước; động lực tổ chức thường quan trọng không kém nhu cầu throughput.

Modular Monolith — lựa chọn bị bỏ quên

Modular monolith có một đơn vị triển khai nhưng chia module theo domain. Mỗi module có API nội bộ và dữ liệu mình sở hữu. Giao tiếp trong tiến trình tránh vòng mạng không cần thiết, nhưng vẫn cần quy tắc cấm truy cập tùy tiện vào nội bộ module khác.

Ví dụ module Order gọi facade của Inventory thay vì trực tiếp sửa bảng tồn. Dữ liệu có thể bắt đầu ở các schema riêng trong cùng database. Nếu sau này tách hạ tầng, ranh giới sở hữu đã rõ sẽ giúp giảm công việc di trú.

Tuy nhiên, đổi connection string không đủ nếu còn join hoặc transaction xuyên module. In-memory event cũng không tự bền sau crash. Modular monolith là cách quản lý độ phức tạp và hoãn một số chi phí phân tán, chứ không tự bảo đảm mọi thứ tách ra được dễ dàng.

Khi nào mới nên tách — và cái giá bằng tiền

Có thể cân nhắc tách khi một module cần scale khác biệt, yêu cầu bảo mật khác, phát hành nhanh hơn phần còn lại hoặc đã có đội sở hữu riêng. Các dấu hiệu là gợi ý cần phân tích, không phải đủ hai ô là tự động tách.

Hãy tính cả chi phí. Ví dụ mỗi tháng giảm từ 30.000 xuống 15.000 đơn vị tiền, còn di trú tốn 500.000, thời gian hòa vốn đơn giản là khoảng 33 tháng. Nếu chi phí trực vận hành và nền tảng tăng, thời gian thực còn dài hơn.

Ngoài tiền là rủi ro chuyển dữ liệu và thời gian chậm phát triển tính năng. Có khi lợi ích chính là tổ chức hoặc tuân thủ ranh giới dữ liệu, không phải tiết kiệm máy. Cần ghi rõ mục tiêu để sau khi tách có thể đánh giá thành công.

Sáu kỹ thuật phân rã

Nguyên tắc đầu tiên là đặt phần liên quan chặt trong cùng ranh giới, giảm phụ thuộc qua biên. Bắt đầu thô rồi chia nhỏ khi hiểu rõ hơn; tách quá mịn dễ tạo chuỗi gọi dài và nhiều distributed transaction.

Có thể chọn phần non-core ít rủi ro để luyện quy trình di trú. Một root service như User có nhiều bên phụ thuộc nên cần cân nhắc kỹ; mức dùng chung cao không tự có nghĩa phải tách nó đầu tiên. Thứ tự phải dựa trên khả năng cắt ranh giới và blast radius.

Dual run giúp so kết quả cũ và mới, nhưng không được để cả hai tạo cùng side effect như thu tiền. Có thể shadow phần đọc hoặc so đầu ra trước khi chuyển quyền ghi. Event storming và domain storytelling hỗ trợ tìm biên từ quy trình nghiệp vụ thay vì từ tên bảng.

Vẽ và ghi lại kiến trúc

C4 dùng bốn mức nhìn: context cho thấy hệ với người dùng và hệ ngoài; container cho thấy ứng dụng và kho dữ liệu; component đi sâu bên trong; code là chi tiết triển khai. Container ở đây không nhất thiết là Docker container.

Architecture Decision Record ghi bối cảnh, lựa chọn, phương án đã cân nhắc và hệ quả. Khi quyết định thay đổi, thêm bản ghi thay thế thay vì xóa mất lý do cũ. Sáu tháng sau, người mới có thể hiểu vì sao một lựa chọn từng hợp lý.

Ranh giới quan trọng nên được kiểm tra tự động khi có thể, như module nào không được phụ thuộc module nào. Sơ đồ, quyết định và kiểm tra bổ sung cho nhau: sơ đồ giúp nhìn, bản ghi giúp hiểu lý do, còn kiểm tra ngăn code đi lệch một cách âm thầm.

Một ranh giới module có thể kiểm soát

Một deployment:
  Order --gọi public API--> Inventory
    |                         |
  dữ liệu Order           dữ liệu Inventory

Không sửa trực tiếp bảng của module khác.
Trước khi tách: kiểm kê JOIN/transaction qua biên.

Hòa vốn đơn giản:
Chi phí di trú / tiết kiệm mỗi tháng
= 500.000 / 15.000 = 33,3 tháng

Tiền trong ví dụ là đơn vị giả định. Chi phí vận hành mới, rủi ro di trú và thời gian ngừng phát triển tính năng có thể làm thời gian hòa vốn dài hơn. ADR nên ghi cả phương án chưa chọn và điều kiện phải xem lại quyết định.

Thuật ngữ cần nhớ

  • C4 — Context, Container, Component, Code: Bốn mức zoom của sơ đồ kiến trúc: bối cảnh hệ thống, ứng dụng/kho dữ liệu triển khai được, thành phần bên trong và mã nguồn. Container ở đây không mặc định là Docker container.

  • ADR — Architecture Decision Record: Bản ghi quyết định kiến trúc: lưu bối cảnh, phương án cân nhắc, lựa chọn và hệ quả để người sau hiểu lý do.

  • API — Application Programming Interface: Giao diện lập trình ứng dụng: hợp đồng để thành phần khác gọi chức năng, gồm đầu vào, đầu ra và quy tắc lỗi.

Bài tập tự thực hành

Một đội nhỏ muốn tách mười service ngay.

Gợi ý kiểm tra lời giải

Hỏi động lực, ownership, chi phí vận hành; đề xuất ranh giới module trước nếu phù hợp.

Tự kiểm tra sau bài học

  • Module có dữ liệu sở hữu và hợp đồng giao tiếp rõ.

  • Lợi ích tách service được đo bằng mục tiêu cụ thể.

  • Dual run không tạo side effect hai lần, đặc biệt với thanh toán.

Đọc thêm từ tài liệu gốc

C4 Model: các mức mô tả kiến trúc

Tiếp tục lộ trình

Bài trước — Saga, Outbox và resilience: giữ dữ liệu đúng khi service lỗi

Bài tiếp — Cấu trúc dữ liệu và concurrency qua bài toán một triệu heartbeat

Xem toàn bộ khóa học System Design 21 bài

S

Site Admin

Engineer and writer. Building things with TypeScript and distributed systems.

Bình luận (0)

Bạn cần đăng nhập bằng Google để bình luận.

Hãy là người bình luận đầu tiên.

Bài viết liên quan

K

Học System Design qua 21 bài: yêu cầu, capacity, database, cache, hệ phân tán và 6 bài thực hành, kèm bài tập, sơ đồ luồng và mục lục đầy đủ.

10 thg 10, 20266 phút2
S

Học cách đi từ vấn đề đến kiến trúc, phân biệt architecture với design và bảo vệ lựa chọn bằng yêu cầu, số liệu và đánh đổi.

10 thg 10, 20266 phút2
S

Đặt câu hỏi về người dùng, thao tác, tải, độ trễ và tính đúng đắn trước khi chọn database hay vẽ sơ đồ kiến trúc.

10 thg 10, 20266 phút1