# Syllabus software engineering (3)

# Giai đoạn 6 - Networking & OS I/O (Ngày 267–294)

*Giai đoạn mới bổ sung.* Đây chính là nền tảng đứng sau Netty, asyncio, gRPC, nginx - bắt buộc phải hiểu trước khi đọc source các framework đó ở Giai đoạn 10.

## Nhóm 1: Giới thiệu (Ngày 267–268)

**Ngày 267: Vì sao Framework Engineer cần hiểu Networking**

*   Mục tiêu: Thấy rõ mối liên hệ giữa mảng này và các framework sẽ học sau.
    
*   Lý thuyết: Netty (Java), asyncio (Python), gRPC, nginx - tất cả đều xây trên socket + I/O multiplexing; hiểu tầng dưới giúp đọc source tầng trên dễ hơn nhiều.
    
*   Thực hành: Liệt kê 3 framework/thư viện bạn dự định đọc source sau này, ghi chú thành phần I/O nào của chúng liên quan tới mảng sắp học.
    

**Ngày 268: OSI Model & TCP/IP Model**

*   Mục tiêu: Có khung tham chiếu chung trước khi đi sâu từng tầng.
    
*   Lý thuyết: 7 tầng OSI vs 4 tầng TCP/IP thực tế, tầng nào framework engineer chạm vào nhiều nhất (Transport, Application).
    
*   Thực hành: Vẽ sơ đồ mapping OSI ↔ TCP/IP, đánh dấu tầng nào sẽ học trong 28 ngày tới.
    

## Nhóm 2: TCP/IP Fundamentals (Ngày 269–274)

**Ngày 269: IP - Địa chỉ & Routing cơ bản**

*   Mục tiêu: Hiểu cách gói tin tìm đường tới đích.
    
*   Lý thuyết: IPv4 address, subnet mask, default gateway, khái niệm routing table cơ bản.
    
*   Thực hành: Dùng `ip route`/`traceroute` (hoặc `tracert` trên Windows) xem đường đi thực tế của gói tin tới 1 địa chỉ bất kỳ.
    

**Ngày 270: TCP - 3-Way Handshake**

*   Mục tiêu: Hiểu cách 2 bên thiết lập kết nối tin cậy.
    
*   Lý thuyết: SYN → SYN-ACK → ACK, ý nghĩa của sequence number khởi tạo.
    
*   Thực hành: Dùng `tcpdump`/Wireshark bắt gói tin khi mở 1 kết nối TCP thật (VD: `curl` tới 1 website), xác định 3 gói tin handshake.
    

**Ngày 271: TCP - Reliable Delivery**

*   Mục tiêu: Hiểu cách TCP đảm bảo dữ liệu tới đích đầy đủ, đúng thứ tự.
    
*   Lý thuyết: Sequence number, ACK, retransmission khi mất gói/timeout.
    
*   Thực hành: Đọc capture Wireshark của Ngày 270, chỉ ra sequence number tăng dần theo dữ liệu gửi.
    

**Ngày 272: TCP - Flow Control**

*   Mục tiêu: Hiểu cách TCP tránh làm ngập bên nhận.
    
*   Lý thuyết: Sliding window, receive window size trong TCP header.
    
*   Thực hành: Quan sát trường "Window Size" trong Wireshark capture, giải thích vì sao nó thay đổi qua thời gian.
    

**Ngày 273: TCP - Congestion Control**

*   Mục tiêu: Hiểu cách TCP tránh làm nghẽn mạng.
    
*   Lý thuyết: Slow start, congestion avoidance, ý tưởng cơ bản (không cần đi sâu thuật toán cụ thể như Reno/Cubic).
    
*   Thực hành: Đọc 1 bài viết ngắn giải thích slow start, tóm tắt lại bằng lời của mình kèm sơ đồ tăng dần cửa sổ gửi.
    

**Ngày 274: UDP - So sánh với TCP**

*   Mục tiêu: Hiểu khi nào nên chọn UDP thay vì TCP.
    
*   Lý thuyết: UDP không đảm bảo tin cậy/thứ tự, đổi lại độ trễ thấp hơn - phù hợp cho streaming, gaming, DNS, và là nền tảng cho QUIC (sẽ gặp ở Ngày 279).
    
*   Thực hành: Lập bảng so sánh TCP vs UDP theo các tiêu chí: độ tin cậy, độ trễ, use case điển hình.
    

## Nhóm 3: HTTP Protocol Evolution (Ngày 275–280)

**Ngày 275: HTTP/1.1 cơ bản**

*   Mục tiêu: Nắm cấu trúc request/response.
    
*   Lý thuyết: Method, header, status code, request line, message body.
    
*   Thực hành: Dùng `curl -v` gọi 1 API thật, đọc raw request/response header được in ra.
    

**Ngày 276: HTTP/1.1 - Keep-Alive & Head-of-Line Blocking**

*   Mục tiêu: Hiểu giới hạn của HTTP/1.1 dẫn tới HTTP/2 ra đời.
    
*   Lý thuyết: Keep-Alive tái sử dụng connection, nhưng pipelining bị Head-of-Line blocking (request sau phải đợi request trước xử lý xong trên cùng connection).
    
*   Thực hành: Vẽ sơ đồ minh hoạ HOL blocking khi gửi nhiều request trên 1 connection HTTP/1.1.
    

**Ngày 277: HTTP/2 - Multiplexing & Binary Framing**

*   Mục tiêu: Hiểu cách HTTP/2 giải quyết HOL blocking ở tầng application.
    
*   Lý thuyết: Binary framing layer, stream, multiplexing nhiều request trên 1 connection TCP duy nhất.
    
*   Thực hành: Dùng `curl --http2 -v` gọi 1 API hỗ trợ HTTP/2, so sánh với `curl --http1.1`.
    

**Ngày 278: HTTP/2 - Header Compression & Server Push**

*   Mục tiêu: Hiểu các tối ưu bổ sung của HTTP/2.
    
*   Lý thuyết: HPACK (nén header dựa trên bảng tham chiếu), Server Push (đã bị deprecate ở nhiều trình duyệt nhưng vẫn nên biết ý tưởng), stream prioritization.
    
*   Thực hành: Đọc tài liệu HPACK, tóm tắt ý tưởng chính bằng lời của mình (không cần cài đặt).
    

**Ngày 279: HTTP/3 - QUIC**

*   Mục tiêu: Hiểu HTTP/2 vẫn còn HOL blocking ở tầng TCP, và QUIC giải quyết thế nào.
    
*   Lý thuyết: QUIC chạy trên UDP thay vì TCP, mỗi stream độc lập nên mất gói ở 1 stream không chặn stream khác.
    
*   Thực hành: Lập bảng tổng hợp tiến hoá HTTP/1.1 → HTTP/2 → HTTP/3, vấn đề nào được giải quyết ở mỗi bước.
    

**Ngày 280: Lab - Quan sát HTTP thật bằng Wireshark**

*   Mục tiêu: Củng cố lý thuyết bằng quan sát thực tế.
    
*   Lý thuyết: Ôn lại cách đọc capture: TCP handshake → TLS handshake (nếu HTTPS) → HTTP request/response.
    
*   Thực hành: Capture toàn bộ quá trình truy cập 1 website HTTPS, chỉ ra rõ ràng từng giai đoạn trong 1 luồng capture.
    

## Nhóm 4: TLS & Transport Security (Ngày 281–283)

**Ngày 281: TLS Handshake**

*   Mục tiêu: Hiểu cách 2 bên thiết lập kênh mã hoá an toàn.
    
*   Lý thuyết: ClientHello/ServerHello, trao đổi khoá (asymmetric) để thiết lập session key (symmetric), vì sao dùng kết hợp cả 2 loại mã hoá.
    
*   Thực hành: Vẽ sơ đồ tuần tự đầy đủ TLS 1.2 handshake (đơn giản hoá).
    

**Ngày 282: Certificate & Chain of Trust**

*   Mục tiêu: Hiểu cách trình duyệt xác minh server đáng tin cậy.
    
*   Lý thuyết: X.509 certificate, Certificate Authority (CA), chain of trust từ root CA tới leaf certificate.
    
*   Thực hành: Dùng trình duyệt xem certificate của 1 website thật, xác định chain of trust của nó.
    

**Ngày 283: Lab - Quan sát TLS Handshake bằng OpenSSL**

*   Mục tiêu: Thấy handshake thật, không chỉ qua sơ đồ.
    
*   Lý thuyết: Ôn lại các bước handshake đã học Ngày 281.
    
*   Thực hành: Chạy `openssl s_client -connect <host>:443 -showcerts`, đọc log handshake và certificate trả về.
    

## Nhóm 5: Socket Programming (Ngày 284–287)

**Ngày 284: Socket API cơ bản**

*   Mục tiêu: Hiểu bộ syscall nền tảng cho mọi thư viện network cấp cao.
    
*   Lý thuyết: `socket()`, `bind()`, `listen()`, `accept()`, `connect()` - vòng đời 1 kết nối TCP ở tầng OS.
    
*   Thực hành: Vẽ sơ đồ tuần tự các syscall phía server và phía client cho 1 kết nối TCP.
    

**Ngày 285: Lab - TCP Echo Server/Client (C++)**

*   Mục tiêu: Viết networking code ở tầng thấp nhất bằng raw socket.
    
*   Lý thuyết: Ôn lại các syscall Ngày 284, áp dụng bằng POSIX socket API.
    
*   Thực hành: Viết TCP echo server và client bằng C++ dùng socket API trực tiếp (không dùng thư viện cao cấp).
    

**Ngày 286: Lab - TCP Echo Server/Client (Python)**

*   Mục tiêu: So sánh trải nghiệm với ngôn ngữ có abstraction cao hơn.
    
*   Lý thuyết: `socket` module Python bọc lại cùng syscall nhưng API thân thiện hơn.
    
*   Thực hành: Viết lại TCP echo server/client bằng Python `socket` module, so sánh độ dài code với bản C++.
    

**Ngày 287: Blocking vs Non-blocking Socket**

*   Mục tiêu: Hiểu sự khác biệt sẽ dẫn tới nhu cầu I/O multiplexing (nhóm tiếp theo).
    
*   Lý thuyết: Socket blocking mặc định - `accept()`/`recv()` treo thread cho tới khi có dữ liệu; non-blocking trả về ngay kể cả khi chưa có gì.
    
*   Thực hành: Sửa echo server Ngày 285/286 sang non-blocking socket, quan sát hành vi khi không có client kết nối.
    

## Nhóm 6: OS I/O Models (Ngày 288–292)

**Ngày 288: Tổng quan các mô hình I/O**

*   Mục tiêu: Có bức tranh đầy đủ trước khi học từng cơ chế cụ thể.
    
*   Lý thuyết: Theo phân loại kinh điển (W. Richard Stevens): Blocking I/O, Non-blocking I/O, I/O Multiplexing, Signal-driven I/O, Asynchronous I/O.
    
*   Thực hành: Lập bảng so sánh 5 mô hình theo tiêu chí: số thread cần thiết, độ phức tạp code, khả năng scale.
    

**Ngày 289: select/poll**

*   Mục tiêu: Hiểu cơ chế I/O multiplexing đầu tiên, nền tảng để hiểu vì sao epoll ra đời.
    
*   Lý thuyết: `select()`/`poll()` cho phép 1 thread theo dõi nhiều socket cùng lúc, nhưng có giới hạn hiệu năng khi số lượng socket lớn (duyệt tuyến tính).
    
*   Thực hành: Viết echo server dùng `select()` xử lý nhiều client trên 1 thread duy nhất.
    

**Ngày 290: epoll (Linux)**

*   Mục tiêu: Hiểu vì sao epoll hiệu quả hơn select/poll ở quy mô lớn.
    
*   Lý thuyết: `epoll_create`/`epoll_ctl`/`epoll_wait`, cơ chế event-driven (kernel chỉ trả về socket có sự kiện, không cần duyệt hết) - độ phức tạp O(1) thay vì O(n).
    
*   Thực hành: Viết lại echo server Ngày 289 bằng `epoll`, benchmark với vài nghìn connection giả lập, so sánh với bản `select`.
    

**Ngày 291: kqueue & IOCP**

*   Mục tiêu: Biết các cơ chế tương đương epoll trên hệ điều hành khác.
    
*   Lý thuyết: `kqueue` (BSD/macOS) tương tự epoll; IOCP (Windows) là mô hình hoàn toàn khác - thực sự async (completion-based) thay vì readiness-based như epoll.
    
*   Thực hành: Lập bảng so sánh epoll (readiness-based) vs IOCP (completion-based) - khác biệt về mô hình lập trình.
    

**Ngày 292: Liên hệ thực tế - Netty/asyncio/nginx**

*   Mục tiêu: Kết nối kiến thức vừa học với framework sẽ đọc source ở Giai đoạn 10-11.
    
*   Lý thuyết: nginx dùng event loop trên epoll để phục vụ hàng chục nghìn connection với ít worker process; Netty dùng epoll qua JNI (`EpollEventLoopGroup`); asyncio Python dùng `selectors` module (tự chọn epoll/kqueue/select tuỳ OS).
    
*   Thực hành: Đọc tài liệu kiến trúc event loop của nginx (worker process + epoll), tóm tắt lại bằng lời của mình.
    

## Nhóm 7: Lab tổng hợp & Review (Ngày 293–294)

**Ngày 293: Lab - Mini Event Loop dùng epoll**

*   Mục tiêu: Tự tay xây dựng phiên bản đơn giản của thứ đứng sau asyncio/Netty.
    
*   Lý thuyết: Ôn lại vòng lặp: `epoll_wait` → lấy danh sách socket sẵn sàng → gọi callback tương ứng.
    
*   Thực hành: Viết 1 mini event loop bằng C++ (`epoll`) hoặc Python (`selectors` module) hỗ trợ đăng ký callback cho từng socket, chạy được nhiều echo connection đồng thời trên 1 thread.
    

**Ngày 294: Review & Retrospective Giai đoạn 6**

*   Mục tiêu: Tổng kết toàn bộ Networking & OS I/O.
    
*   Lý thuyết: Nhìn lại toàn bộ chuỗi: TCP/IP → HTTP evolution → TLS → Socket → I/O Multiplexing, liên hệ lại với Concurrency (Giai đoạn 5) - vì sao async I/O và concurrency luôn đi cùng nhau trong thiết kế framework.
    
*   Thực hành: Viết báo cáo retrospective, vẽ lại 1 sơ đồ tổng thể toàn bộ pipeline từ khi client gửi request TCP/TLS/HTTP tới khi server (dùng epoll) xử lý xong.
    

* * *

# Giai đoạn 7 - Domain Driven Design (Ngày 295–350)

Tài liệu chính: *Domain-Driven Design* (Eric Evans) và *Implementing Domain-Driven Design* (Vaughn Vernon).

## Nhóm 1: Giới thiệu (Ngày 295–296)

**Ngày 295: Vì sao cần DDD**

*   Mục tiêu: Hiểu vấn đề DDD giải quyết trước khi học thuật ngữ.
    
*   Lý thuyết: Khoảng cách giữa mô hình code và mô hình nghiệp vụ thực tế càng lớn thì phần mềm càng khó bảo trì - DDD là tập kỹ thuật thu hẹp khoảng cách đó.
    
*   Thực hành: Nhớ lại 1 hệ thống bạn từng làm việc có code không phản ánh đúng nghiệp vụ, ghi chú lại vấn đề gặp phải.
    

**Ngày 296: Strategic vs Tactical Design**

*   Mục tiêu: Có bức tranh tổng thể trước khi đi sâu.
    
*   Lý thuyết: Strategic Design (vẽ ranh giới lớn: domain, bounded context) diễn ra trước và quan trọng hơn Tactical Design (pattern code cụ thể: entity, value object...).
    
*   Thực hành: Đọc lại Mini Web Framework hoặc E-commerce project cũ, thử phác thảo domain/subdomain nó thuộc về (chưa cần chính xác).
    

## Nhóm 2: Strategic Design (Ngày 297–308)

**Ngày 297: Domain**

*   Mục tiêu: Định nghĩa domain là gì trong bối cảnh phần mềm nghiệp vụ.
    
*   Lý thuyết: Domain = lĩnh vực vấn đề mà phần mềm phục vụ (VD: thương mại điện tử, ngân hàng, logistics).
    
*   Thực hành: Viết mô tả ngắn (3-5 câu) về domain của 1 hệ thống bạn quen thuộc, không dùng thuật ngữ kỹ thuật.
    

**Ngày 298: Subdomain**

*   Mục tiêu: Phân loại các phần trong domain theo mức độ quan trọng chiến lược.
    
*   Lý thuyết: Core Subdomain (lợi thế cạnh tranh, cần đầu tư nhiều nhất), Supporting Subdomain (cần thiết nhưng không phải lợi thế), Generic Subdomain (dùng giải pháp có sẵn, VD: authentication).
    
*   Thực hành: Phân loại các subdomain của 1 hệ thống E-commerce (Catalog, Order, Payment, Shipping, Auth...) vào 3 nhóm trên.
    

**Ngày 299: Bounded Context**

*   Mục tiêu: Hiểu khái niệm trung tâm nhất của Strategic Design.
    
*   Lý thuyết: Bounded Context là ranh giới mà trong đó 1 mô hình (model) và ngôn ngữ có ý nghĩa nhất quán - cùng 1 từ (VD: "Product") có thể mang nghĩa khác nhau ở context khác nhau.
    
*   Thực hành: Chỉ ra ví dụ 1 từ (VD: "Customer") có ý nghĩa/thuộc tính khác nhau giữa context Sales và context Support trong cùng 1 công ty.
    

**Ngày 300: Ubiquitous Language**

*   Mục tiêu: Hiểu vì sao ngôn ngữ chung giữa dev và domain expert lại quan trọng tới mức trở thành pattern riêng.
    
*   Lý thuyết: Ubiquitous Language là ngôn ngữ dùng chung, nhất quán, xuất hiện cả trong hội thoại, tài liệu, VÀ trong tên class/method của code - không dịch qua lại giữa "ngôn ngữ nghiệp vụ" và "ngôn ngữ kỹ thuật".
    
*   Thực hành: Lấy 1 đoạn mô tả nghiệp vụ bằng lời thường, chuyển thành danh sách thuật ngữ Ubiquitous Language sẽ dùng làm tên class trong code.
    

**Ngày 301: Lab - Xác định Bounded Context cho E-commerce**

*   Mục tiêu: Thực hành Strategic Design trên hệ thống đã quen thuộc.
    
*   Lý thuyết: Ôn lại tiêu chí tách bounded context (nhóm theo team sở hữu, theo ngôn ngữ nghiệp vụ khác biệt, theo tốc độ thay đổi khác nhau).
    
*   Thực hành: Vẽ sơ đồ các Bounded Context cho hệ thống E-commerce đã refactor ở Giai đoạn 2 (Order, Product, Payment, Customer), chỉ rõ ranh giới và lý do tách.
    

**Ngày 302: Context Mapping - Giới thiệu**

*   Mục tiêu: Hiểu các bounded context không tồn tại độc lập, cần quản lý quan hệ giữa chúng.
    
*   Lý thuyết: Context Map là sơ đồ thể hiện quan hệ và cách giao tiếp giữa các bounded context.
    
*   Thực hành: Vẽ Context Map sơ bộ cho các Bounded Context đã xác định ở Ngày 301, chỉ ra context nào phụ thuộc context nào.
    

**Ngày 303: Context Mapping - Shared Kernel & Customer-Supplier**

*   Mục tiêu: Học 2 pattern quan hệ đầu tiên.
    
*   Lý thuyết: Shared Kernel (2 team chia sẻ 1 phần model chung, phối hợp chặt); Customer-Supplier (1 context là "khách hàng" phụ thuộc vào context "nhà cung cấp", có ưu tiên trong roadmap).
    
*   Thực hành: Xác định quan hệ giữa Order context và Payment context theo 1 trong 2 pattern trên, giải thích lựa chọn.
    

**Ngày 304: Context Mapping - Anticorruption Layer & Open Host Service**

*   Mục tiêu: Học 2 pattern quan trọng nhất cho việc tích hợp hệ thống cũ/bên thứ 3.
    
*   Lý thuyết: Anticorruption Layer (ACL) - lớp dịch để model bên ngoài không "ăn mòn" model nội bộ; Open Host Service - cung cấp API công khai chuẩn hoá cho nhiều consumer.
    
*   Thực hành: Thiết kế 1 ACL giả định giữa hệ thống nội bộ và 1 payment gateway bên thứ 3 (VD: Stripe) - model nội bộ không lộ trực tiếp cấu trúc dữ liệu của Stripe.
    

**Ngày 305: Context Mapping - Partnership & Conformist**

*   Mục tiêu: Học 2 pattern còn lại.
    
*   Lý thuyết: Partnership (2 team hợp tác chặt, cùng thành công/thất bại); Conformist (1 team chấp nhận hoàn toàn model của team khác, không có quyền thương lượng - thường xảy ra khi tích hợp hệ thống lớn bên ngoài).
    
*   Thực hành: Với Context Map đã vẽ Ngày 302, gán 1 trong 5 pattern quan hệ (Shared Kernel/Customer-Supplier/ACL/Partnership/Conformist) cho từng cặp context.
    

**Ngày 306: Event Storming - Giới thiệu**

*   Mục tiêu: Học kỹ thuật workshop thực tế để khám phá domain cùng domain expert.
    
*   Lý thuyết: Event Storming dùng sticky note để cùng nhau vẽ ra domain event, command, aggregate theo dòng thời gian nghiệp vụ.
    
*   Thực hành: Đọc tài liệu về Event Storming, tóm tắt quy trình 5 bước cơ bản (domain event → command → aggregate → policy → read model).
    

**Ngày 307: Lab - Event Storming cho Payment**

*   Mục tiêu: Thực hành Event Storming, chuẩn bị trực tiếp cho project cuối giai đoạn.
    
*   Lý thuyết: Ôn lại domain event nên đặt tên ở thì quá khứ (VD: "PaymentInitiated", không phải "InitiatePayment").
    
*   Thực hành: Tự làm Event Storming (dùng công cụ online hoặc giấy note) cho domain thanh toán, liệt kê toàn bộ domain event từ lúc khách hàng bắt đầu thanh toán tới lúc hoàn tất.
    

**Ngày 308: Review Strategic Design**

*   Mục tiêu: Củng cố toàn bộ nhóm Strategic Design.
    
*   Lý thuyết: Tổng hợp lại: Domain → Subdomain → Bounded Context → Context Mapping → Event Storming, đây là quy trình thường làm trước khi viết dòng code đầu tiên.
    
*   Thực hành: Viết lại toàn bộ artifact đã tạo (subdomain classification, context map, event storming board) thành 1 tài liệu thiết kế mạch lạc.
    

## Nhóm 3: Tactical Design (Ngày 309–332)

**Ngày 309: Entity - Concept**

*   Mục tiêu: Hiểu khái niệm object có identity xuyên suốt vòng đời.
    
*   Lý thuyết: Entity được phân biệt bởi identity (ID), không phải bởi giá trị thuộc tính - 2 entity có cùng thuộc tính vẫn khác nhau nếu ID khác nhau.
    
*   Thực hành: Xác định trong domain Order (đã học từ Giai đoạn 1-2), object nào nên là Entity và tại sao.
    

**Ngày 310: Entity - Lab**

*   Mục tiêu: Thực hành implement Entity đúng chuẩn DDD.
    
*   Lý thuyết: Ôn lại Entity nên tự bảo vệ invariant của chính nó (liên hệ Encapsulation đã học Giai đoạn 1).
    
*   Thực hành: Viết class `Order` (Entity) với ID, đảm bảo mọi thay đổi state đi qua method có validate, không expose setter trần trụi.
    

**Ngày 311: Entity - Identity vs Equality**

*   Mục tiêu: Hiểu cách implement `equals()` đúng cho Entity.
    
*   Lý thuyết: Entity so sánh bằng ID, không so sánh bằng toàn bộ thuộc tính (khác Value Object sẽ học tiếp).
    
*   Thực hành: Override `equals()`/`hashCode()` cho `Order` chỉ dựa trên ID, viết test verify 2 object cùng ID nhưng khác thuộc tính vẫn được coi là "bằng nhau".
    

**Ngày 312: Value Object - Concept**

*   Mục tiêu: Ôn và hình thức hoá khái niệm đã chạm ở Giai đoạn 1 (Lab Money).
    
*   Lý thuyết: Value Object không có identity, so sánh bằng giá trị, bất biến (immutable) - thay thế thay vì sửa đổi.
    
*   Thực hành: Liệt kê 5 khái niệm trong domain Order nên là Value Object thay vì Entity (VD: `Address`, `Money`, `Email`).
    

**Ngày 313: Value Object - Lab**

*   Mục tiêu: Thực hành implement Value Object đúng chuẩn.
    
*   Lý thuyết: Ôn lại immutability - mọi "thay đổi" thực chất là tạo object mới.
    
*   Thực hành: Viết class `Address` immutable, method `withCity(newCity)` trả về `Address` mới thay vì sửa object hiện tại.
    

**Ngày 314: Value Object - Lợi ích của Immutability**

*   Mục tiêu: Hiểu tại sao immutability quan trọng, đặc biệt trong hệ thống concurrent.
    
*   Lý thuyết: Object bất biến an toàn chia sẻ giữa nhiều thread mà không cần đồng bộ hoá - liên hệ lại Giai đoạn 5 (Concurrency).
    
*   Thực hành: Viết ví dụ minh hoạ: 1 Value Object bất biến được nhiều thread đọc cùng lúc không cần lock, so với Entity mutable cần đồng bộ hoá.
    

**Ngày 315: Aggregate - Concept**

*   Mục tiêu: Hiểu khái niệm phức tạp và quan trọng nhất của Tactical Design.
    
*   Lý thuyết: Aggregate là 1 cụm Entity/Value Object được coi như 1 đơn vị nhất quán dữ liệu (consistency boundary) - mọi thay đổi trong aggregate phải giữ invariant đúng ngay lập tức.
    
*   Thực hành: Xác định aggregate boundary cho domain Order: `Order` (aggregate root) chứa nhiều `OrderLine` - vẽ sơ đồ ranh giới.
    

**Ngày 316: Aggregate Root**

*   Mục tiêu: Hiểu quy tắc truy cập vào aggregate.
    
*   Lý thuyết: Chỉ Aggregate Root được truy cập trực tiếp từ bên ngoài; object bên trong aggregate chỉ truy cập được qua root - đảm bảo invariant không bị vi phạm từ bên ngoài.
    
*   Thực hành: Viết `Order` (root) với method `addLine()`/`removeLine()`, không cho phép code bên ngoài sửa `OrderLine` trực tiếp.
    

**Ngày 317: Aggregate - Lab & Kích thước Aggregate**

*   Mục tiêu: Học cách xác định ranh giới aggregate hợp lý (không quá to, không quá nhỏ).
    
*   Lý thuyết: Aggregate quá lớn gây tranh chấp khoá (lock contention) khi nhiều transaction cùng sửa; quá nhỏ thì khó đảm bảo invariant xuyên nhiều object - nguyên tắc "1 transaction chỉ nên sửa 1 aggregate".
    
*   Thực hành: Đánh giá lại thiết kế `Order` Ngày 315-316, cân nhắc có nên tách `Payment` ra khỏi aggregate `Order` hay không, giải thích lý do.
    

**Ngày 318: Repository - Concept**

*   Mục tiêu: Hiểu cách trừu tượng hoá việc lưu trữ/truy xuất aggregate.
    
*   Lý thuyết: Repository cung cấp interface giống collection trong bộ nhớ để làm việc với aggregate, che giấu chi tiết persistence (database, file, gọi API...).
    
*   Thực hành: Thiết kế interface `OrderRepository` với `save(order)`/`findById(id)`, không lộ chi tiết SQL/ORM ra ngoài.
    

**Ngày 319: Repository - Lab**

*   Mục tiêu: Thực hành implement Repository, áp dụng lại Storage abstraction đã học Giai đoạn 1.
    
*   Lý thuyết: Ôn lại Repository nên trả về/nhận vào aggregate hoàn chỉnh, không phải DTO rời rạc.
    
*   Thực hành: Cài đặt `InMemoryOrderRepository` implement `OrderRepository`, viết test verify save/findById hoạt động đúng.
    

**Ngày 320: Repository vs DAO**

*   Mục tiêu: Phân biệt 2 khái niệm hay bị dùng lẫn.
    
*   Lý thuyết: DAO (Data Access Object) thường map trực tiếp 1-1 với bảng database; Repository làm việc ở mức aggregate (có thể gộp nhiều bảng), tập trung vào ngôn ngữ domain hơn là ngôn ngữ database.
    
*   Thực hành: Lập bảng so sánh `OrderRepository` (Ngày 318) với 1 `OrderDAO` giả định thao tác trực tiếp trên bảng `orders`/`order_lines`.
    

**Ngày 321: Domain Service - Concept**

*   Mục tiêu: Hiểu khi nào logic không thuộc về 1 Entity/Value Object cụ thể nào.
    
*   Lý thuyết: Domain Service chứa logic nghiệp vụ quan trọng nhưng tự nhiên không "thuộc về" 1 object nào - thường liên quan tới nhiều aggregate (VD: chuyển tiền giữa 2 account).
    
*   Thực hành: Xác định 1 logic trong domain Order/Payment không thể đặt gọn vào 1 Entity/Value Object nào, giải thích tại sao nó nên là Domain Service.
    

**Ngày 322: Domain Service - Phân biệt với Application Service**

*   Mục tiêu: Tránh nhầm lẫn phổ biến khi mới học DDD.
    
*   Lý thuyết: Domain Service chứa logic nghiệp vụ thuần (business rule); Application Service điều phối use case (transaction, gọi repository, gọi domain service) nhưng không chứa business rule.
    
*   Thực hành: Lập bảng phân loại: cho 5 đoạn logic ví dụ, xác định thuộc Domain Service hay Application Service.
    

**Ngày 323: Domain Service - Lab**

*   Mục tiêu: Thực hành implement.
    
*   Lý thuyết: Ôn lại Domain Service nên là stateless, nhận aggregate làm tham số thay vì tự query.
    
*   Thực hành: Viết `TransferService` (Domain Service) xử lý logic chuyển tiền giữa 2 `Account`, đảm bảo cả 2 account cùng cập nhật nhất quán.
    

**Ngày 324: Domain Event - Concept**

*   Mục tiêu: Hình thức hoá khái niệm đã gặp ở Event Storming (Ngày 306-307).
    
*   Lý thuyết: Domain Event ghi lại "điều gì đó quan trọng đã xảy ra" trong domain, đặt tên ở thì quá khứ, bất biến.
    
*   Thực hành: Định nghĩa class `PaymentInitiated`, `PaymentCompleted`, `PaymentFailed` - mỗi event chứa đủ dữ liệu ngữ cảnh cần thiết.
    

**Ngày 325: Domain Event - Lab**

*   Mục tiêu: Thực hành publish event từ aggregate.
    
*   Lý thuyết: Aggregate ghi lại danh sách event xảy ra trong 1 thao tác, Application Service chịu trách nhiệm publish sau khi transaction thành công.
    
*   Thực hành: Sửa `Order`/`Payment` aggregate để raise domain event khi state thay đổi, viết 1 event handler đơn giản in log khi nhận event.
    

**Ngày 326: Domain Event vs Integration Event**

*   Mục tiêu: Phân biệt event nội bộ 1 bounded context với event dùng để giao tiếp giữa các context.
    
*   Lý thuyết: Domain Event dùng nội bộ trong bounded context; Integration Event là phiên bản được "dịch" ra để publish cho context khác (thường qua message broker - sẽ gặp lại ở Giai đoạn Distributed Systems).
    
*   Thực hành: Thiết kế `PaymentCompletedIntegrationEvent` (dữ liệu tối giản, ổn định về schema) khác với `PaymentCompleted` domain event nội bộ (có thể chứa nhiều chi tiết hơn).
    

**Ngày 327: Specification - Concept**

*   Mục tiêu: Học pattern ít phổ biến nhưng hữu ích để đóng gói business rule dạng điều kiện.
    
*   Lý thuyết: Specification đóng gói 1 quy tắc kiểm tra (`isSatisfiedBy(candidate)`) thành object, có thể tái sử dụng và kết hợp.
    
*   Thực hành: Thiết kế `Specification<Order>` cho quy tắc "đơn hàng đủ điều kiện miễn phí ship" (VD: tổng tiền > ngưỡng).
    

**Ngày 328: Specification - Lab, Kết hợp nhiều Specification**

*   Mục tiêu: Thực hành combine specification bằng and/or/not.
    
*   Lý thuyết: Ôn lại Composite pattern (đã học Giai đoạn 3) chính là cách implement `and()`/`or()`/`not()` cho Specification.
    
*   Thực hành: Cài đặt `AndSpecification`/`OrSpecification`, kết hợp 2 specification (VD: "đủ điều kiện free ship" AND "là khách VIP") thành 1 rule phức hợp.
    

**Ngày 329: Specification - Dùng cho Query**

*   Mục tiêu: Thấy ứng dụng thứ 2 của Specification ngoài validate.
    
*   Lý thuyết: Specification cũng có thể dịch thành query điều kiện cho Repository (VD: tìm tất cả Order thoả specification).
    
*   Thực hành: Viết `OrderRepository.findSatisfying(specification)` áp dụng specification làm điều kiện lọc (bản in-memory, chưa cần dịch sang SQL).
    

**Ngày 330: Factory - Concept**

*   Mục tiêu: Ôn lại Factory Method/Abstract Factory (Giai đoạn 3) trong bối cảnh DDD.
    
*   Lý thuyết: Factory trong DDD chịu trách nhiệm tạo aggregate phức tạp đảm bảo invariant ngay từ lúc khởi tạo - không để aggregate tồn tại ở trạng thái không hợp lệ dù chỉ 1 khoảnh khắc.
    
*   Thực hành: Xác định 1 aggregate trong domain Order/Payment có logic khởi tạo phức tạp (nhiều bước validate) cần Factory riêng thay vì constructor trần.
    

**Ngày 331: Factory - Lab**

*   Mục tiêu: Thực hành implement Factory cho aggregate.
    
*   Lý thuyết: Ôn lại Factory nên là nơi duy nhất được phép tạo aggregate từ đầu (constructor có thể để package-private/internal).
    
*   Thực hành: Viết `OrderFactory.createFromCart(cart)` đảm bảo `Order` được tạo ra luôn hợp lệ (đủ line, tổng tiền đúng) ngay từ đầu.
    

**Ngày 332: Review Tactical Design**

*   Mục tiêu: Tổng hợp toàn bộ 8 building block: Entity, Value Object, Aggregate, Repository, Domain Service, Domain Event, Specification, Factory.
    
*   Lý thuyết: Vẽ sơ đồ quan hệ giữa 8 khái niệm - cái nào chứa cái nào, cái nào tạo ra cái nào.
    
*   Thực hành: Tự quiz: cho 8 tình huống thiết kế, xác định đúng building block nên dùng cho từng tình huống.
    

## Nhóm 4: Project - Payment Platform (Ngày 333–350)

**Ngày 333: Event Storming cho Payment Platform**

*   Mục tiêu: Bắt đầu project bằng đúng quy trình đã học (Strategic Design trước, code sau).
    
*   Lý thuyết: Ôn lại kỹ thuật Event Storming Ngày 306-307.
    
*   Thực hành: Làm Event Storming đầy đủ cho toàn bộ Payment Platform (không chỉ 1 flow), xác định toàn bộ domain event từ khởi tạo giao dịch tới đối soát (reconciliation).
    

**Ngày 334: Xác định Bounded Context**

*   Mục tiêu: Vẽ ranh giới hệ thống trước khi thiết kế chi tiết.
    
*   Lý thuyết: Ôn lại tiêu chí tách bounded context.
    
*   Thực hành: Xác định các Bounded Context cho Payment Platform (VD: Payment Processing, Ledger/Accounting, Fraud Detection, Notification), vẽ Context Map.
    

**Ngày 335: Ubiquitous Language cho Payment**

*   Mục tiêu: Thống nhất thuật ngữ trước khi viết code.
    
*   Lý thuyết: Ôn lại Ngày 300.
    
*   Thực hành: Lập từ điển thuật ngữ (glossary) cho domain Payment: `Transaction`, `Ledger Entry`, `Settlement`, `Chargeback`... định nghĩa rõ ràng từng từ.
    

**Ngày 336: Thiết kế Aggregate - PaymentTransaction**

*   Mục tiêu: Xác định aggregate trung tâm của hệ thống.
    
*   Lý thuyết: Ôn lại nguyên tắc xác định aggregate boundary (Ngày 315-317).
    
*   Thực hành: Thiết kế `PaymentTransaction` aggregate - xác định nó chứa gì, invariant nào cần bảo vệ (VD: tổng tiền không đổi sau khi khởi tạo).
    

**Ngày 337: Thiết kế Value Object - Money, Currency, PaymentMethod**

*   Mục tiêu: Xác định các Value Object cốt lõi.
    
*   Lý thuyết: Ôn lại Money value object đã làm ở Giai đoạn 1, mở rộng thêm.
    
*   Thực hành: Thiết kế `Money` (amount + currency, immutable, không cho cộng khác currency), `PaymentMethod` (value object mô tả phương thức, không phải aggregate riêng).
    

**Ngày 338: Thiết kế Entity - Account, Ledger Entry**

*   Mục tiêu: Xác định các Entity trong domain Ledger.
    
*   Lý thuyết: Ôn lại tiêu chí Entity cần identity xuyên vòng đời (Ngày 309).
    
*   Thực hành: Thiết kế `Account` (Entity) và `LedgerEntry` (Entity, ghi nhận từng dòng ghi sổ - immutable sau khi tạo, tương tự append-only log).
    

**Ngày 339: Thiết kế Domain Service - TransferService**

*   Mục tiêu: Xác định logic không thuộc về 1 aggregate.
    
*   Lý thuyết: Ôn lại Ngày 321-323, `TransferService` đã thiết kế trước đó chính là ví dụ áp dụng trực tiếp vào đây.
    
*   Thực hành: Hoàn thiện thiết kế `TransferService` xử lý việc ghi nhận đồng thời debit/credit giữa 2 `Account` trong 1 giao dịch.
    

**Ngày 340: Thiết kế Domain Event**

*   Mục tiêu: Định nghĩa toàn bộ event quan trọng của hệ thống.
    
*   Lý thuyết: Ôn lại Ngày 324-326.
    
*   Thực hành: Định nghĩa `PaymentInitiated`/`PaymentAuthorized`/`PaymentCompleted`/`PaymentFailed`/`PaymentRefunded`, xác định event nào là domain event nội bộ, event nào cần trở thành integration event.
    

**Ngày 341: Thiết kế Repository**

*   Mục tiêu: Xác định cách persist các aggregate đã thiết kế.
    
*   Lý thuyết: Ôn lại Ngày 318-320.
    
*   Thực hành: Thiết kế interface `PaymentTransactionRepository`/`AccountRepository`.
    

**Ngày 342: Thiết kế Factory**

*   Mục tiêu: Đảm bảo `PaymentTransaction` luôn được tạo ra hợp lệ.
    
*   Lý thuyết: Ôn lại Ngày 330-331.
    
*   Thực hành: Thiết kế `PaymentTransactionFactory.createFromRequest(request)`, xác định các bước validate cần có trước khi transaction được tạo.
    

**Ngày 343: Implement Aggregate & Invariant Validation**

*   Mục tiêu: Bắt đầu code thật sau khi đã thiết kế đầy đủ.
    
*   Lý thuyết: Ôn lại aggregate tự bảo vệ invariant, không dựa vào validate bên ngoài.
    
*   Thực hành: Cài đặt đầy đủ `PaymentTransaction`, `Account`, `Money` với toàn bộ invariant đã xác định.
    

**Ngày 344: Implement Domain Event Publishing**

*   Mục tiêu: Cài đặt cơ chế raise và publish event.
    
*   Lý thuyết: Ôn lại Application Service raise event sau khi transaction persist thành công (tránh publish event cho thay đổi chưa commit).
    
*   Thực hành: Cài đặt cơ chế thu thập event trong aggregate, Application Service publish sau khi `repository.save()` thành công.
    

**Ngày 345: Implement Repository**

*   Mục tiêu: Có persistence layer hoạt động thật.
    
*   Lý thuyết: Ôn lại Repository interface đã thiết kế Ngày 341, có thể dùng in-memory trước, thay bằng database thật ở Giai đoạn Database Internals sau.
    
*   Thực hành: Cài đặt `InMemoryPaymentTransactionRepository`/`InMemoryAccountRepository`.
    

**Ngày 346: Implement Anticorruption Layer**

*   Mục tiêu: Tích hợp với payment gateway bên ngoài mà không làm ô nhiễm domain model.
    
*   Lý thuyết: Ôn lại ACL đã học Ngày 304.
    
*   Thực hành: Thiết kế và cài đặt `PaymentGatewayAdapter` (kết hợp Adapter pattern đã học Giai đoạn 3) dịch response từ payment gateway giả định sang domain event nội bộ.
    

**Ngày 347: Test cho Domain Layer**

*   Mục tiêu: Đảm bảo domain logic đúng, độc lập với infrastructure.
    
*   Lý thuyết: Ôn lại Testing Strategy Playbook đã viết ở Giai đoạn 4 - domain logic nên có unit test thuần, không cần database/network thật.
    
*   Thực hành: Viết bộ unit test cho `PaymentTransaction`, `TransferService`, `Money` - verify toàn bộ invariant và business rule.
    

**Ngày 348: Tích hợp toàn bộ - End-to-End Flow**

*   Mục tiêu: Verify toàn bộ hệ thống hoạt động nhất quán.
    
*   Lý thuyết: Ôn lại luồng: Application Service → Factory tạo aggregate → Domain Service xử lý → Repository lưu → publish event.
    
*   Thực hành: Viết integration test cho flow đầy đủ: khởi tạo thanh toán → xác thực → hoàn tất → ghi sổ → publish event thành công.
    

**Ngày 349: Review Kiến trúc**

*   Mục tiêu: Đối chiếu implementation thực tế với lý thuyết DDD đã học.
    
*   Lý thuyết: Ôn lại toàn bộ 8 building block Tactical Design.
    
*   Thực hành: Viết review checklist: từng building block đã dùng đúng nguyên tắc chưa (VD: aggregate có đúng kích thước không, Value Object có thực sự immutable không).
    

**Ngày 350: Retrospective Giai đoạn 7**

*   Mục tiêu: Tổng kết toàn bộ Giai đoạn DDD.
    
*   Lý thuyết: Nhìn lại hành trình Strategic Design → Tactical Design → Payment Platform, DDD không phải là "dùng nhiều pattern" mà là quy trình tư duy bắt đầu từ domain thực tế.
    
*   Thực hành: Viết báo cáo retrospective, chỉ ra phần nào của DDD khó áp dụng nhất, phần nào bạn sẽ ưu tiên áp dụng ngay vào công việc thực tế.
    

* * *

# Giai đoạn 8 - Architecture Patterns (Ngày 351–427)

Tài liệu chính: *Clean Architecture* (Robert C. Martin) và các nguồn gốc Hexagonal/Onion Architecture.

## Nhóm 1: Giới thiệu (Ngày 351–352)

**Ngày 351: Vì sao Architecture quan trọng hơn code chi tiết**

*   Mục tiêu: Hiểu vai trò của kiến trúc so với các kỹ thuật đã học trước đó.
    
*   Lý thuyết: Kiến trúc quyết định thứ khó thay đổi nhất về sau (ranh giới hệ thống, hướng phụ thuộc) - trong khi pattern/refactoring chỉ sửa được chi tiết cục bộ.
    
*   Thực hành: Nhớ lại 1 hệ thống có kiến trúc tệ (mọi thứ phụ thuộc chằng chịt), mô tả hậu quả cụ thể gặp phải khi cần thay đổi.
    

**Ngày 352: Dependency Rule**

*   Mục tiêu: Nắm nguyên lý xuyên suốt toàn bộ 4 kiến trúc sắp học.
    
*   Lý thuyết: Mọi kiến trúc hiện đại (Hexagonal/Onion/Clean) đều dựa trên 1 nguyên lý chung: dependency luôn hướng vào trong, về phía business logic - chi tiết kỹ thuật (database, framework, UI) phụ thuộc vào business logic, không phải ngược lại.
    
*   Thực hành: Vẽ sơ đồ minh hoạ vi phạm Dependency Rule (business logic import trực tiếp thư viện database) và cách tuân thủ (qua interface).
    

## Nhóm 2: Layered Architecture (Ngày 353–357)

**Ngày 353: Layered Architecture - Khái niệm**

*   Mục tiêu: Ôn lại kiến trúc phổ biến nhất, thường là điểm khởi đầu của mọi dự án.
    
*   Lý thuyết: Controller → Service → Repository, mỗi tầng chỉ gọi xuống tầng dưới, không gọi ngược lên.
    
*   Thực hành: Vẽ sơ đồ 3 tầng cho 1 use case đơn giản (tạo Order).
    

**Ngày 354: Lab - Tổ chức Payment Platform theo Layered**

*   Mục tiêu: Áp dụng vào project đã có sẵn từ Giai đoạn 7.
    
*   Lý thuyết: Ôn lại cách map domain layer (đã xây ở Giai đoạn 7) vào tầng Service.
    
*   Thực hành: Tổ chức lại code Payment Platform theo 3 tầng rõ ràng: Controller (nhận request), Service (điều phối use case), Repository (persistence).
    

**Ngày 355: Vấn đề của Layered Architecture**

*   Mục tiêu: Hiểu giới hạn thực tế của kiến trúc này khi hệ thống lớn dần.
    
*   Lý thuyết: Smart UI anti-pattern (business logic leak vào Controller), Service layer dễ trở thành God Object khi ôm quá nhiều logic.
    
*   Thực hành: Tìm 1 ví dụ trong code Ngày 354 nơi logic nghiệp vụ đang nằm sai tầng (VD: validate trong Controller).
    

**Ngày 356: Anemic Domain Model vs Rich Domain Model**

*   Mục tiêu: Nhận diện anti-pattern phổ biến nhất khi làm Layered Architecture kết hợp DDD sai cách.
    
*   Lý thuyết: Anemic Domain Model (Entity chỉ có getter/setter, toàn bộ logic nằm ở Service) vi phạm Encapsulation đã học Giai đoạn 1 - Rich Domain Model để Entity tự chứa hành vi của nó.
    
*   Thực hành: Tìm 1 Entity trong Payment Platform đang "anemic", refactor để đưa logic về đúng vị trí trong Entity.
    

**Ngày 357: Review Layered Architecture**

*   Mục tiêu: Chốt lại khi nào Layered Architecture đủ dùng.
    
*   Lý thuyết: Layered phù hợp cho hệ thống CRUD đơn giản, quy mô nhỏ-vừa; không phù hợp khi cần cô lập business logic khỏi framework/infrastructure hoàn toàn (đó là lý do Hexagonal/Clean ra đời).
    
*   Thực hành: Viết nhận xét: với quy mô Payment Platform hiện tại, Layered Architecture có đủ dùng không, tại sao.
    

## Nhóm 3: Hexagonal Architecture (Ngày 358–364)

**Ngày 358: Hexagonal Architecture - Ports & Adapters**

*   Mục tiêu: Hiểu ý tưởng cốt lõi: business logic không biết gì về thế giới bên ngoài.
    
*   Lý thuyết: Ports & Adapters (Alistair Cockburn) - core logic định nghĩa "port" (interface nó cần), "adapter" implement port đó để kết nối với thế giới thật (DB, REST, message queue).
    
*   Thực hành: Vẽ sơ đồ hình lục giác kinh điển: core ở giữa, các adapter bao quanh (REST adapter, DB adapter, message queue adapter).
    

**Ngày 359: Port - Định nghĩa Interface cho Use Case**

*   Mục tiêu: Phân biệt 2 loại port.
    
*   Lý thuyết: Inbound Port (interface use case, được driver bên ngoài gọi vào - VD: `TransferMoneyUseCase`), Outbound Port (interface core cần, được implement bởi adapter bên ngoài - VD: `AccountRepository`).
    
*   Thực hành: Định nghĩa `TransferMoneyUseCase` (inbound port) và `AccountRepository` (outbound port) cho domain Payment.
    

**Ngày 360: Adapter - Inbound vs Outbound**

*   Mục tiêu: Hiểu vai trò của adapter ở cả 2 phía.
    
*   Lý thuyết: Inbound Adapter (VD: REST Controller) gọi vào inbound port; Outbound Adapter (VD: `JpaAccountRepository`) implement outbound port.
    
*   Thực hành: Vẽ sơ đồ luồng request đi qua: REST Adapter → Inbound Port → Core Logic → Outbound Port → Database Adapter.
    

**Ngày 361: Lab - Thiết kế Port cho "Transfer Money"**

*   Mục tiêu: Thực hành thiết kế trước khi code.
    
*   Lý thuyết: Ôn lại port nên được thiết kế theo ngôn ngữ domain (Ubiquitous Language từ Giai đoạn 7), không theo ngôn ngữ kỹ thuật (không có `save()` chung chung mà là hành động nghiệp vụ cụ thể).
    
*   Thực hành: Thiết kế đầy đủ interface `TransferMoneyUseCase` (inbound), `AccountRepository`, `NotificationPort` (outbound) cho use case chuyển tiền.
    

**Ngày 362: Lab - Implement Adapter**

*   Mục tiêu: Hoàn thiện implementation cho cả 2 chiều.
    
*   Lý thuyết: Ôn lại core logic (`TransferMoneyService`) chỉ phụ thuộc vào port (interface), không phụ thuộc adapter cụ thể nào.
    
*   Thực hành: Cài đặt `TransferMoneyService` implement `TransferMoneyUseCase`, cài đặt `InMemoryAccountRepository` (outbound adapter), cài đặt 1 REST controller đơn giản (inbound adapter) gọi vào use case.
    

**Ngày 363: Testability - Lợi ích lớn nhất của Hexagonal**

*   Mục tiêu: Thấy rõ tại sao kiến trúc này được ưa chuộng cho core logic phức tạp.
    
*   Lý thuyết: Vì core chỉ phụ thuộc interface, test core logic hoàn toàn không cần database/network thật - chỉ cần fake adapter.
    
*   Thực hành: Viết unit test cho `TransferMoneyService` dùng `InMemoryAccountRepository`, verify hoàn toàn không có dependency ra ngoài (không DB, không network).
    

**Ngày 364: Review Hexagonal Architecture**

*   Mục tiêu: Tổng hợp lại toàn bộ nhóm.
    
*   Lý thuyết: Ôn lại Dependency Rule áp dụng cụ thể như thế nào trong Hexagonal (mọi thứ phụ thuộc vào port, port không phụ thuộc gì cả).
    
*   Thực hành: Viết nhận xét: Hexagonal khác Layered Architecture ở điểm mấu chốt nào (gợi ý: hướng phụ thuộc của Repository).
    

## Nhóm 4: Onion Architecture (Ngày 365–368)

**Ngày 365: Onion Architecture - Các vòng tròn đồng tâm**

*   Mục tiêu: Hiểu cách biểu diễn Dependency Rule bằng hình ảnh vòng tròn.
    
*   Lý thuyết: Domain Model ở trung tâm, tiếp theo là Domain Services, rồi Application Services, ngoài cùng là Infrastructure/UI - mọi phụ thuộc hướng vào tâm.
    
*   Thực hành: Vẽ sơ đồ Onion Architecture, đặt các thành phần Payment Platform (Entity, Domain Service, Application Service, REST Controller, Repository implementation) vào đúng vòng.
    

**Ngày 366: So sánh Onion vs Hexagonal**

*   Mục tiêu: Nhận ra 2 kiến trúc thực chất là cùng 1 ý tưởng với cách trình bày khác nhau.
    
*   Lý thuyết: Cả 2 đều tuân thủ Dependency Rule, khác nhau chủ yếu ở cách hình dung (lục giác với port/adapter tường minh vs vòng tròn đồng tâm với nhiều lớp domain rõ ràng hơn).
    
*   Thực hành: Lập bảng ánh xạ thuật ngữ giữa Hexagonal (Port/Adapter) và Onion (các vòng tròn) cho cùng 1 ví dụ.
    

**Ngày 367: Lab - Vẽ lại Payment Platform theo Onion**

*   Mục tiêu: Củng cố bằng thực hành trực tiếp.
    
*   Lý thuyết: Ôn lại thứ tự vòng tròn từ trong ra ngoài.
    
*   Thực hành: Tổ chức lại thư mục code Payment Platform theo cấu trúc Onion (VD: `domain/`, `application/`, `infrastructure/`), verify không có import ngược từ trong ra ngoài.
    

**Ngày 368: Review Onion Architecture**

*   Mục tiêu: Chốt lại điểm khác biệt thực dụng khi chọn Onion so với Hexagonal.
    
*   Lý thuyết: Onion nhấn mạnh nhiều lớp domain hơn khi domain phức tạp (phù hợp hệ thống DDD nặng); Hexagonal đơn giản, trực quan hơn khi cần nhấn mạnh việc dễ dàng thay thế adapter (VD: đổi database, đổi UI).
    
*   Thực hành: Viết nhận xét ngắn: dự án Payment Platform phù hợp hơn với cách trình bày nào, tại sao.
    

## Nhóm 5: Clean Architecture (Ngày 369–375)

**Ngày 369: Clean Architecture - 4 vòng tròn**

*   Mục tiêu: Học phiên bản tổng hợp và phổ biến nhất hiện nay.
    
*   Lý thuyết: Entities (business rule chung nhất) → Use Cases (business rule riêng của ứng dụng) → Interface Adapters (chuyển đổi dữ liệu giữa use case và thế giới ngoài) → Frameworks & Drivers (chi tiết kỹ thuật ngoài cùng).
    
*   Thực hành: Vẽ sơ đồ 4 vòng tròn Clean Architecture, so sánh với Onion (Ngày 365) - chỉ ra điểm tương đồng.
    

**Ngày 370: Dependency Rule chi tiết trong Clean Architecture**

*   Mục tiêu: Hiểu cách Clean Architecture xử lý trường hợp vòng trong cần gọi vòng ngoài.
    
*   Lý thuyết: Dependency Inversion - vòng trong định nghĩa interface, vòng ngoài implement (giống Port/Adapter), đảm bảo source code dependency luôn hướng vào trong dù luồng điều khiển (control flow) có thể đi ra ngoài.
    
*   Thực hành: Vẽ sơ đồ minh hoạ cách 1 Use Case gọi Repository (vòng ngoài hơn) mà vẫn tuân thủ Dependency Rule nhờ Dependency Inversion.
    

**Ngày 371: Use Case Layer - Interactor Pattern**

*   Mục tiêu: Hiểu cách tổ chức 1 use case cụ thể.
    
*   Lý thuyết: Interactor đóng gói 1 use case hoàn chỉnh, nhận Input boundary, trả về qua Output boundary - tách biệt hoàn toàn khỏi cách trình bày kết quả.
    
*   Thực hành: Thiết kế `TransferMoneyInteractor` với `InputData`/`OutputData` riêng biệt, không phụ thuộc format response HTTP cụ thể.
    

**Ngày 372: Interface Adapters - Presenter, Controller, Gateway**

*   Mục tiêu: Hiểu vai trò từng thành phần ở vòng Interface Adapters.
    
*   Lý thuyết: Controller (chuyển request bên ngoài thành Input boundary), Presenter (chuyển Output boundary thành format hiển thị), Gateway (implement outbound port, tương đương Repository).
    
*   Thực hành: Vẽ sơ đồ luồng đầy đủ: HTTP Request → Controller → Interactor → Presenter → HTTP Response, chỉ rõ thành phần nào thuộc vòng nào.
    

**Ngày 373: Lab - Implement 1 Use Case đầy đủ**

*   Mục tiêu: Thực hành toàn bộ 4 vòng tròn trên 1 ví dụ cụ thể.
    
*   Lý thuyết: Ôn lại toàn bộ luồng đã vẽ Ngày 372.
    
*   Thực hành: Cài đặt đầy đủ use case "Transfer Money" theo Clean Architecture: Entity, Interactor, Input/Output boundary, Controller, Presenter, Gateway.
    

**Ngày 374: Clean Architecture - Tranh cãi thực tế**

*   Mục tiêu: Có góc nhìn cân bằng, không áp dụng máy móc.
    
*   Lý thuyết: Chi phí thực tế của Clean Architecture (nhiều class/interface cho 1 use case đơn giản, learning curve cao) - nhiều kỹ sư có kinh nghiệm khuyến nghị chỉ áp dụng đầy đủ cho phần core phức tạp, không áp dụng cho toàn bộ hệ thống nhỏ.
    
*   Thực hành: Đọc 1-2 bài viết tranh luận về "Clean Architecture is overkill" và phản biện lại, viết quan điểm cá nhân sau khi cân nhắc cả 2 phía.
    

**Ngày 375: Review Clean Architecture**

*   Mục tiêu: Chốt lại nhóm kiến trúc quan trọng nhất trong 4 nhóm đã học.
    
*   Lý thuyết: Tổng hợp lại toàn bộ Use Case Ngày 373, đối chiếu với nguyên lý Dependency Rule.
    
*   Thực hành: Viết review: use case vừa cài đặt có thực sự độc lập với framework/database không (thử tưởng tượng đổi từ REST sang message queue, có cần sửa Interactor không).
    

## Nhóm 6: So sánh & Lựa chọn Kiến trúc (Ngày 376–377)

**Ngày 376: So sánh tổng hợp 4 kiến trúc**

*   Mục tiêu: Có bảng tham chiếu nhanh để dùng khi ra quyết định thực tế.
    
*   Lý thuyết: Lập bảng: Layered (đơn giản, nhanh, ít tuân thủ Dependency Rule chặt) → Hexagonal (nhấn Port/Adapter, dễ test) → Onion (nhấn nhiều lớp domain) → Clean (đầy đủ nhất, chi phí cao nhất).
    
*   Thực hành: Hoàn thiện bảng so sánh với các tiêu chí: độ phức tạp, khả năng test, tốc độ phát triển ban đầu, khả năng maintain lâu dài.
    

**Ngày 377: Lab - Chọn kiến trúc cho 3 tình huống**

*   Mục tiêu: Luyện ra quyết định kiến trúc dựa trên ngữ cảnh thực tế, không theo trend.
    
*   Lý thuyết: Ôn lại nguyên tắc: độ phức tạp kiến trúc nên tỷ lệ thuận với độ phức tạp và tuổi thọ dự kiến của hệ thống.
    
*   Thực hành: Cho 3 tình huống (MVP startup cần ra mắt nhanh, core banking system tuổi thọ 10 năm, internal tool dùng nội bộ 5 người), chọn kiến trúc phù hợp cho từng tình huống và giải thích.
    

## Nhóm 7: CQRS (Ngày 378–385)

**Ngày 378: CQRS - Giới thiệu**

*   Mục tiêu: Hiểu ý tưởng tách biệt đường ghi và đường đọc.
    
*   Lý thuyết: Command Query Responsibility Segregation - Command (thay đổi state, không trả dữ liệu) và Query (đọc dữ liệu, không thay đổi state) dùng model riêng biệt thay vì 1 model chung cho cả 2.
    
*   Thực hành: Xác định trong Order module hiện tại, method nào là Command, method nào là Query - liệt kê ra bảng.
    

**Ngày 379: Command Side**

*   Mục tiêu: Hiểu cách tổ chức phía ghi.
    
*   Lý thuyết: Command Handler nhận Command, gọi vào aggregate (đã học Giai đoạn 7) để thực hiện thay đổi, write model tối ưu cho tính nhất quán/invariant.
    
*   Thực hành: Thiết kế `CreateOrderCommand` + `CreateOrderCommandHandler` gọi vào `Order` aggregate.
    

**Ngày 380: Query Side**

*   Mục tiêu: Hiểu cách tổ chức phía đọc, độc lập hoàn toàn với write model.
    
*   Lý thuyết: Read Model được thiết kế tối ưu riêng cho việc hiển thị (denormalized, có thể là 1 flat DTO khác hẳn cấu trúc aggregate).
    
*   Thực hành: Thiết kế `OrderSummaryQuery` + `OrderSummaryQueryHandler` trả về DTO phẳng, khác cấu trúc với `Order` aggregate.
    

**Ngày 381: CQRS đơn giản vs CQRS phức tạp**

*   Mục tiêu: Hiểu CQRS có nhiều mức độ áp dụng, không phải "tất cả hoặc không gì cả".
    
*   Lý thuyết: CQRS đơn giản (cùng 1 database, chỉ tách class Command/Query) vs CQRS đầy đủ (database/read-store riêng cho read model, đồng bộ qua event).
    
*   Thực hành: Xác định mức độ CQRS phù hợp cho Order module hiện tại (đơn giản là đủ, hay cần database riêng).
    

**Ngày 382: Lab - Implement CQRS đơn giản**

*   Mục tiêu: Thực hành mức độ CQRS phổ biến nhất trong thực tế.
    
*   Lý thuyết: Ôn lại Command Handler và Query Handler dùng chung database nhưng tách biệt code path hoàn toàn.
    
*   Thực hành: Cài đặt đầy đủ CQRS đơn giản cho Order module: `CreateOrderCommandHandler`, `CancelOrderCommandHandler`, `OrderSummaryQueryHandler`, `OrderListQueryHandler`.
    

**Ngày 383: Đồng bộ Read Model - Eventual Consistency**

*   Mục tiêu: Hiểu thách thức khi tách read model ra store riêng.
    
*   Lý thuyết: Khi read model ở database riêng, nó được cập nhật qua domain event (đã học Giai đoạn 7) - có độ trễ (eventual consistency) so với write model.
    
*   Thực hành: Thiết kế event handler cập nhật read model (giả định) khi nhận `OrderCreated` event, giải thích độ trễ có thể chấp nhận được ở đâu trong hệ thống thật.
    

**Ngày 384: CQRS - Khi nào dùng, khi nào overkill**

*   Mục tiêu: Có tiêu chí rõ ràng tránh áp dụng CQRS bừa bãi.
    
*   Lý thuyết: CQRS đầy đủ phù hợp khi đường đọc và đường ghi có tải/yêu cầu rất khác nhau (VD: đọc nhiều gấp 1000 lần ghi, cần read model tối ưu riêng); với CRUD đơn giản, CQRS đầy đủ là overkill.
    
*   Thực hành: Đánh giá 3 module (Order, Product Catalog, User Profile) - module nào thực sự cần CQRS đầy đủ, module nào chỉ cần Layered Architecture thông thường.
    

**Ngày 385: Review CQRS**

*   Mục tiêu: Tổng hợp nhóm CQRS.
    
*   Lý thuyết: Ôn lại mối liên hệ giữa CQRS và Domain Event (Giai đoạn 7) - CQRS đầy đủ gần như luôn cần event để đồng bộ read model.
    
*   Thực hành: Viết tổng kết ngắn: CQRS giải quyết vấn đề gì, đánh đổi cái gì.
    

## Nhóm 8: Event Sourcing (Ngày 386–395)

**Ngày 386: Event Sourcing - Giới thiệu**

*   Mục tiêu: Hiểu cách tiếp cận lưu trữ hoàn toàn khác so với lưu snapshot state hiện tại.
    
*   Lý thuyết: Thay vì lưu state cuối cùng, lưu toàn bộ chuỗi event dẫn tới state đó - state hiện tại luôn có thể tính lại bằng cách replay toàn bộ event.
    
*   Thực hành: Vẽ sơ đồ minh hoạ: cách lưu truyền thống (chỉ lưu balance hiện tại) vs Event Sourcing (lưu từng `MoneyDeposited`/`MoneyWithdrawn`).
    

**Ngày 387: Event Store**

*   Mục tiêu: Hiểu nơi lưu trữ chuyên biệt cho Event Sourcing.
    
*   Lý thuyết: Event Store là append-only log - chỉ được thêm event mới, không bao giờ sửa/xoá event cũ.
    
*   Thực hành: Thiết kế schema đơn giản cho Event Store (stream ID, event type, event data, version/sequence number).
    

**Ngày 388: Rebuilding State từ Event**

*   Mục tiêu: Hiểu cách tính lại state hiện tại từ chuỗi event.
    
*   Lý thuyết: Replay toàn bộ event theo thứ tự, áp dụng từng event lên aggregate để đạt state hiện tại (tương tự reduce/fold trong lập trình hàm).
    
*   Thực hành: Viết hàm `rebuildAccount(events)` fold qua danh sách event, trả về `Account` với balance đúng.
    

**Ngày 389: Snapshot**

*   Mục tiêu: Học kỹ thuật tối ưu khi số lượng event quá lớn.
    
*   Lý thuyết: Snapshot lưu lại state tại 1 thời điểm, khi replay chỉ cần load snapshot gần nhất + các event sau đó, thay vì replay từ đầu.
    
*   Thực hành: Thiết kế cơ chế snapshot mỗi 100 event, viết lại `rebuildAccount` để tận dụng snapshot nếu có.
    

**Ngày 390: Lab - Event Sourcing cho Account**

*   Mục tiêu: Áp dụng vào aggregate đã có sẵn từ Payment Platform (Giai đoạn 7).
    
*   Lý thuyết: Ôn lại toàn bộ khái niệm Ngày 386-389.
    
*   Thực hành: Chuyển `Account` (đã xây ở Giai đoạn 7) sang dùng Event Sourcing hoàn toàn: mọi thay đổi qua `apply(event)`, state luôn tính từ chuỗi event.
    

**Ngày 391: Event Versioning**

*   Mục tiêu: Xử lý vấn đề thực tế: schema event thay đổi theo thời gian.
    
*   Lý thuyết: Event cũ đã lưu không được sửa (append-only), nhưng code xử lý event cần hỗ trợ nhiều version - kỹ thuật upcasting (chuyển event version cũ sang version mới khi đọc).
    
*   Thực hành: Giả lập tình huống `MoneyDeposited` v1 thiếu field `currency`, thiết kế upcaster chuyển v1 sang v2 khi replay.
    

**Ngày 392: Event Sourcing + CQRS**

*   Mục tiêu: Hiểu vì sao 2 pattern này thường đi cùng nhau trong thực tế.
    
*   Lý thuyết: Event Sourcing tự nhiên sinh ra event, rất phù hợp làm nguồn cập nhật read model của CQRS - write side là event store, read side là các projection được build từ event.
    
*   Thực hành: Vẽ sơ đồ kết hợp: Command → Aggregate (Event Sourced) → Event → Projection → Read Model.
    

**Ngày 393: Lab - Kết hợp Event Sourcing + CQRS**

*   Mục tiêu: Thực hành kết hợp cả 2 trên domain Payment.
    
*   Lý thuyết: Ôn lại Projection là 1 dạng event handler xây read model từ event stream.
    
*   Thực hành: Viết `AccountBalanceProjection` lắng nghe event từ `Account` (Ngày 390), build ra read model `AccountBalanceView` tối ưu cho truy vấn.
    

**Ngày 394: Event Sourcing - Tradeoffs**

*   Mục tiêu: Có góc nhìn cân bằng, tránh áp dụng bừa.
    
*   Lý thuyết: Chi phí thực tế: query phức tạp hơn (cần projection cho mọi câu hỏi), storage tăng theo thời gian, learning curve cao, debug khó hơn (phải hiểu toàn bộ lịch sử event).
    
*   Thực hành: Liệt kê 3 lợi ích chính (audit trail tự nhiên, time-travel debugging, dễ thêm read model mới) và 3 chi phí chính, đánh giá xem domain Payment có thực sự cần Event Sourcing hay chỉ cần domain event thông thường (Giai đoạn 7) là đủ.
    

**Ngày 395: Review Event Sourcing**

*   Mục tiêu: Tổng hợp nhóm Event Sourcing.
    
*   Lý thuyết: Ôn lại toàn bộ pipeline: Command → Event → Event Store → Replay/Snapshot → Projection.
    
*   Thực hành: Viết tổng kết: khi nào nên chọn Event Sourcing đầy đủ, khi nào chỉ cần "Domain Event" thông thường (Giai đoạn 7) là đủ cho nhu cầu audit.
    

## Nhóm 9: Saga Pattern (Ngày 396–403)

**Ngày 396: Saga - Giới thiệu**

*   Mục tiêu: Hiểu vấn đề distributed transaction giữa nhiều service/bounded context.
    
*   Lý thuyết: Không thể dùng 1 transaction ACID xuyên nhiều service (khác database) - Saga chia 1 nghiệp vụ dài thành chuỗi local transaction, mỗi bước có compensating action nếu cần rollback.
    
*   Thực hành: Vẽ sơ đồ minh hoạ vấn đề: flow đặt hàng cần cập nhật Order, Payment, Inventory ở 3 service khác nhau - không thể dùng 1 transaction chung.
    

**Ngày 397: Choreography-based Saga**

*   Mục tiêu: Học cách tiếp cận phi tập trung.
    
*   Lý thuyết: Mỗi service tự lắng nghe event từ service khác và tự quyết định phản ứng - không có nhạc trưởng trung tâm.
    
*   Thực hành: Vẽ sơ đồ Choreography cho flow đặt hàng: `OrderCreated` → Payment service lắng nghe → `PaymentCompleted` → Inventory service lắng nghe → `StockReserved`.
    

**Ngày 398: Orchestration-based Saga**

*   Mục tiêu: Học cách tiếp cận tập trung.
    
*   Lý thuyết: 1 Saga Orchestrator trung tâm điều phối, gọi tuần tự từng service và quyết định bước tiếp theo dựa trên kết quả.
    
*   Thực hành: Vẽ sơ đồ Orchestration cho cùng flow đặt hàng, với `OrderSagaOrchestrator` gọi lần lượt Payment service rồi Inventory service.
    

**Ngày 399: So sánh Choreography vs Orchestration**

*   Mục tiêu: Có tiêu chí chọn lựa rõ ràng.
    
*   Lý thuyết: Choreography đơn giản khi ít bước, nhưng khó theo dõi luồng tổng thể khi nhiều service (logic rải rác); Orchestration dễ theo dõi/debug hơn nhưng orchestrator có thể trở thành điểm phụ thuộc trung tâm.
    
*   Thực hành: Lập bảng so sánh 2 approach theo tiêu chí: độ phức tạp, khả năng debug, coupling giữa các service.
    

**Ngày 400: Compensating Transaction**

*   Mục tiêu: Hiểu cách "rollback" trong thế giới không có transaction ACID xuyên service.
    
*   Lý thuyết: Mỗi bước thuận (forward action) cần có 1 hành động bù trừ tương ứng (compensating action) - VD: `ReserveStock` có `ReleaseStock` bù trừ nếu bước sau thất bại.
    
*   Thực hành: Thiết kế compensating action cho từng bước trong Saga đặt hàng: `CancelPayment` bù cho `ChargePayment`, `ReleaseStock` bù cho `ReserveStock`.
    

**Ngày 401: Lab - Thiết kế Saga cho Flow Đặt Hàng**

*   Mục tiêu: Chuẩn bị thiết kế đầy đủ trước khi code, dùng trực tiếp cho project cuối giai đoạn.
    
*   Lý thuyết: Ôn lại toàn bộ các bước forward và compensating action đã xác định.
    
*   Thực hành: Viết đặc tả đầy đủ Saga: danh sách bước, event trigger từng bước, compensating action cho từng bước nếu thất bại.
    

**Ngày 402: Lab - Implement Saga**

*   Mục tiêu: Hiện thực hoá thiết kế.
    
*   Lý thuyết: Ôn lại chọn Orchestration (dễ debug hơn cho người mới) hoặc Choreography.
    
*   Thực hành: Cài đặt Saga đã thiết kế (khuyến nghị Orchestration), test cả trường hợp thành công và trường hợp cần compensate (VD: hết hàng ở bước Inventory).
    

**Ngày 403: Review Saga**

*   Mục tiêu: Tổng hợp nhóm Saga.
    
*   Lý thuyết: Ôn lại Saga đảm bảo "eventual consistency" giữa nhiều service, không đảm bảo tính nhất quán tức thời như transaction ACID truyền thống.
    
*   Thực hành: Viết tổng kết: những rủi ro cần lưu ý khi dùng Saga trong production (VD: retry idempotency, thứ tự event không đảm bảo).
    

## Nhóm 10: Outbox Pattern (Ngày 404–408)

**Ngày 404: Outbox Pattern - Giới thiệu**

*   Mục tiêu: Hiểu vấn đề "dual write" mà pattern này giải quyết.
    
*   Lý thuyết: Khi 1 use case cần vừa ghi database vừa publish message ra broker, 2 thao tác này không atomic - nếu ghi DB thành công nhưng publish message thất bại (hoặc ngược lại), hệ thống rơi vào trạng thái không nhất quán.
    
*   Thực hành: Vẽ sơ đồ minh hoạ tình huống dual write thất bại: DB commit thành công, nhưng service crash trước khi publish message - hậu quả là gì.
    

**Ngày 405: Transactional Outbox**

*   Mục tiêu: Học giải pháp chuẩn cho vấn đề trên.
    
*   Lý thuyết: Lưu event cần publish vào 1 bảng `outbox` trong cùng transaction database với business data - đảm bảo tính atomic ở tầng database quen thuộc.
    
*   Thực hành: Thiết kế schema bảng `outbox` (id, event type, payload, published\_at, created\_at).
    

**Ngày 406: Message Relay**

*   Mục tiêu: Hiểu cách message thực sự được đưa ra broker sau khi lưu outbox.
    
*   Lý thuyết: 1 process riêng (Message Relay/Publisher) định kỳ đọc bảng outbox, publish ra message broker, đánh dấu đã publish - có thể dùng polling hoặc Change Data Capture (CDC, VD: Debezium).
    
*   Thực hành: Vẽ sơ đồ luồng đầy đủ: Application ghi business data + outbox row (1 transaction) → Message Relay đọc outbox → publish → đánh dấu published.
    

**Ngày 407: Lab - Implement Outbox cho Payment Platform**

*   Mục tiêu: Áp dụng thực tế vào project đã có.
    
*   Lý thuyết: Ôn lại Domain Event đã publish ở Giai đoạn 7 (Ngày 344) - giờ cần đảm bảo việc publish đó atomic với việc lưu aggregate.
    
*   Thực hành: Sửa lại flow publish Domain Event ở Payment Platform: ghi aggregate + ghi outbox row trong cùng transaction, viết 1 Message Relay đơn giản (polling) đọc outbox và "publish" (in log giả lập).
    

**Ngày 408: Review Outbox Pattern**

*   Mục tiêu: Tổng hợp nhóm Outbox, kết thúc phần lý thuyết của Giai đoạn 8.
    
*   Lý thuyết: Ôn lại mối liên hệ Outbox - Saga - Event Sourcing: cả 3 đều xoay quanh việc đảm bảo tính nhất quán khi hệ thống được chia nhỏ thành nhiều service.
    
*   Thực hành: Viết sơ đồ tổng hợp thể hiện mối liên hệ giữa CQRS, Event Sourcing, Saga, Outbox - pattern nào thường đi kèm pattern nào trong thực tế.
    

## Nhóm 11: Project - Order + Payment + Inventory Services (Ngày 409–427)

**Ngày 409: Thiết kế tổng thể 3 Service**

*   Mục tiêu: Bắt đầu project bằng Strategic Design, liên hệ lại Giai đoạn 7.
    
*   Lý thuyết: Ôn lại cách xác định Bounded Context - mỗi service tương ứng 1 bounded context độc lập, có database riêng.
    
*   Thực hành: Xác định ranh giới rõ ràng cho 3 service: Order Service, Payment Service (tái sử dụng Payment Platform Giai đoạn 7), Inventory Service.
    

**Ngày 410: Chọn kiến trúc cho từng Service**

*   Mục tiêu: Áp dụng đúng mức độ kiến trúc cho từng service theo độ phức tạp thực tế.
    
*   Lý thuyết: Ôn lại bảng so sánh Ngày 376 - không phải service nào cũng cần Clean Architecture đầy đủ.
    
*   Thực hành: Chọn và ghi rõ lý do: VD Payment Service dùng Hexagonal (business rule phức tạp, cần test kỹ), Inventory Service dùng Layered đơn giản (logic đơn giản hơn).
    

**Ngày 411: Thiết kế Order Service - Domain Model**

*   Mục tiêu: Thiết kế domain model cho service đầu tiên.
    
*   Lý thuyết: Ôn lại Tactical Design (Giai đoạn 7) áp dụng cho `Order` aggregate.
    
*   Thực hành: Thiết kế `Order` aggregate, domain event (`OrderCreated`, `OrderCancelled`) cho Order Service.
    

**Ngày 412: Thiết kế Payment Service - Domain Model**

*   Mục tiêu: Tái sử dụng và điều chỉnh Payment Platform đã xây.
    
*   Lý thuyết: Ôn lại toàn bộ Payment Platform từ Giai đoạn 7-8 (Event Sourcing, Outbox đã áp dụng).
    
*   Thực hành: Xác định API/event nào của Payment Service sẽ được Order Service và Saga gọi tới.
    

**Ngày 413: Thiết kế Inventory Service - Domain Model**

*   Mục tiêu: Thiết kế domain model cho service thứ 3.
    
*   Lý thuyết: Ôn lại Aggregate, Entity cho domain quản lý tồn kho (`StockItem`, invariant "số lượng không âm").
    
*   Thực hành: Thiết kế `StockItem` aggregate với method `reserve()`/`release()`, domain event (`StockReserved`, `StockReleased`, `StockInsufficient`).
    

**Ngày 414: Thiết kế Saga cho Flow Đặt Hàng End-to-End**

*   Mục tiêu: Áp dụng trực tiếp kiến thức Saga (Ngày 396-403) cho 3 service thật.
    
*   Lý thuyết: Ôn lại thiết kế Orchestration Saga đã làm Ngày 401.
    
*   Thực hành: Hoàn thiện đặc tả Saga cho 3 service thật: `OrderCreated` → `ChargePayment` → `ReserveStock` → `OrderConfirmed` (hoặc compensate nếu bất kỳ bước nào thất bại).
    

**Ngày 415: Thiết kế Outbox cho từng Service**

*   Mục tiêu: Đảm bảo mọi service publish event một cách đáng tin cậy.
    
*   Lý thuyết: Ôn lại Transactional Outbox (Ngày 404-407), áp dụng cho cả 3 service.
    
*   Thực hành: Thiết kế schema outbox cho từng service, xác định Message Relay sẽ publish tới đâu (giả lập message broker bằng in-memory queue trước, sẽ dùng broker thật ở Giai đoạn Distributed Systems).
    

**Ngày 416: Implement Order Service - Domain + Application Layer**

*   Mục tiêu: Bắt đầu code.
    
*   Lý thuyết: Ôn lại kiến trúc đã chọn Ngày 410 cho Order Service.
    
*   Thực hành: Cài đặt `Order` aggregate, Application Service điều phối use case tạo đơn hàng.
    

**Ngày 417: Implement Order Service - API Layer**

*   Mục tiêu: Expose Order Service qua HTTP.
    
*   Lý thuyết: Tái sử dụng Mini Web Framework đã xây ở Giai đoạn 3.
    
*   Thực hành: Viết REST endpoint cho Order Service chạy trên Mini Web Framework.
    

**Ngày 418: Implement Payment Service - Domain + Application Layer**

*   Mục tiêu: Hoàn thiện service thứ 2.
    
*   Lý thuyết: Ôn lại toàn bộ Payment Platform đã xây (Event Sourcing, Outbox).
    
*   Thực hành: Điều chỉnh Payment Platform thành 1 service độc lập, expose use case `ChargePayment` qua Application Service.
    

**Ngày 419: Implement Payment Service - API Layer**

*   Mục tiêu: Expose Payment Service qua HTTP/API nội bộ.
    
*   Lý thuyết: Ôn lại API layer đã làm ở Order Service Ngày 417.
    
*   Thực hành: Viết endpoint nội bộ cho Payment Service (dùng để Saga Orchestrator gọi tới).
    

**Ngày 420: Implement Inventory Service - Domain + Application Layer**

*   Mục tiêu: Hoàn thiện service thứ 3.
    
*   Lý thuyết: Ôn lại thiết kế `StockItem` Ngày 413.
    
*   Thực hành: Cài đặt `StockItem` aggregate, Application Service cho `ReserveStock`/`ReleaseStock`.
    

**Ngày 421: Implement Inventory Service - API Layer**

*   Mục tiêu: Hoàn thiện expose service thứ 3.
    
*   Lý thuyết: Ôn lại pattern API layer đã dùng ở 2 service trước.
    
*   Thực hành: Viết endpoint nội bộ cho Inventory Service.
    

**Ngày 422: Implement Saga Orchestrator**

*   Mục tiêu: Ghép 3 service lại thành 1 luồng nghiệp vụ hoàn chỉnh.
    
*   Lý thuyết: Ôn lại thiết kế Saga Ngày 414.
    
*   Thực hành: Cài đặt `OrderSagaOrchestrator` gọi tuần tự Payment Service → Inventory Service, xử lý compensating action khi có lỗi.
    

**Ngày 423: Implement Outbox + Message Relay cho cả 3 Service**

*   Mục tiêu: Đảm bảo publish event đáng tin cậy xuyên suốt hệ thống.
    
*   Lý thuyết: Ôn lại thiết kế Ngày 415.
    
*   Thực hành: Cài đặt outbox + message relay cho cả 3 service, verify event được publish đúng dù service có "crash" giả lập giữa chừng (test bằng cách tắt Message Relay tạm thời rồi bật lại).
    

**Ngày 424: Test End-to-End Flow thành công**

*   Mục tiêu: Verify toàn bộ hệ thống hoạt động đúng khi mọi bước thành công.
    
*   Lý thuyết: Ôn lại Testing Strategy (Giai đoạn 4) áp dụng cho integration test đa service.
    
*   Thực hành: Viết test end-to-end: tạo Order → Saga chạy qua Payment → Inventory → Order chuyển trạng thái `Confirmed`.
    

**Ngày 425: Test Compensating Transaction**

*   Mục tiêu: Verify hệ thống xử lý đúng khi có lỗi giữa chừng.
    
*   Lý thuyết: Ôn lại compensating action đã thiết kế.
    
*   Thực hành: Viết test giả lập Inventory hết hàng ở bước cuối, verify Saga tự động gọi `CancelPayment` để hoàn tiền, Order chuyển trạng thái `Cancelled`.
    

**Ngày 426: Đo lường & Review Kiến trúc Tổng thể**

*   Mục tiêu: Đánh giá lại toàn bộ quyết định kiến trúc đã đưa ra trong project.
    
*   Lý thuyết: Ôn lại toàn bộ pattern đã dùng: Hexagonal/Layered, CQRS (nếu có), Event Sourcing (Payment), Saga, Outbox.
    
*   Thực hành: Viết review checklist: mỗi pattern đã dùng có thực sự cần thiết cho quy mô 3-service này không, hay đang over-engineer.
    

**Ngày 427: Retrospective Giai đoạn 8**

*   Mục tiêu: Tổng kết toàn bộ Giai đoạn Architecture Patterns - giai đoạn dài thứ 2 trong roadmap.
    
*   Lý thuyết: Nhìn lại hành trình từ Layered đơn giản tới hệ thống 3-service với Saga/Outbox - đây chính là bức tranh kiến trúc microservices thực tế production.
    
*   Thực hành: Viết báo cáo retrospective đầy đủ, xác định 3 quyết định kiến trúc bạn tự tin nhất và 3 điều sẽ làm khác nếu làm lại project này.
