ADR Là Gì và Tại Sao Cần Thiết
Architecture Decision Record (ADR) là document ngắn gọn ghi lại một quyết định kiến trúc quan trọng cùng với context và consequences. ADR giải quyết vấn đề "tại sao hệ thống được thiết kế như vậy?" - câu hỏi mà mọi developer mới join team đều hỏi.
Giá trị cốt lõi:
- Institutional memory - quyết định không bị mất khi người ra đi
- Onboarding nhanh - dev mới hiểu rationale thay vì chỉ thấy kết quả
- Tránh revisit - không lặp lại debate đã có kết luận
- Accountability - ai quyết định, dựa trên thông tin nào, khi nào
ADR Template Chuẩn
markdown# ADR-{NUMBER}: {TITLE}
## Status
[Proposed | Accepted | Deprecated | Superseded by ADR-XXX]
## Date
YYYY-MM-DD
## Context
Mô tả vấn đề, constraints, và forces đang ảnh hưởng đến quyết định.
Bao gồm: business requirements, technical constraints, team capabilities.
## Decision
Quyết định cụ thể được đưa ra. Viết ở dạng khẳng định:
"We will use X for Y because Z."
## Consequences
### Positive
- Lợi ích 1
- Lợi ích 2
### Negative
- Trade-off 1
- Trade-off 2
### Risks
- Rủi ro cần theo dõi
## Participants
- Người tham gia thảo luận và quyết định
Quản Lý ADR Trong Git Repository
Cấu trúc thư mục khuyến nghị
docs/
adr/
0001-use-postgresql-as-primary-database.md
0002-adopt-cqrs-pattern-for-order-service.md
0003-choose-rabbitmq-as-message-broker.md
template.md
README.md # index với summary của tất cả ADR
Quy tắc quản lý
- Immutable - không sửa ADR đã Accepted, chỉ tạo ADR mới supersede
- Sequential numbering - dễ reference và track timeline
- Link liên quan - ADR mới reference ADR cũ nếu liên quan
- Review qua PR - ADR được review như code, cần approval trước khi merge
Real Example 1: Choosing Message Broker
markdown# ADR-0003: Chọn RabbitMQ Làm Message Broker
## Status
Accepted
## Date
2025-03-15
## Context
Hệ thống order processing cần async communication giữa Order Service
và Inventory Service. Yêu cầu:
- Throughput: ~5000 messages/second peak
- Message ordering trong cùng partition
- Dead letter queue support
- Team có 2 người có kinh nghiệm RabbitMQ, 0 người biết Kafka
- Budget giới hạn, ưu tiên self-hosted hoặc managed service rẻ
- Không cần event replay/stream processing
### Options đánh giá:
| Criteria | RabbitMQ | Azure Service Bus | Kafka |
|-----------------------|----------|--------------------|---------|
| Throughput phù hợp | Yes | Yes | Overkill|
| Team experience | High | Medium | None |
| Ops complexity | Medium | Low (managed) | High |
| Cost (self-hosted) | Low | N/A | Medium |
| Cost (managed) | Medium | Medium | High |
| Dead letter support | Yes | Yes | Manual |
## Decision
Chọn RabbitMQ self-hosted trên Kubernetes cluster hiện tại, sử dụng
RabbitMQ Cluster Operator. Lý do chính:
1. Team đã có production experience
2. Throughput requirement vừa đủ
3. Tận dụng K8s cluster sẵn có, giảm cost
4. Routing flexibility tốt với exchange/binding model
## Consequences
### Positive
- Triển khai nhanh (team đã quen)
- Cost thấp (~$50/month cho HA cluster trên existing K8s)
- Rich routing patterns cho future use cases
### Negative
- Ops burden: team phải maintain RabbitMQ cluster
- Không có native event replay nếu cần sau này
- Scale beyond 50K msg/s sẽ cần reconsider
### Risks
- Single point of failure nếu K8s cluster down → mitigation: HA setup
- Version upgrade cần careful planning
## Participants
- Minh (Tech Lead) - decision maker
- Huy (Senior Dev) - RabbitMQ expert
- Lan (DevOps) - infrastructure assessment
Real Example 2: Choosing ORM
markdown# ADR-0005: Sử Dụng Hybrid Approach - EF Core + Dapper
## Status
Accepted
## Date
2025-04-01
## Context
Team cần standardize data access layer. Hiện tại code base dùng
raw ADO.NET, khó maintain và thiếu consistency.
| Criteria | EF Core Only | Dapper Only | Hybrid |
|---------------------|-------------|-------------|----------------|
| Developer velocity | High | Medium | High |
| Query performance | Medium | High | High |
| Complex queries | Tricky | Easy | Easy (Dapper) |
| CRUD operations | Very easy | Verbose | Easy (EF Core) |
| Migration support | Built-in | None | Built-in (EF) |
## Decision
Sử dụng EF Core cho CRUD operations và migrations, Dapper cho
complex read queries và reporting. Convention:
- Write operations → EF Core (change tracking, validation)
- Simple reads → EF Core
- Complex queries, reports, bulk reads → Dapper
- Cả hai share cùng DbContext/connection string
## Consequences
### Positive
- Best of both worlds: productivity + performance
- Team dễ adopt (EF Core cho daily work, Dapper khi cần optimize)
### Negative
- Hai abstraction layers → cần clear guidelines
- Risk inconsistency nếu không enforce convention
ADR Review Process
Workflow đề xuất
1. Author tạo ADR draft → push branch → open PR
2. Required reviewers: Tech Lead + ít nhất 1 Senior Dev affected
3. Discussion period: tối thiểu 2 business days
4. Approval criteria: majority consent, Tech Lead có veto
5. Merge PR → ADR status chuyển sang "Accepted"
6. Announce trong team channel
Review checklist cho reviewer
- Context có đầy đủ không? Người ngoài team đọc hiểu không?
- Alternatives có được evaluate công bằng không?
- Consequences có realistic không? Thiếu risk nào không?
- Decision có align với system constraints hiện tại không?
- Có conflict với ADR nào đã tồn tại không?
Tools Hỗ Trợ
adr-tools (CLI)
bash# Install
npm install -g adr-tools
# Tạo ADR mới
adr new "Use PostgreSQL as primary database"
# List tất cả ADR
adr list
# Generate table of contents
adr generate toc
Markdown conventions
- Dùng Markdown thuần - render tốt trên GitHub/Azure DevOps
- Embed Mermaid diagrams cho architecture visualization
- Dùng tables cho comparison matrix - dễ scan hơn prose
- Link đến source code hoặc PR liên quan nếu có
Checklist Khi Viết ADR
- Title rõ ràng, mô tả quyết định (không phải vấn đề)
- Context đủ chi tiết để người không tham gia cũng hiểu
- Ít nhất 2 alternatives được đánh giá
- Consequences bao gồm cả positive, negative, và risks
- Comparison matrix cho technical choices
- Status và date chính xác
- Participants được ghi nhận