System Design .NET — Tổng kết: 5 bài học còn lại
Tổng kết series System Design .NET A -> Z: năm bài học sống lâu hơn case study, checklist kiến trúc .NET production, và đọc gì sau series.
Mục lục
- Bài 1 — ước lượng đem lại gì mà vẽ hộp không?
- Bài 2 — stack mặc định đúng cho service .NET là gì?
- Bài 3 — khi nào việc chậm nên qua queue?
- Bài 4 — thiết kế cho lỗi cục bộ ra sao?
- Bài 5 — observability thật sự để làm gì?
- Năm bài học lắp thành một kiến trúc ra sao?
- Checklist kiến trúc .NET production?
- Đọc gì sau series?
- Bạn nên làm gì ngày mai?
- Nếu bài toán của tôi không khớp case study nào?
- Đi tiếp đâu từ đây?
Bạn bắt đầu series muốn cách lặp được trả lời "thiết kế X ra sao". Hy vọng bạn kết thúc với nhận thức "ra sao" quan trọng ít hơn "với số nào". Chương cuối ngắn theo thiết kế - năm bài học, một checklist, và danh sách đọc tiếp. Series ở lại; cuộc trò chuyện về codebase của bạn bắt đầu.
Bài 1 — ước lượng đem lại gì mà vẽ hộp không?
Mọi quyết định kiến trúc trong series đều quay về một số từ chương 2. Cache tồn tại vì khuếch đại read; queue tồn tại vì side effect dài; shard tồn tại vì write vượt một node Postgres. Không có số, mọi hộp trên sơ đồ là đoán mò.
Hệ quả gắt: ứng viên hay engineer bỏ qua ước lượng không bảo vệ được kiến trúc của mình. Họ mô tả được, triển khai được, nhưng khi ai đó hỏi "vì sao" họ không có gì. Số là câu trả lời. Luyện trên mọi hệ bạn gặp, ngay cả khi không ai hỏi.
Bài 2 — stack mặc định đúng cho service .NET là gì?
Sau hai mươi sáu chương, câu trả lời tẻ nhạt đúng phần lớn thời gian:
- PostgreSQL + EF Core cho lưu trữ.
- Redis cho cache và counter chia sẻ.
- RabbitMQ hoặc Azure Service Bus cho queue.
- MediatR cho pub/sub trong process.
- Polly / Microsoft.Extensions.Http.Resilience cho retry và circuit breaker.
- OpenTelemetry cho trace, metric, log.
- ASP.NET Core minimal API cho HTTP, gRPC cho service-to-service trong cluster.
Với tay vào thay thế ngoại lai
- Cassandra, Kafka, Elasticsearch - chỉ khi số ép. Lỗi phổ biến nhất tôi thấy khi review code là ai đó dùng Kafka cho service 10 RPS. Stack mặc định scale xa hơn nhiều người tưởng.
Bài 3 — khi nào việc chậm nên qua queue?
Ba dấu hiệu trả lời "có":
- Việc lâu hơn user chờ đồng bộ được.
- Việc có side effect ngoài cần giao at-least-once.
- Tải bùng phát và bên nhận không hấp thụ được đỉnh.
Nếu cái nào đúng, chương message queue áp dụng. Ghép queue với consumer idempotent (chương 10) và bạn có service sống sót mọi failure mode tạm.
Hệ quả: HTTP đồng bộ fire-and-forget gần như luôn sai. Cho bạn không retry không bền vững không back-pressure. Số sự cố production do "controller gọi third-party trong đường request" cao đến nản.
Bài 4 — thiết kế cho lỗi cục bộ ra sao?
Mọi thành phần trong kiến trúc sẽ fail lúc nào đó. Câu hỏi là cái nào trong bốn pattern reliability áp dụng:
// Bốn default resilience cho service .NET:
builder.Services.AddHttpClient<IExternalApi>()
.AddStandardResilienceHandler(); // Polly: retry + circuit breaker + timeout
builder.Services.AddMassTransit(x =>
{
x.AddSagaStateMachine<OrderSaga, OrderSagaState>()
.EntityFrameworkRepository(); // Saga + outbox + persistence
x.UsingRabbitMq();
});
// Cộng: middleware idempotency trên mọi endpoint thay đổi state
// (chương 10), và OpenTelemetry để thấy việc đang xảy ra
// (chương 13).
Bốn dòng đăng ký, bốn pattern sẵn sàng production. Phần lớn service .NET sập 3 giờ sáng thiếu một trong số đó. Cách sửa hiếm khi là kỹ thuật anh hùng; là kỷ luật tẻ dây pattern từ ngày đầu.
Bài 5 — observability thật sự để làm gì?
Ba thứ, theo thứ tự ưu tiên:
- Alert triệu chứng - báo oncall khi user không vui.
- Điều hướng trace - giải thích vì sao một request chậm.
- Planning capacity - số sau back-of-envelope thành số thật trong dashboard.
Không có OpenTelemetry, bạn có hy vọng. Có nó, bạn có phương pháp. Mọi quyết định kiến trúc trong series test được với metric nó sinh; nếu không đo được, không bảo vệ được.
Năm bài học lắp thành một kiến trúc ra sao?
Các bài học không độc lập - chúng xếp tầng thành hình mà mọi service .NET production cuối cùng nhận:
flowchart LR
Estimate[1. Ước lượng trước thiết kế] --> Stack[2. Stack mặc định:<br/>PG + Redis + RabbitMQ]
Stack --> Async[3. Queue việc chậm]
Async --> Reliab[4. Idempotency + outbox<br/>+ handler resilience]
Reliab --> Obs[5. OpenTelemetry<br/>trace + metric + log]
Obs --> Iterate[Lặp trên số]
Iterate --> Estimate
Vòng đóng vì observability nuôi vòng ước lượng tiếp: metric thật thay đoán, kiến trúc điều chỉnh, chu kỳ tiếp tục. Service chạy vòng này vài năm rốt cuộc thiết kế tốt thật - không nhờ lựa chọn anh hùng nào, mà nhờ kỷ luật đều.
Checklist kiến trúc .NET production?
Cắt ra dán lên tường:
Trước khi ship service mới, kiểm tra:
[ ] Ước lượng QPS đỉnh, storage 1 năm, budget latency p99, trên giấy.
[ ] PostgreSQL với read replica dây trong EF Core.
[ ] Redis cache với TTL rõ và IDistributedCache.
[ ] Ít nhất một đường async qua queue (RabbitMQ / Service Bus).
[ ] Middleware idempotency trên mọi endpoint thay đổi state.
[ ] Outbox pattern cho write + publish.
[ ] Resilience handler HttpClient trên mọi call ngoài.
[ ] Trace + metric OpenTelemetry, export sang backend.
[ ] Middleware rate limiter trên endpoint public.
[ ] Auth qua cookie hoặc JWT, không bao giờ localStorage.
[ ] Sitemap, dashboard observability, và runbook cho alert đầu.
[ ] Load test ở 2x đỉnh ước lượng trước launch.
Mười hai mục. Phần lớn sự cố production tôi đã debug trace về một checkbox thiếu. Danh sách là phần tẻ của system design; làm chủ nó là cái phân biệt "chạy trên máy tôi" với "chạy trên Black Friday".
Đọc gì sau series?
- Martin Kleppmann, Designing Data-Intensive Applications - hậu truyện kinh điển. Đọc một lần, rồi đọc lại.
- Google, Site Reliability Engineering (miễn phí online) - nửa vận hành của system design. SLO/SLI, error budget, ứng phó sự cố.
- Michael Nygard, Release It! - sách failure mode. Mọi pattern trong chương reliability có ở đây sâu hơn.
- highscalability.com - case study kiến trúc production thật (Twitter, Discord, Stack Overflow). Dùng để luyện khung chương 24.
- Microsoft Architecture Center -
learn.microsoft.com/azure/architecture
- kiến trúc tham chiếu .NET với code cụ thể.
Bạn nên làm gì ngày mai?
Năm thói quen nhỏ biến series thành kỹ năng vĩnh viễn:
- Trích một số bất cứ khi đề xuất thành phần. "Cần Kafka vì 10K event/giây" không phải "cần Kafka vì nó tốt cho event".
- Thêm observability trước tính năng. Dây OpenTelemetry ngày đầu của service mới. Chương observability mất 30 phút dây.
- Hỏi queue. Mỗi khi ai đề xuất queue, hỏi "ngăn failure mode nào?". Đôi khi câu trả lời là không và call đồng bộ đúng.
- Review PR qua lăng kính system design. "Idempotency của endpoint này ở đâu?" "Nếu Redis down thì sao?". Câu hỏi đẩy team về phía pattern.
- Đọc lại một chương mỗi tuần trong sáu tuần. Nhận biết quan trọng hơn nhớ; đọc lại có giãn cách thắng đọc một mạch anh hùng.
Nếu bài toán của tôi không khớp case study nào?
Bạn sẽ, thi thoảng, thiết kế gì đó không khớp chín cái nào. Vậy ổn. Case study là hình phổ biến, không phải duy nhất. Khi bài toán thật mới, nước đi đúng là mô tả nó tốt - ước lượng tải, gọi tên mô hình nhất quán, xác định failure mode - dùng từ vựng chương 1. Đó là cách pattern mới vào cuộc trò chuyện.
Đi tiếp đâu từ đây?
- Bắt đầu series: giới thiệu.
- Chuẩn bị phỏng vấn: cách trả lời.
- Chọn case study khớp dự án hiện tại: URL shortener, news feed, payment, hoặc bất cứ trong sáu cái còn lại.
Cảm ơn bạn đã đọc series. Bộ từ vựng là của bạn; các kiến trúc là của bạn để remix. Bây giờ mở một codebase production đó và tìm một pattern bạn nhận ra.