01 Component là gì?
Component = sự bao gói vật lý của các module (physical packaging of modules) — đơn vị đơn giản & cơ bản nhất của kiến trúc. Mỗi ngôn ngữ có cơ chế riêng: jar (Java), dll (.NET), gem (Ruby), package/namespace/library/service.
Phạm vi component — từ "cùng bộ nhớ" đến "qua mạng"
02 KTS vs Lập trình viên
Component là tầng thấp nhất mà KTS trực tiếp đụng tới. Bên trong nó là lớp/hàm — việc của tech lead & dev.
Kiến trúc sư
- Xác định, tinh chỉnh, quản trị các component.
- Chịu trách nhiệm phân vùng cấp cao (top-level partitioning).
- Một trong các việc đầu tiên trên dự án mới.
Lập trình viên
- Tạo lớp & hàm bên trong component.
- Thiết kế class là việc của tech lead/dev.
- KTS không nên micromanage từng quyết định.
Nếu KTS không bao giờ để vai khác ra quyết định hệ trọng, tổ chức sẽ cạn nguồn KTS kế cận — trao quyền cũng là một phần của thiết kế tốt.
03 Phân vùng: kỹ thuật vs domain
Quyết định nền tảng nhất khi định danh component: tổ chức theo khả năng kỹ thuật hay theo domain/luồng nghiệp vụ? Hai cách, hai bộ đánh đổi — không cách nào "đúng" tuyệt đối.
| Khía cạnh | Phân vùng kỹ thuật (technical) | Phân vùng domain |
|---|---|---|
| Tổ chức quanh | Vai trò kỹ thuật: presentation, business, persistence… | Luồng/domain: CatalogCheckout, Payment… |
| Khớp với | Layered architecture, mẫu MVC (mặc định nhiều nơi) | Domain-Driven Design, Bounded Context, microservices |
| Ưu | Dễ tìm code theo loại kỹ thuật; decoupling giữa các tầng. | Domain gọn trong một chỗ, khớp kiểu thay đổi hay xảy ra. |
| Nhược | Một domain (vd CatalogCheckout) bị trải mỏng khắp các tầng. | Code kỹ thuật cùng loại nằm rải theo nhiều domain. |
Định luật Conway. "Tổ chức thiết kế hệ thống bị ràng buộc tạo ra bản sao của cấu trúc giao tiếp của chính tổ chức." Chia đội theo kỹ thuật (Backend / DBA / UI) → kiến trúc nghiêng về phân tầng kỹ thuật. Inverse Conway Maneuver: đổi cấu trúc đội trước để thúc kiến trúc mong muốn.
RAG Hệ "Hỏi–đáp tài liệu" nên phân vùng theo domain/luồng: pipeline Python tách thành các component có mục đích rõ, PHP là một component web ở ranh giới:
flowchart LR
subgraph PHP["PHP · Web (component ranh giới)"]
W["Web/API"]
end
subgraph PY["Python · pipeline (domain components)"]
direction TB
ING["Ingestion"]:::m
RET["Retrieval"]:::m
ANS["AnswerService"]:::m
VS[("Vector Store")]:::db
ING --> VS
RET --> VS
RET --> ANS
end
W -->|"POST /ingest"| ING
W -->|"POST /query"| RET
classDef m fill:#dbeee8,stroke:#0f7d72,color:#1c1a14;
classDef db fill:#e2edf3,stroke:#2f6d93,color:#1c1a14;
04 Chu kỳ định danh component
Tìm đúng component là một trong các việc khó nhất của KTS — nên nó là vòng lặp, không phải làm một lần. Năm bước:
Xác định component ban đầu
Dựa trên kiểu phân vùng đã chọn, phác các khối cấp cao. Bộ đầu tiên hiếm khi tốt — nên mới phải lặp.
Gán yêu cầu / user story
Ánh xạ story vào component để xem khớp tới đâu: tạo mới, gộp lại, hay tách ra nếu một component ôm quá nhiều.
Phân tích vai trò & trách nhiệm
Soi roles + hành vi để tính hạt (granularity) của component khớp hành vi ứng dụng.
Phân tích đặc tính kiến trúc
Đặc tính (vd scalability) có thể buộc chia nhỏ: hai phần đều xử lý input nhưng một phần phải đỡ hàng trăm user đồng thời → tách.
Tái cấu trúc component
Phản hồi là sống còn — liên tục lặp với dev khi edge case & hiểu biết mới lộ ra.
Granularity là bài toán đánh đổi. Quá mịn (fine-grained) → giao tiếp giữa component quá nhiều. Quá thô (coarse-grained) → coupling nội bộ cao, khó test & triển khai.
05 Cách phân rã & cái bẫy
Không có cách "đúng" duy nhất — chỉ là các kỹ thuật với đánh đổi khác nhau:
Actor / Actions
Xác định tác nhân (user/system) & hành động họ làm. Tổng quát, hợp cả monolith lẫn phân tán; tốt cho quy trình nhiều thiết kế trước.
Event Storming
Giả định hệ dùng message/event; tìm các sự kiện xảy ra rồi dựng component quanh handler. Hợp microservices.
Workflow
Như event storming nhưng không bắt buộc message — mô hình hoá quanh luồng công việc & vai trò then chốt.
Anti-pattern · Bẫy Thực thể (Entity Trap). Ánh xạ component một-một với bảng cơ sở dữ liệu (CustomerManager, OrderManager…). Đó không phải kiến trúc — chỉ là một ORM trá hình. Dấu hiệu thiếu suy nghĩ về luồng thật; component sinh ra quá thô, chẳng hướng dẫn được đội code.
RAG Đừng rơi vào Entity Trap khi dựng RAG — gom theo luồng, đừng theo bảng:
# ✗ Entity Trap — mỗi bảng DB thành một "Manager" (ORM trá hình)
class DocumentManager: ... # bảng documents
class ChunkManager: ... # bảng chunks
class VectorManager: ... # bảng vectors
# ✓ Theo luồng — component ôm một workflow có mục đích
class IngestionService: ... # load → chunk → embed → lưu
class RetrievalService: ... # truy hồi theo câu hỏi
class AnswerService: ... # ghép ngữ cảnh → gọi LLM
Tách theo đặc tính — bài học GGG
Trong case study đấu giá Going, Going, Gone, KTS tách Bid Capture thành Bid Capture (người đấu giá) & Auctioneer Capture (đấu giá viên) vì hai bên cần scalability/reliability khác hẳn nhau.
RAG Cùng logic: tách Ingestion (chạy nền, theo lô, chịu tải) khỏi Retrieval (đồng bộ, độ trễ thấp) — vì đặc tính kiến trúc của chúng đối nghịch, ép chung một component là gượng.
Architecture quantum (đơn vị triển khai độc lập + kết dính chức năng cao + connascence đồng bộ) giúp chốt sớm: nếu các component cần đặc tính khác nhau → nghiêng về phân tán; giống nhau → có thể giữ monolith. Đừng ám ảnh "thiết kế đúng duy nhất" — chọn bộ đánh đổi ít tệ nhất.
06 Ghi nhớ nhanh
Component = module đóng gói vật lý — viên gạch cơ bản nhất KTS cầm nắm, từ library → service.
KTS định component, dev định class — phân vùng cấp cao là việc của KTS; đừng micromanage bên trong.
Kỹ thuật hay domain? — quyết định nền tảng; nhớ Conway's Law: kiến trúc soi gương cấu trúc đội.
Định danh component là vòng lặp — gán story, soi vai trò & đặc tính, rồi tái cấu trúc liên tục.
Tránh Entity Trap — đừng map component 1-1 với bảng DB; gom theo luồng để kết dính cao.