🗺️ MOC - Kiến trúc ứng dụng web
28 dot notes tách từ ebook của Nguyễn Thế Huy. Sách đi từ tư tưởng gốc (tách core code khỏi infrastructure code) → Domain Model & Repository → Read/View Model & Application Service → Kiến trúc Phân lớp → và đỉnh cao là Hexagonal Architecture (Ports & Adapters). Đọc tuần tự lần đầu, sau đó dùng từng note như tài liệu tra cứu.
Tư tưởng xuyên suốt của cả cuốn sách: Code tốt = code dễ bảo trì & mở rộng. Đạt được bằng cách tách logic nghiệp vụ (core) khỏi chi tiết kỹ thuật (infrastructure) - nguyên tắc nền là Separation of Concerns, chìa khoá kỹ thuật là phụ thuộc vào trừu tượng (Dependency Inversion Principle, Dependency Injection).
🔗 Liên thông với bộ OOP cho người đi làm cùng tác giả: các note ở đây link thẳng tới Single Responsibility Principle, Open Closed Principle, Program to Interface - Tập trung vào trừu tượng, Dependency Injection, Observer Pattern… trong vault OOP.
🧭 Lộ trình học đề xuất (6 level)
Level 0 - Tư tưởng nền: vì sao phải tách Core & Infrastructure
Hiểu động cơ của cả cuốn sách trước khi đi vào kỹ thuật.
- Separation of Concerns - sợi chỉ đỏ xuyên suốt: mỗi phần code lo một mối quan tâm
- Vì sao phải tách biệt Core và Infrastructure - mục tiêu & tầm quan trọng. Bắt đầu từ đây!
- Vấn đề khi Core và Infrastructure trộn lẫn - 5 hậu quả của “mixed code”
- Lợi ích của việc tách biệt Core và Infrastructure - 6 phần thưởng khi tách đúng
- Giải pháp tổng quan để tách biệt Core và Infrastructure - bản đồ các kỹ thuật ⭐
Level 1 - Định nghĩa: phân biệt Core vs Infrastructure
Nền tảng khái niệm. Không chắc level này thì mọi kiến trúc phía sau chỉ là học vẹt.
- Core Code là gì - 2 quy tắc + bài test “chạy trong memory?”
- Infrastructure Code là gì - code kết nối bên ngoài; abstraction KHÔNG tự biến thành core
- Ranh giới giữa Core và Infrastructure Code - vùng xám thực tế & câu hỏi vàng để định danh
Level 2 - Domain Model & Repository 🔥 (trái tim)
Hiện thực hóa core code một cách chặt chẽ, có cấu trúc.
- Domain Model - Mô hình miền nghiệp vụ - Transaction Script vs Domain Model
- Domain Entity - invariant: đủ dữ liệu & hợp lệ ngay khi tạo
- Entity vs Value Object - có danh tính vs có giá trị
- Domain Service - logic không thuộc riêng entity nào
- Domain Event -
OrderPlaced,ProductOutOfStock; pub/sub cấp domain - Repository Pattern - interface ở domain, implementation ở hạ tầng ⭐⭐
Level 3 - Đọc/Ghi & Tầng ứng dụng
Tách mô hình đọc khỏi ghi, tách use case khỏi controller.
- Read Model - mô hình chuyên để đọc; đừng dùng entity cho hiển thị
- View Model - DTO sẵn sàng cho UI/API (JSON)
- CQRS - Command Query Responsibility Segregation - tách hẳn đường đọc và đường ghi
- Application Service - điều phối use case, gọi được từ web/CLI/test
- Command Object và DTO - gom input thành 1 object (khác Command Pattern GoF)
Level 4 - Kiến trúc Phân lớp (Layered Architecture)
Tổ chức toàn bộ thành các tầng có trách nhiệm rõ ràng.
- Layered Architecture - Kiến trúc Phân lớp - 3-tier cổ điển → biến thể DDD
- Tầng Domain - trái tim, thuần PHP, không dính framework
- Tầng Application - lớp keo điều phối, gọi domain
- Tầng Infrastructure - kết nối bên ngoài, triển khai mọi interface
- Dependency Rule - Quy tắc phụ thuộc một chiều - luật xương sống: phụ thuộc chỉ đi vào trong ⭐
Level 5 - Ports and Adapters (Hexagonal Architecture) 🏆 (đỉnh cao)
Đưa việc tách biệt lên mức linh hoạt cao nhất.
- Hexagonal Architecture - Ports and Adapters - lõi ở giữa, các cổng quanh rìa
- Port - Cổng giao tiếp - input port (UI/API/CLI) & output port (DB/mail/API)
- Adapter - Bộ chuyển đổi - code cụ thể cắm vào port (khác Adapter Pattern GoF)
- Bounded Context - cô lập giữa các phân hệ; tiền đề tách microservices
🚀 Lối vào nhanh theo vấn đề (tra cứu khi gặp chuyện)
| Bạn đang gặp gì? | Đọc note |
|---|---|
| Đọc code mà không hiểu “câu chuyện nghiệp vụ” đang diễn ra | Vấn đề khi Core và Infrastructure trộn lẫn → Domain Model - Mô hình miền nghiệp vụ |
| Logic nghiệp vụ nằm rải rác trong controller | Application Service → Command Object và DTO |
| Dữ liệu sai (email lỗi, số lượng âm) lọt vào DB | Domain Entity (invariant trong constructor) |
| Đổi MySQL sang DB khác phải sửa khắp nơi | Repository Pattern → Dependency Inversion Principle |
| Không viết được unit test vì phụ thuộc DB/network | Core Code là gì → Hexagonal Architecture - Ports and Adapters |
| Model vừa đọc vừa ghi, phình to khó bảo trì | Read Model → View Model → CQRS - Command Query Responsibility Segregation |
| Không biết nên đặt code ở tầng nào | Layered Architecture - Kiến trúc Phân lớp → Dependency Rule - Quy tắc phụ thuộc một chiều |
| Muốn thêm kênh mới (API, CLI, mobile) mà không sửa logic | Hexagonal Architecture - Ports and Adapters → Port - Cổng giao tiếp |
| Đổi email sang SMS / đổi framework | Adapter - Bộ chuyển đổi |
| Muốn phản ứng thêm khi có sự kiện (gửi SMS khi đặt hàng) | Domain Event → Observer Pattern |
| Context này gọi thẳng DB của context kia | Bounded Context |
| Không biết đoạn code là core hay hạ tầng | Ranh giới giữa Core và Infrastructure Code |
🧠 5 câu thần chú rút gọn cả cuốn sách
- “Tách logic nghiệp vụ (core) khỏi chi tiết kỹ thuật (infrastructure)” - Vì sao phải tách biệt Core và Infrastructure
- “Đoạn code này có chạy độc lập, trong memory, không side effect ngoài không?” - Core Code là gì
- “Phụ thuộc chỉ đi một chiều: từ ngoài vào trong” - Dependency Rule - Quy tắc phụ thuộc một chiều
- “Core định nghĩa port (interface), infrastructure cung cấp adapter” - Hexagonal Architecture - Ports and Adapters
- “Domain là trái tim - thuần, sạch, không biết gì về framework” - Tầng Domain
🗺️ Kiến trúc ứng dụng web - Reading Map
Sơ đồ cấu trúc & thứ tự đọc ebook của Nguyễn Thế Huy. Nguyên tắc: đọc tuần tự lần đầu (phần sau xây trên phần trước), sau đó dùng từng phần như tài liệu tra cứu.
Tổng quan: thứ tự đọc 6 phần
flowchart TD Start(["📖 Bắt đầu"]) --> P0 subgraph P0["0️⃣ TƯ TƯỞNG NỀN - vì sao tách Core & Infrastructure"] direction TB A0["Separation of Concerns<br/>(mỗi phần lo một mối quan tâm)"] A1["Vì sao tách biệt<br/>(mục tiêu cả cuốn sách)"] A2["Vấn đề khi trộn lẫn<br/>(5 hậu quả)"] A3["Lợi ích khi tách biệt<br/>(6 phần thưởng)"] A4["Giải pháp tổng quan<br/>(bản đồ kỹ thuật)"] A0 --> A1 --> A2 --> A3 --> A4 end P0 --> P1 subgraph P1["1️⃣ ĐỊNH NGHĨA - Core vs Infrastructure Code"] direction TB B1["Core Code là gì<br/>(2 quy tắc, chạy trong memory)"] B2["Infrastructure Code là gì<br/>(kết nối bên ngoài)"] B3["Ranh giới thực tế<br/>(vùng xám, câu hỏi vàng)"] B1 --> B2 --> B3 end P1 --> P2 subgraph P2["2️⃣ DOMAIN MODEL & REPOSITORY 🔥 (trái tim)"] direction TB C0["Domain Model<br/>(mô hình miền nghiệp vụ)"] C1["Domain Entity + Value Object<br/>(invariant, danh tính vs giá trị)"] C2["Domain Service + Domain Event"] C3["Repository Pattern<br/>(interface ở domain, impl ở hạ tầng)"] C0 --> C1 --> C2 --> C3 end P2 --> P3 subgraph P3["3️⃣ ĐỌC/GHI & TẦNG ỨNG DỤNG"] direction TB D1["Read Model + View Model<br/>(mô hình chuyên đọc)"] D2["CQRS<br/>(tách đường đọc / đường ghi)"] D3["Application Service + Command/DTO<br/>(tách use case khỏi controller)"] D1 --> D2 --> D3 end P3 --> P4 subgraph P4["4️⃣ KIẾN TRÚC PHÂN LỚP (Layered)"] direction TB E1["Domain - Application - Infrastructure<br/>(3 tầng)"] E2["Dependency Rule<br/>(phụ thuộc chỉ đi VÀO TRONG)"] E1 --> E2 end P4 --> P5 subgraph P5["5️⃣ PORTS & ADAPTERS 🏆 (Hexagonal - đỉnh cao)"] direction TB F1["Hexagonal Architecture<br/>(lõi giữa, cổng quanh rìa)"] F2["Port (cổng) + Adapter (đầu nối)<br/>(input/output, quan hệ 1-n)"] F3["Bounded Context<br/>(cô lập giữa các phân hệ)"] F1 --> F2 --> F3 end P5 --> Done(["✅ Dùng từng phần làm tài liệu tra cứu"]) style P2 fill:#f8d7da,stroke:#c82333,stroke-width:2px style P5 fill:#fff3cd,stroke:#e0a800,stroke-width:3px style Start fill:#d4edda,stroke:#28a745 style Done fill:#d4edda,stroke:#28a745
Tư tưởng chi phối tất cả
flowchart LR T1["🧠 Core Code<br/>(logic nghiệp vụ thuần)"] --> X["Mọi mẫu thiết kế<br/>& kiến trúc trong sách"] T2["🔌 Infrastructure Code<br/>(kết nối bên ngoài)"] --> X X --> R1["Domain Model + Repository"] X --> R2["Kiến trúc Phân lớp"] X --> R3["Ports & Adapters (Hexagonal)"] K["🔑 Chìa khoá chung:<br/>phụ thuộc vào TRỪU TƯỢNG (port),<br/>không phụ thuộc CHI TIẾT (adapter)"] --> X
Chọn cách xử lý nào? (tra nhanh theo vấn đề)
flowchart TD Q(["🤔 Vấn đề của bạn?"]) --> G{"Thuộc nhóm nào?"} G -- "Logic nghiệp vụ lộn xộn" --> C1{"Tình huống?"} C1 -- "Rải rác trong controller" --> F1["Application Service + Command"] C1 -- "Dữ liệu sai lọt vào DB" --> F2["Domain Entity (invariant)"] C1 -- "Logic tản mát, thủ tục" --> F3["Domain Model"] G -- "Phụ thuộc công nghệ" --> C2{"Tình huống?"} C2 -- "Đổi DB phải sửa khắp nơi" --> F4["Repository Pattern"] C2 -- "Đổi email→SMS / đổi framework" --> F5["Adapter (Hexagonal)"] C2 -- "Không test được vì dính DB" --> F6["Ports & Adapters + Core Code"] G -- "Đọc / hiển thị dữ liệu" --> C3{"Tình huống?"} C3 -- "Model vừa đọc vừa ghi, phình to" --> F7["Read Model / View Model"] C3 -- "Cần tối ưu đọc & ghi riêng" --> F8["CQRS"] G -- "Tổ chức & tích hợp" --> C4{"Tình huống?"} C4 -- "Không biết đặt code ở tầng nào" --> F9["Layered + Dependency Rule"] C4 -- "Thêm kênh mới (API/CLI/mobile)" --> F10["Port (Hexagonal)"] C4 -- "Context này gọi thẳng DB context kia" --> F11["Bounded Context"] style Q fill:#f8d7da,stroke:#c82333 style F1 fill:#d4edda,stroke:#28a745 style F2 fill:#d4edda,stroke:#28a745 style F3 fill:#d4edda,stroke:#28a745 style F4 fill:#d4edda,stroke:#28a745 style F5 fill:#d4edda,stroke:#28a745 style F6 fill:#d4edda,stroke:#28a745 style F7 fill:#d4edda,stroke:#28a745 style F8 fill:#d4edda,stroke:#28a745 style F9 fill:#d4edda,stroke:#28a745 style F10 fill:#d4edda,stroke:#28a745 style F11 fill:#d4edda,stroke:#28a745