TOGAF

Kiến trúc ứng dụng (Application Architecture): Design patterns cho các hệ thống doanh nghiệp lớn

7/19/2026 · 13p đọc


title: "Kiến trúc ứng dụng (Application Architecture): Design patterns cho các hệ thống doanh nghiệp lớn"
series: "TOGAF Mastery: Kiến trúc sư Doanh nghiệp Chuyên sâu"
part: "Phần 3 — Kỹ thuật thiết kế & Mô hình hóa (Modeling & Artifacts)"
order: 26
audience: "Enterprise Architect (EA) & Solutions Architect hướng EA"
reading_time: "13 phút"
tags:

  • TOGAF
  • Application Architecture
  • Microservices
  • Event-Driven Architecture
  • SOA
  • Monolith
  • Enterprise Architecture

Kiến trúc ứng dụng (Application Architecture): Design patterns cho các hệ thống doanh nghiệp lớn

Tôi từng ngồi trong một buổi review kiến trúc mà kỹ sư trưởng trình bày kế hoạch tách một hệ thống ERP nội bộ — vốn đang chạy ổn với 6 người dùng backend team — thành 24 microservices. Lý do được đưa ra: "Netflix làm vậy, Amazon làm vậy, đây là chuẩn công nghiệp." Không ai trong phòng hỏi câu quan trọng nhất: đội ngũ vận hành có 3 người, ai sẽ trực 24 service mesh đó lúc 2 giờ sáng khi một service downstream timeout dây chuyền?

Đây là hiểu lầm phổ biến nhất mà tôi gặp ở cấp Application Architecture: coi pattern kiến trúc là một thang bậc tiến hóa — monolith là "lạc hậu", microservices là "hiện đại", ai chưa lên microservices là chưa trưởng thành. Thực tế ngược lại. Ở tầm Enterprise Architect, Application Architecture không phải là chọn công nghệ hot nhất, mà là quyết định cấu trúc ranh giới (boundary), sự phụ thuộc (coupling), và mô hình vận hành cho hàng chục đến hàng trăm ứng dụng cùng tồn tại, tương tác, và tiến hóa qua nhiều năm — trong giới hạn thật của tổ chức: số người, kỹ năng, ngân sách vận hành, và tốc độ thay đổi nghiệp vụ.

Khác biệt căn bản với thiết kế một ứng dụng đơn lẻ: một Solutions Architect thiết kế cho một hệ thống, tối ưu cho bài toán cụ thể trước mắt. Một Enterprise Architect thiết kế cho một danh mục ứng dụng (application portfolio) — nơi quyết định pattern cho một hệ thống hôm nay sẽ ràng buộc cách 5, 10 hệ thống khác tích hợp với nó trong 5 năm tới. Sai lầm ở tầng này không sập một ứng dụng, nó tạo nợ kiến trúc lan khắp doanh nghiệp.

Bản chất

Trong TOGAF ADM, Application Architecture là sản phẩm chính của Giai đoạn C (Information Systems Architecture) — cùng với Data Architecture đã bàn ở bài trước. Application Architecture trả lời ba câu hỏi cốt lõi: hệ thống nào cần tồn tại để hiện thực hóa Business Capability đã xác định ở Giai đoạn B, các hệ thống đó phân rã thành các thành phần (application component) như thế nào, và chúng giao tiếp với nhau theo cơ chế nào.

Điểm mà nhiều Solutions Architect chuyển sang vai trò EA hay bỏ lỡ: TOGAF không quy định một pattern kiến trúc ứng dụng cụ thể nào là "đúng". Framework chỉ yêu cầu bạn phải có một Target Application Architecture nhất quán, truy vết được về Business Architecture, và một Application Portfolio Catalog ghi nhận trạng thái từng ứng dụng (baseline, target, retire). Việc chọn monolith, microservices, SOA hay event-driven là quyết định thiết kế — nó thuộc về Architecture Decision, phải được lập luận, ghi lại (Architecture Decision Record), và bảo vệ được trước Architecture Review Board.

Ba khái niệm nền tảng cần phân biệt rạch ròi trước khi so sánh pattern:

Coupling (mức độ ràng buộc) — mức độ một thành phần phụ thuộc vào chi tiết triển khai của thành phần khác. Coupling chặt (tight coupling) nghĩa là thay đổi ở A buộc phải thay đổi ở B. Đây là biến số quan trọng nhất khi đánh giá pattern, quan trọng hơn cả việc code có nằm trong một process hay nhiều process.

Cohesion (mức độ gắn kết nội bộ) — mức độ các phần tử bên trong một thành phần cùng phục vụ một trách nhiệm nghiệp vụ. Cohesion cao là mục tiêu bất biến của mọi pattern: một service, một module, hay một bounded context tốt đều phải có cohesion cao, bất kể nó được deploy như thế nào.

Deployment boundary (ranh giới triển khai) — việc một thành phần được deploy độc lập hay cùng với thành phần khác. Đây chính là trục phân biệt monolith với microservices — nhưng nó là quyết định vận hành (operational), không tự động quyết định chất lượng kiến trúc. Một monolith module hóa tốt (modular monolith) với coupling lỏng và cohesion cao có thể vượt trội một cụm microservices coupling chặt qua API đồng bộ.

Nhầm lẫn phổ biến nhất: đánh đồng "chia nhỏ deployment boundary" với "giảm coupling". Nhiều tổ chức tách monolith thành microservices nhưng giữ nguyên các lời gọi đồng bộ chằng chịt giữa service — kết quả là distributed monolith: mất hết lợi ích cô lập lỗi và scale độc lập của microservices, nhưng gánh đủ chi phí vận hành phân tán (network latency, observability, deployment pipeline riêng cho từng service).

Khung & Kỹ thuật áp dụng

Bốn pattern kiến trúc ứng dụng phổ biến nhất ở tầm doanh nghiệp, đánh giá theo hai trục: độ phức tạp vận hành (operational complexity) và khả năng mở rộng độc lập (independent scalability).

Pattern Độ phức tạp vận hành Khả năng mở rộng độc lập Tốc độ thay đổi nghiệp vụ phù hợp Quy mô đội tối thiểu khuyến nghị Rủi ro chính nếu áp dụng sai bối cảnh
Monolith có kiểm soát (modular monolith) Thấp — một pipeline CI/CD, một runtime, quan sát tập trung Thấp — scale cả khối, không tách được theo module tải cao Trung bình — release theo lô, phối hợp release trong module dễ 3–15 kỹ sư Coupling ngầm giữa module tăng dần nếu không kỷ luật ranh giới nội bộ (module boundary, dependency rule)
SOA (Service-Oriented Architecture) cổ điển Cao — ESB (Enterprise Service Bus) là điểm trung tâm hóa, cần đội vận hành middleware riêng Trung bình — scale theo service qua ESB, nhưng ESB dễ thành bottleneck Chậm đến trung bình — thay đổi contract qua ESB tốn chi phí governance 30+ kỹ sư, có đội middleware chuyên trách ESB trở thành single point of failure và cổ chai thay đổi; chi phí license, vận hành cao
Microservices Rất cao — mỗi service một pipeline, cần service mesh, distributed tracing, contract testing Cao — scale độc lập từng service theo tải thực tế Nhanh — team nhỏ sở hữu service, release độc lập 50+ kỹ sư, tổ chức theo mô hình 2-pizza team, có nền tảng DevOps trưởng thành Distributed monolith nếu coupling không được cắt thật; chi phí vận hành vượt xa lợi ích nếu đội còn nhỏ
Event-Driven Architecture (EDA) Cao — cần message broker, event schema registry, xử lý eventual consistency Cao — consumer scale độc lập theo throughput sự kiện Rất nhanh cho luồng bất đồng bộ, nhưng khó cho luồng cần phản hồi tức thời 20+ kỹ sư, quen tư duy bất đồng bộ và eventual consistency Khó truy vết luồng nghiệp vụ (traceability), khó debug khi sự kiện mất thứ tự hoặc trùng lặp

Lưu ý: EDA không loại trừ ba pattern kia — nó là một chiều bổ sung. Một hệ thống có thể là modular monolith với một số luồng xử lý theo event nội bộ, hoặc là microservices giao tiếp chủ yếu qua event thay vì gọi API đồng bộ (choreography thay vì orchestration).

Nguyên tắc chọn pattern — không có pattern "tốt nhất tuyệt đối":

  1. Conway's Law đi trước, không đi sau. Cấu trúc hệ thống sẽ phản chiếu cấu trúc giao tiếp của tổ chức xây nó. Nếu đội chỉ có 5 kỹ sư ngồi chung một phòng, họ sẽ tự nhiên coupling chặt bất kể vẽ bao nhiêu boundary trên giấy — chọn monolith có kiểm soát và đầu tư kỷ luật module hóa nội bộ thay vì ép microservices ngược với cấu trúc đội.

  2. Chọn theo tốc độ thay đổi nghiệp vụ khác biệt giữa các phần, không phải theo tải kỹ thuật. Nếu module Thanh toán thay đổi hàng tuần theo yêu cầu compliance còn module Danh mục sản phẩm gần như tĩnh, đó là tín hiệu thật để tách boundary — không phải vì tải CPU khác nhau.

  3. Đo chi phí vận hành phân tán trước khi cam kết. Mỗi service boundary mới phát sinh chi phí cố định: một pipeline CI/CD, một bộ dashboard giám sát, một hợp đồng API cần versioning, một điểm cần contract testing. Nhân chi phí này với số service dự kiến và so với năng lực đội thực tế — không phải năng lực mong muốn có trong tương lai.

  4. Strangler Fig cho hệ thống legacy, không Big Bang rewrite. Khi cần tách một module ra khỏi monolith hiện hữu, tách dần từng capability theo Business Capability Model (đã lập ở Giai đoạn B), giữ hệ thống cũ chạy song song, chuyển traffic từng phần — không đóng băng phát triển nghiệp vụ để rewrite toàn bộ.

  5. Tách theo Bounded Context, không tách theo tầng kỹ thuật. Sai lầm cổ điển là tách "service giao diện", "service logic", "service dữ liệu" — tạo ra coupling ngang chặt hơn cả monolith. Ranh giới đúng đi theo domain nghiệp vụ (đơn hàng, khách hàng, kho) — mỗi bounded context sở hữu trọn logic và dữ liệu của mình.

Case thực tế minh hoạ

(Tình huống minh hoạ điển hình, tổng hợp từ các mẫu hình thường gặp — không phải case của một công ty cụ thể, không có số liệu định lượng thật.)

Một doanh nghiệp thương mại điện tử tầm trung, đội kỹ thuật khoảng 12 người, đang vận hành một monolith Node.js xử lý toàn bộ nghiệp vụ: catalog, giỏ hàng, thanh toán, vận đơn, khuyến mãi. Hệ thống chạy ổn, nhưng ban lãnh đạo kỹ thuật mới tham dự một hội nghị công nghệ, nghe nhiều case study về microservices từ các công ty quy mô hàng nghìn kỹ sư, và quyết định "hiện đại hóa" toàn bộ hệ thống sang microservices trong một quý.

Đội tách hệ thống thành 18 service theo ranh giới kỹ thuật lẫn nghiệp vụ chưa rõ ràng — một số theo tầng (service "validation", service "notification"), một số theo domain (service "cart", service "order"). Không có service mesh, không có distributed tracing, không có contract testing tự động. Giao tiếp giữa service chủ yếu qua gọi HTTP đồng bộ trực tiếp — đúng dạng distributed monolith.

Hậu quả xuất hiện trong vòng hai tháng: một request đặt hàng đi qua 7 service tuần tự, mỗi service thêm độ trễ mạng — thời gian phản hồi tăng gấp ba so với monolith cũ. Khi service "inventory" bị chậm do một truy vấn không tối ưu, toàn bộ luồng đặt hàng bị nghẽn theo — vì không có circuit breaker hay timeout hợp lý. Đội 12 người giờ phải trực 18 pipeline CI/CD, 18 bộ log riêng biệt không tập trung, và debug một lỗi nghiệp vụ đơn giản giờ đòi hỏi lần theo dấu vết qua nhiều service không có công cụ tracing hỗ trợ. Tốc độ phát hành tính năng mới — vốn là lý do ban đầu để chuyển đổi — giảm đi chứ không tăng, vì phần lớn thời gian đội dồn vào vá lỗi vận hành phân tán.

Bài học rút ra khi Kiến trúc sư doanh nghiệp vào cuộc đánh giá lại: pattern không sai ở bản chất — microservices vẫn là lựa chọn hợp lý về dài hạn khi doanh nghiệp tăng trưởng đội ngũ và cần scale độc lập theo domain. Vấn đề là thời điểm và năng lực tổ chức chưa khớp với chi phí vận hành phân tán. Hướng khắc phục đúng là hợp nhất lại thành modular monolith có ranh giới module rõ theo domain (cart, order, inventory, payment mỗi module một package độc lập, giao tiếp nội bộ qua interface rõ ràng, không qua network) — giữ khả năng tách dần từng module thành service riêng theo chiến lược Strangler Fig khi có tín hiệu thật: một domain cụ thể cần scale độc lập, hoặc một đội đủ lớn để sở hữu riêng domain đó.

📐 Template/Artifact áp dụng ngay

Application Architecture Pattern Selection Matrix — dùng khi đánh giá pattern cho một hệ thống mới hoặc quyết định tái cấu trúc hệ thống hiện hữu. Điền cho từng tiêu chí, đối chiếu với bảng so sánh pattern ở trên trước khi chốt quyết định.

Tiêu chí đánh giá Câu hỏi cần trả lời Ghi nhận thực tế Pattern gợi ý theo tiêu chí này
Quy mô đội kỹ thuật hiện tại Có bao nhiêu kỹ sư full-time sẽ vận hành hệ thống này? < 15: Monolith có kiểm soát; 15–50: cân nhắc theo domain; > 50: Microservices khả thi
Năng lực DevOps hiện có Đội đã có CI/CD tự động, observability tập trung (log, metric, trace), chưa? Chưa có nền tảng trưởng thành → không chọn Microservices/EDA
Tốc độ thay đổi nghiệp vụ theo domain Domain nào thay đổi nhanh hơn hẳn các domain khác? Có bằng chứng (tần suất release, số ticket) không? Nếu đồng đều → giữ Monolith; nếu lệch rõ → tách domain thay đổi nhanh
Yêu cầu chịu tải khác biệt giữa các domain Domain nào cần scale độc lập vì tải đột biến (ví dụ: catalog vào mùa sale)? Có domain tải khác biệt rõ → cân nhắc tách riêng domain đó
Ranh giới dữ liệu (data ownership) Domain có sở hữu trọn dữ liệu của mình hay chia sẻ bảng/schema với domain khác? Chia sẻ schema chặt → chưa sẵn sàng tách boundary triển khai
Yêu cầu nhất quán giao dịch (transactional consistency) Luồng nghiệp vụ có cần ACID transaction xuyên domain không? Cần ACID xuyên domain → giữ trong một deployment boundary hoặc thiết kế Saga pattern kỹ trước khi tách
Chi phí vận hành phân tán chấp nhận được Ngân sách và nhân lực có đủ cho N pipeline, N bộ dashboard, N hợp đồng API? Không đủ → giảm số boundary xuống mức đội quản lý được
Quyết định cuối cùng + lý do (ADR) Pattern chọn là gì, đánh đổi nào được chấp nhận, điều kiện nào sẽ kích hoạt tái đánh giá? Ghi thành Architecture Decision Record chính thức

🎯 Góc nhìn Chief EA

Nếu tôi tách hệ thống này thành N service, đội hiện tại có đủ người để trực N pipeline lúc nửa đêm không, hay chúng ta đang thiết kế cho đội mơ ước chứ không phải đội đang có?

Ranh giới service tôi vừa vẽ đi theo domain nghiệp vụ thật, hay đi theo cấu trúc phòng ban/tầng kỹ thuật — và nếu Conway's Law đúng, cấu trúc giao tiếp đội ngũ hiện tại có khớp với ranh giới này không?

Lý do thật sự để chọn pattern này là gì — một ràng buộc nghiệp vụ đã lập luận rõ, hay vì đây là xu hướng đang được nhắc đến nhiều?

Nếu quyết định này sai trong 18 tháng tới, chi phí đảo ngược là bao nhiêu — và tôi đã thiết kế đường lui (ví dụ giữ modular monolith có thể strangler dần) hay đã tự khóa mình vào một hướng không quay lại được?

🔗 Bài viết liên quan


Bài trước: Kiến trúc dữ liệu (Data Architecture) · Bài tiếp theo: Kiến trúc hạ tầng (Infrastructure Architecture)

Kiến trúc ứng dụng (Application Architecture): Design patterns cho các hệ thống doanh nghiệp lớn