Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

ADR: giữ lý do của một quyết định kiến trúc

Câu hỏi: sáu tháng sau, làm sao biết vì sao code chia thành ordering và inventory, và khi nào quyết định ấy cần đổi?

Cần biết trước: layer, module và aggregate, năm view hệ thống. Bài dùng cùng ứng dụng đơn hàng giả. ADR dưới đây là quyết định trong ví dụ, không phải phê duyệt một hệ thống production.

Quyết định cần ghi điều gì?

ADR giữ một lựa chọn cùng bối cảnh và đánh đổi ở thời điểm chọn. Tài liệu kiến trúc giữ trạng thái đang vận hành: module, database, luồng và nơi triển khai hiện tại. Nếu chỉ sửa sơ đồ sau refactor, lý do cũ mất đi; nếu chỉ giữ ADR, sơ đồ có thể lỗi thời. Nygard đề xuất ghi các quyết định nhỏ trong kho mã, giữ bản cũ khi quyết định bị thay thế. MADR cung cấp cấu trúc Markdown nhẹ cho bối cảnh, các lựa chọn, kết quả và cách xác nhận. Không cần điền mọi mục mới có một ADR hữu ích.

Không phải lần đổi tên biến nào cũng cần ADR. Ở đây, quyền sở hữu quy tắc hủy đơn và giữ tồn ảnh hưởng nhiều use case và hướng phụ thuộc, nên cần lưu lý do chung.

ADR-0001 — Chia module nghiệp vụ, giữ layer trong module

**Ngày:**2026-10-03. **Trạng thái:**Accepted trong case giả lập. **Phạm vi:**bố trí code và hợp đồng phụ thuộc của ứng dụng đặt/hủy đơn.

Bối cảnh và ràng buộc (Context)

Order sở hữu trạng thái và các dòng đơn; StockItem sở hữu 0 <= reserved <= on_hand. Hủy đơn đã SHIPPED bị từ chối; hủy lặp không sinh thêm sự kiện. Trả reservation phải qua inventory, không sửa bảng tồn từ ordering.

Ba cách tổ chức ở bài trước giữ cùng18file. Thêm lý do hủy chạm cùng5file ở cả ba cách. Mục tiêu hiện tại là tìm đúng owner và nhìn được dependency, chưa có phép đo thời gian tìm code hoặc tốc độ runtime. Chưa yêu cầu hai deploy hay database.

Ràng buộc chọn trước khi so:

  • Domain không phụ thuộc HTTP/SQL driver; application dùng domain và port.
  • Các thay đổi quy tắc hủy phải tìm được trong ordering; tồn thuộc inventory.
  • Không thêm distributed transaction hoặc service chỉ để chia folder.
  • Số aggregate hiện tại ít; không buộc thêm folder nếu chưa giải quyết khó khăn cụ thể.

Các lựa chọn

Lựa chọnTìm owner hủy đơnNhìn layerChi phí hiện tại
A: layer ngoài, domain theo typeĐi qua application/domain/interfaces/testsTập trung theo vai trò kỹ thuật18file; thay đổi5file phân tán qua các khu vực
B: module ngoài, layer trongBắt đầu ở orderingVẫn có domain/application/adapter18file; thay đổi5file cùng module
C: như B, domain theo aggregateNhư B; Order và event cùng cụmVẫn giữ use case/adapter ngoài aggregate18file;5file đổi, thêm tên cụm khi case còn nhỏ

Đây là đối chiếu vị trí của file, không là điểm hiệu năng hay điểm DDD. A vẫn có thể bảo vệ invariant đúng; C chưa giảm số file so với B trong case này.

Quyết định và hệ quả (Consequences)

Chọn B vì ranh giới ordering/inventory hiện có giúp định vị owner; layer bên trong vẫn giữ hướng phụ thuộc. Chưa chọn C vì một aggregate chính trong mỗi module chưa cho thấy cần thêm tầng folder. Một database và một deploy vẫn hợp lệ.

Hệ quả mong muốn: thay đổi lý do hủy nằm dưới ordering; inventory chỉ nhận hợp đồng trả reservation. Đánh đổi: lặp tên layer giữa module, cần wiring ở composition root, và hợp đồng giữa hai module phải được giữ rõ. Thay tên folder không cưỡng chế import; code vẫn có thể vi phạm nếu ordering truy cập adapter inventory trực tiếp.

Cách xác nhận và điều chưa kiểm (Confirmation)

Đã đối chiếu cây B trong bài tổ chức code: Order, CancelOrder, OrderStore, SqlOrderStore và OrdersHttp nằm đúng layer; luồng đặt hàng trong sequence giữ commit/rollback và201/409.

Khi có ứng dụng thật, thêm kiểm import dependency và test SHIPPED/blank reason/ hủy lặp, lưu trạng thái cùng sự kiện trong ranh giới transaction đã chọn. Hiện cây thư mục và hành vi trong bài trước là mô hình; chưa chạy các test ứng dụng ấy, chưa đo chi phí tìm code, không viết chúng thành kết quả đã đạt.

Khi nào xem lại?

Tình huống giả định: ordering có nhiều aggregate với enum/event trùng tên; ba thay đổi liên tiếp phải lần qua nhiều cụm mới tìm đúng owner. Thu thập các file thực sự đổi và đường đi tìm code. Nếu gom theo aggregate cải thiện việc đó mà vẫn giữ dependency, viết ADR-0002 cho C; đánh dấu0001 làSuperseded và link hai chiều.

Nếu inventory cần deploy độc lập, đó là ràng buộc mới về ownership, giao thức và lỗi giữa service, cần quyết định khác. Không suy từ B rằng hiện đã có microservice.

Rà soát ADR trước khi nhận

Người đọc phải truy từ câu hỏi tới ràng buộc, options, lý do chọn, hệ quả và evidence. Thử thay một ràng buộc: nếu mọi phương án vẫn được khen giống nhau, tiêu chí chưa giúp chọn. Một ADR chỉ ghi “chọn B vì best practice” không trả lời câu hỏi.

Khi thay quyết định, giữ lý do ở thời điểm cũ, tạo bản mới và cập nhật architecture hiện tại. Khi sửa lỗi chữ hoặc link, có thể sửa bản cũ với lịch sử thay đổi rõ. Không tự đổi một Proposed thành Accepted vì build Markdown xanh: trạng thái quyết định thuộc người có quyền trong dự án; ở bài này chỉ là một ví dụ đã điền.

Học tiếp: code organization, system views. Giữ ADR ngắn đủ để người sửa code đọc; link tới bằng chứng thay vì chép lại toàn bộ sơ đồ và benchmark.