·

Tiếng Việt: Hands On System Design Session

Hands On System Design Session

Đây là buổi diễn tập đầy đủ nhất của module — bạn sẽ đi từ một câu yêu cầu mơ hồ đến một bộ tài liệu kiến trúc hoàn chỉnh, dùng đúng những kỹ thuật đã học ở bốn bài trước, trên một hệ thống có thật: dispatch system (hệ thống điều phối) cho một ứng dụng gọi xe thời gian thực.

Bạn Sẽ Xây Dựng Gì

Trước khi mở AI assistant lên, bạn cần biết rõ đường biên của bài toán. Hệ thống bạn sẽ thiết kế trong bài này là lõi điều phối (dispatch core) của một ứng dụng gọi xe kiểu Grab/Uber — không phải toàn bộ super-app, chỉ phần lõi: rider mở app đặt xe, hệ thống tìm driver gần nhất đang rảnh, match hai bên lại, theo dõi vị trí driver theo thời gian thực trong suốt chuyến đi, quản lý vòng đời của một trip từ lúc tạo đến lúc hoàn tất, và xử lý thanh toán khi trip kết thúc.

Đặc điểm khiến bài toán này thú vị — và khó — nằm ở bốn ràng buộc chồng lên nhau: matching phải xảy ra trong vài giây, vị trí driver phải cập nhật gần như liên tục (mỗi 3-5 giây) cho hàng chục nghìn driver cùng lúc, trạng thái trip phải nhất quán ngay cả khi driver mất kết nối giữa chừng, và toàn bộ luồng phải chịu được tải đột biến vào giờ cao điểm hoặc khi trời mưa.

Bạn sẽ đi qua đúng 8 giai đoạn (phase) — từ requirements discovery đến system overview document — dùng AI assistant (Claude Code, Cursor, hoặc ChatGPT đều được, prompt trong bài này dùng được cho cả ba) làm đối tác thiết kế, không phải máy sinh code. Mục tiêu cuối: một bộ tài liệu bạn có thể đưa cho một senior engineer khác đọc và bắt tay vào implement ngay, không cần hỏi lại bạn "ý bạn là gì".

Mẹo: Trước khi bắt đầu phase 1, viết ra giấy (không phải hỏi AI) 3 câu bạn tin chắc về bài toán này — ví dụ "matching phải ưu tiên thời gian chờ của rider hơn quãng đường di chuyển của driver". Đây là "ground truth" của riêng bạn để đối chiếu với output của AI sau này, tránh bị AI dẫn dắt lệch hướng.

Giai Đoạn 1: Khám Phá Yêu Cầu

Mọi thiết kế tồi đều bắt đầu từ một bộ yêu cầu tồi — không phải vì thiếu năng lực kỹ thuật, mà vì thiếu câu hỏi đúng lúc đầu. Đây là lúc AI phát huy giá trị thật sự: không phải sinh ra thiết kế, mà đóng vai "devil's advocate" (người phản biện) để lôi ra những edge case bạn quên hỏi.

Prompt mở đầu:

Tôi cần thiết kế lõi dispatch system cho một app gọi xe thời gian thực,
tương tự Grab/Uber. Phạm vi: rider request ride, hệ thống match với driver
gần nhất đang rảnh, track vị trí driver real-time trong suốt trip, quản lý
trip lifecycle, xử lý payment khi kết thúc.

Hãy đóng vai một staff engineer phản biện. Liệt kê:
1. Functional requirements tôi có thể đang bỏ sót
2. Non-functional requirements quan trọng (latency, availability, consistency)
3. Ít nhất 8 edge case có khả năng phá vỡ thiết kế nếu không tính trước
Đừng đề xuất giải pháp — chỉ đặt câu hỏi và liệt kê rủi ro.

AI trả về (rút gọn):

Functional requirements dễ bị bỏ sót: rider hủy ride sau khi đã match; driver từ chối một ride được assign; rider đặt ride cho người khác (đặt hộ); nhiều rider request cùng lúc trong bán kính hẹp.

Non-functional: matching phải hoàn tất trong <3 giây p95; location ping chấp nhận độ trễ tới 5 giây nhưng không được mất gói tin quá 30 giây liên tục; hệ thống phải survive được việc một AZ (availability zone) chết giữa giờ cao điểm.

Edge case đáng chú ý: driver mất kết nối mạng giữa trip (điện thoại hết pin, vào hầm gửi xe); không có driver rảnh trong bán kính chấp nhận được — cần chiến lược mở rộng bán kính hay báo "không có xe"? surge pricing cần kích hoạt dựa trên tỉ lệ cung/cầu real-time — dữ liệu này lấy từ đâu, tính theo cửa sổ thời gian nào? rider và driver đồng thời hủy trip ở microsecond gần nhau — ai thắng? GPS driver "nhảy" bất thường (do phản xạ tín hiệu ở nhà cao tầng) — có cần lọc outlier vị trí không?

Đây chính là lúc bạn dùng kỹ thuật stress-testing đã học ở bài 2 của module: đừng chấp nhận danh sách này ngay, hỏi tiếp "trong 8 edge case trên, cái nào nếu bỏ qua sẽ gây thiệt hại tiền bạc thực tế, cái nào chỉ là trải nghiệm xấu?" — để ưu tiên đúng.

Mẹo: Luôn yêu cầu AI tách riêng "câu hỏi cần bạn quyết định" khỏi "rủi ro kỹ thuật cần thiết kế xử lý". Gộp chung hai loại này là nguyên nhân phổ biến khiến session thảo luận yêu cầu lan man không hội tụ.

Giai Đoạn 2: Mô Hình Hóa Domain

Có yêu cầu rồi, bước tiếp là xác định domain gồm những entity nào và chúng quan hệ với nhau ra sao — trước khi vẽ bất kỳ bảng dữ liệu hay component nào. Domain modeling sai ở bước này sẽ kéo theo sai lệch ở data model và API sau đó.

Prompt:

Dựa trên yêu cầu ở trên, hãy đề xuất domain model cho dispatch system.
Với mỗi entity, liệt kê: tên, mục đích, thuộc tính cốt lõi (không cần đầy đủ),
và quan hệ với các entity khác (1-1, 1-n, n-n).
Phân biệt rõ entity nào là aggregate root theo tinh thần DDD.

Kết quả rút gọn:

  • Rider — người đặt xe. Aggregate root (gốc tổng hợp — entity chịu trách nhiệm đảm bảo tính nhất quán cho cả nhóm entity liên quan) độc lập. Quan hệ 1-n với Trip.
  • Driver — người nhận trip. Aggregate root độc lập. Có trạng thái (online/offline/on-trip). Quan hệ 1-n với Trip, 1-n với Location ping.
  • Trip — aggregate root trung tâm của toàn hệ thống, đại diện cho một chuyến đi từ lúc request đến lúc hoàn tất/hủy. Chứa reference tới Rider, Driver (nullable trước khi match), trạng thái (requested → matched → in_progress → completed/cancelled), và embed hoặc reference một Payment.
  • Location (Ping) — bản ghi vị trí rời rạc của driver theo thời gian, không phải aggregate root, thuộc về Driver và (khi đang có trip) thuộc về Trip.
  • Payment — gắn 1-1 với Trip, chứa amount, method, status (pending/captured/failed/refunded).
  • Rating — gắn 1-1 với Trip, hai chiều (rider rate driver, driver rate rider).

Đây là lúc bạn áp kỹ thuật đã học ở bài 3: hỏi ngược AI "Trip có nên là aggregate root chứa Payment, hay Payment nên là aggregate độc lập vì lý do consistency với hệ thống thanh toán bên ngoài?" — một câu hỏi domain modeling thật sự, không có câu trả lời máy móc, và bạn — không phải AI — là người quyết định cuối cùng.

Mẹo: Với hệ thống có phần realtime nặng (như Location ping), luôn hỏi riêng AI: "entity nào trong domain này có write throughput cao hơn hẳn phần còn lại, và điều đó có nên ảnh hưởng tới việc nó có phải một aggregate root riêng không?" — câu trả lời sẽ ảnh hưởng trực tiếp tới data model ở phase 4.

Giai Đoạn 3: Thiết Kế Component

Có domain rồi, giờ đến việc chia hệ thống thành các component/service chịu trách nhiệm rõ ràng — đây là lúc ranh giới kỹ thuật (technical boundary) và ranh giới nghiệp vụ (domain boundary) phải khớp nhau, nếu không bạn sẽ có microservice chỉ vì "cho oai" mà không giải quyết vấn đề gì.

Prompt:

Từ domain model trên, đề xuất kiến trúc component cho dispatch system.
Với mỗi component: tên, trách nhiệm chính, lý do nó cần tách riêng
(scaling, consistency, hay team ownership), và nó giao tiếp với
component nào khác, theo cơ chế gì (sync/async, protocol).

Kết quả rút gọn (5 component chính):

  1. Matching Service — nhận ride request, chạy geospatial query tìm driver rảnh gần nhất, áp dụng matching algorithm (thuật toán ghép cặp) để chọn driver phù hợp nhất. Tách riêng vì đây là phần cần scale độc lập và có SLA latency khắt khe nhất.
  2. Location/Tracking Service — nhận location ping tần suất cao từ driver app qua WebSocket (kênh kết nối hai chiều, giữ kết nối liên tục thay vì phải poll), ghi vào geospatial index (chỉ mục không gian địa lý dùng để tìm kiếm theo vị trí), phát vị trí cho rider app đang theo dõi trip. Tách riêng vì write throughput cực cao, không nên chung tải với các service khác.
  3. Trip State Service — nguồn sự thật (source of truth) cho vòng đời trip, quản lý state machine (requested → matched → ... → completed), publish event khi state đổi.
  4. Payment Service — gọi payment gateway bên ngoài, xử lý capture/refund, lắng nghe event "trip completed" từ Trip State Service.
  5. Notification Service — subscribe event từ các service trên, đẩy push notification/SMS cho rider và driver.

Giao tiếp giữa các service chủ yếu qua event bus (async) cho các luồng không cần phản hồi ngay (payment, notification), và gRPC/REST (sync) cho matching — nơi latency là yếu tố sống còn.

Mẹo: Khi AI đề xuất tách một component, luôn hỏi ngược "nếu tôi gộp component này với component kia lại thành một service, điều gì tệ đi cụ thể?" — nếu AI không đưa ra được một lý do vận hành cụ thể (scaling, ownership, failure isolation), rất có thể đó là tách vì "pattern đẹp" chứ không phải nhu cầu thật.

Giai Đoạn 4: Data Model

Đây là lúc domain model trừu tượng ở phase 2 trở thành schema cụ thể. Với dispatch system, điểm mấu chốt là Location/Ping — bảng này có write volume gấp hàng trăm lần các bảng còn lại, nên schema của nó cần được thiết kế khác hẳn (thường là time-series hoặc geospatial store riêng, không nằm chung OLTP database với Trip).

Prompt:

Từ domain model và component ở trên, vẽ ERD (entity-relationship diagram)
cho dispatch system bằng Mermaid. Bao gồm đầy đủ entity: Rider, Driver,
Trip, Location Ping, Payment, Rating. Với mỗi entity, liệt kê thuộc tính
cốt lõi kèm kiểu dữ liệu. Chỉ rõ cardinality của từng quan hệ.

Kết quả:

erDiagram
    RIDER ||--o{ TRIP : requests
    DRIVER ||--o{ TRIP : accepts
    DRIVER ||--o{ LOCATION_PING : sends
    TRIP ||--o{ LOCATION_PING : "tracked during"
    TRIP ||--|| PAYMENT : "settled by"
    TRIP ||--o| RATING : "rated via"

    RIDER {
        uuid id PK
        string phone_number
        string full_name
        string default_payment_method_id
        timestamp created_at
    }

    DRIVER {
        uuid id PK
        string phone_number
        string full_name
        string vehicle_plate
        string status "online, offline, on_trip"
        geo current_location
        timestamp last_ping_at
    }

    TRIP {
        uuid id PK
        uuid rider_id FK
        uuid driver_id FK "nullable until matched"
        string status "requested, matched, in_progress, completed, cancelled"
        geo pickup_location
        geo dropoff_location
        decimal estimated_fare
        decimal final_fare
        timestamp requested_at
        timestamp matched_at
        timestamp completed_at
    }

    LOCATION_PING {
        uuid id PK
        uuid driver_id FK
        uuid trip_id FK "nullable when not on trip"
        geo coordinates
        decimal heading
        decimal speed_kmh
        timestamp recorded_at
    }

    PAYMENT {
        uuid id PK
        uuid trip_id FK
        decimal amount
        string method "card, wallet, cash"
        string status "pending, captured, failed, refunded"
        string gateway_transaction_id
        timestamp captured_at
    }

    RATING {
        uuid id PK
        uuid trip_id FK
        int rider_rating_for_driver "1-5"
        int driver_rating_for_rider "1-5"
        string comment
    }

Điểm bạn cần tranh luận thẳng với AI ở đây: LOCATION_PING có thực sự nên là bảng quan hệ với foreign key đầy đủ hay không, khi tần suất ghi có thể lên tới hàng chục nghìn record/giây? Câu trả lời thực tế thường là: chỉ giữ vị trí hiện tại của driver trong bảng DRIVER (cho query nhanh), còn lịch sử ping đẩy sang time-series store (như TimescaleDB hoặc một geospatial index chuyên dụng) — ERD trên mô tả quan hệ logic, không nhất thiết là schema vật lý cuối cùng.

Mẹo: Với mọi entity có write pattern khác biệt rõ rệt (như Location ping), luôn yêu cầu AI đánh dấu riêng "đây là entity nên tách physical storage" ngay trong lúc vẽ ERD logic — tránh việc bạn quên mất và áp một database duy nhất cho toàn bộ hệ thống.

Giai Đoạn 5: Thiết Kế API

Có data model rồi, giờ đến hợp đồng giao tiếp (contract) giữa client (rider app, driver app) và dispatch system. Dispatch system cần cả REST (cho các thao tác request/response thông thường) lẫn WebSocket (cho luồng vị trí thời gian thực) — chọn sai giao thức ở đây là sai lầm kiến trúc tốn kém nhất để sửa sau này.

Prompt:

Thiết kế API cho dispatch system dựa trên data model trên. Cần:
- REST endpoint cho: tạo ride request, driver accept/decline, cập nhật
  trip status, xem chi tiết trip
- Một kênh realtime cho location update (đề xuất WebSocket hay
  Server-Sent Events, giải thích lý do)
Với mỗi endpoint: method, path, request/response body rút gọn.

Kết quả rút gọn:

POST /rides
  body: { rider_id, pickup_location, dropoff_location }
  response: 201 { ride_id, status: "requested", estimated_fare }

POST /rides/{id}/match
  # internal, gọi bởi Matching Service khi tìm được driver
  body: { driver_id }
  response: 200 { ride_id, status: "matched", driver: {...} }

PATCH /rides/{id}/status
  body: { status: "in_progress" | "completed" | "cancelled", reason? }
  response: 200 { ride_id, status, updated_at }

GET /rides/{id}
  response: 200 { ride_id, rider_id, driver_id, status, pickup_location,
                   dropoff_location, estimated_fare, final_fare }

WS /rides/{id}/location
  # rider app subscribe kênh này sau khi matched
  server push: { driver_id, coordinates: {lat, lng}, heading, speed_kmh, recorded_at }
  cadence: mỗi 3-5 giây trong suốt trip

AI đề xuất WebSocket thay vì polling REST vì cadence 3-5 giây trên hàng chục nghìn trip đồng thời sẽ tạo tải request khổng lồ và độ trễ không ổn định nếu polling; WebSocket giữ một kết nối persistent, cho phép server push ngay khi có ping mới, giảm cả băng thông lẫn latency.

Mẹo: Với mọi endpoint có tần suất gọi cao (như location update), luôn bắt AI so sánh ít nhất hai lựa chọn giao thức kèm lý do định lượng (số request/giây, băng thông ước tính) — đừng chấp nhận câu trả lời "WebSocket tốt hơn" mà không có con số đứng sau.

Giai Đoạn 6: Architecture Decision Records (ADR)

Mọi quyết định kiến trúc quan trọng cần được ghi lại — không phải cho AI, mà cho engineer sáu tháng sau (có thể chính là bạn) tự hỏi "tại sao lúc đó lại chọn thế này?". ADR (Architecture Decision Record — bản ghi quyết định kiến trúc) là công cụ cho việc đó.

Prompt:

Viết một ADR cho quyết định: chọn công nghệ lưu trữ vị trí driver
để phục vụ geospatial query (tìm driver gần nhất). Hai lựa chọn đang
cân nhắc: Redis Geo và PostGIS. Format ADR chuẩn: Context, Decision,
Alternatives Considered, Consequences.

Kết quả rút gọn:


## Context
Matching Service cần tìm driver online trong bán kính N km quanh điểm
pickup, với độ trễ dưới 200ms, tại throughput ước tính 5,000 query/giây
giờ cao điểm. Vị trí driver cập nhật liên tục (mỗi 3-5s).

## Decision
Dùng Redis Geo (GEOADD/GEOSEARCH) làm index chính cho truy vấn
"driver gần nhất", đồng bộ bất đồng bộ sang PostGIS cho các báo cáo
phân tích không yêu cầu realtime.

## Alternatives Considered
- PostGIS làm nguồn duy nhất: mạnh về truy vấn phức tạp (polygon,
  zone) nhưng write throughput cho update vị trí liên tục sẽ tạo áp
  lực lớn lên primary database dùng chung với Trip State.
- Redis Geo làm nguồn duy nhất, bỏ PostGIS: đơn giản hơn nhưng mất
  khả năng truy vấn phân tích phức tạp (zone-based pricing, heatmap
  lịch sử).

## Consequences
- Được: latency truy vấn matching thấp, tách tải write khỏi OLTP chính.
- Mất: có độ trễ đồng bộ (eventual consistency) giữa Redis và PostGIS,
  chấp nhận được vì báo cáo phân tích không cần realtime.
- Rủi ro: Redis là single point of failure cho matching nếu không
  cấu hình replica — cần Redis Sentinel/Cluster.

Đây chính là kỹ thuật đã học ở bài 4 của module: một ADR tốt không chỉ ghi lại "chọn gì" mà bắt buộc liệt kê phương án bị loại và lý do — để tránh việc ai đó sau này "phát hiện lại" PostGIS rồi đề xuất chuyển đổi mà không biết nó từng bị cân nhắc và loại bỏ có chủ đích.

Mẹo: Luôn yêu cầu AI viết phần "Consequences" gồm cả điều mất đi, không chỉ điều đạt được — một ADR chỉ toàn ưu điểm là dấu hiệu bạn (hoặc AI) chưa thật sự đánh đổi (trade-off) gì cả.

Giai Đoạn 7: C4 Context Diagram

Trước khi đi sâu vào bên trong hệ thống, bạn cần một bức tranh toàn cảnh: dispatch system tương tác với ai, hệ thống nào bên ngoài. Đây là mức zoom cao nhất trong mô hình C4 (Context, Container, Component, Code) — C4 Context diagram.

Prompt:

Vẽ C4 Context diagram cho dispatch system bằng Mermaid. Actor/hệ thống
ngoài cần có: Rider app, Driver app, Dispatch system (hệ thống trung tâm),
Payment gateway, Maps/geolocation provider, Notification provider (SMS/push).
Chỉ rõ hướng và mục đích của mỗi luồng giao tiếp.

Kết quả:

C4Context
    title C4 Context Diagram - Ride-Hailing Dispatch System

    Person(rider, "Rider", "Đặt xe qua Rider App")
    Person(driver, "Driver", "Nhận trip qua Driver App")

    System(dispatch, "Dispatch System", "Match rider-driver, track trip, quản lý vòng đời chuyến đi")

    System_Ext(paymentGw, "Payment Gateway", "Xử lý capture/refund tiền")
    System_Ext(mapsProvider, "Maps/Geolocation Provider", "Geocoding, routing, ETA")
    System_Ext(notifyProvider, "Notification Provider", "Gửi SMS/push notification")

    Rel(rider, dispatch, "Gửi ride request, xem trạng thái trip", "HTTPS/WebSocket")
    Rel(driver, dispatch, "Gửi location ping, accept/decline trip", "HTTPS/WebSocket")
    Rel(dispatch, paymentGw, "Capture tiền khi trip completed", "REST/HTTPS")
    Rel(dispatch, mapsProvider, "Lấy route, ETA, geocode địa chỉ", "REST/HTTPS")
    Rel(dispatch, notifyProvider, "Gửi thông báo trạng thái trip", "REST/HTTPS")
    Rel(dispatch, rider, "Push vị trí driver realtime", "WebSocket")

Diagram này trả lời đúng một câu hỏi duy nhất: hệ thống này nằm ở đâu trong bức tranh lớn hơn, phụ thuộc gì, và ai phụ thuộc vào nó — không đi vào chi tiết bên trong Matching Service hay Trip State Service (đó là việc của C4 Container/Component diagram, mức zoom sâu hơn).

Mẹo: Khi AI vẽ C4 Context, luôn kiểm tra xem nó có "quên" một dependency ngầm định hay không — ví dụ dispatch system chắc chắn cần một identity/auth provider để xác thực rider và driver, dù việc này thường bị AI bỏ sót vì "hiển nhiên quá nên không nhắc tới".

Giai Đoạn 8: System Overview Document

Bảy phase trước tạo ra rất nhiều mảnh ghép rời rạc: yêu cầu, domain model, component, data model, API, ADR, diagram. Phase cuối cùng gộp tất cả lại thành một tài liệu duy nhất mà một engineer mới có thể đọc trong 20 phút và hiểu toàn bộ hệ thống.

Prompt:

Tổng hợp toàn bộ thông tin từ 7 phase trên thành một System Overview
Document cho dispatch system. Cấu trúc: Purpose, Scope & Constraints,
High-Level Architecture (tham chiếu C4 Context), Core Domain Entities,
Key Components & Responsibilities, Data Flow (mô tả luồng một ride
request điển hình từ đầu tới cuối), Key Architecture Decisions
(tham chiếu ADR), Known Limitations & Future Considerations.

Kết quả rút gọn (outline + đoạn mở của một phần):


## Purpose
Lõi điều phối chịu trách nhiệm match rider với driver gần nhất,
theo dõi vị trí realtime trong suốt trip, và quản lý vòng đời
trip từ request đến completed/cancelled.

## Scope & Constraints
- Trong phạm vi: matching, tracking, trip lifecycle, payment trigger.
- Ngoài phạm vi: pricing model chi tiết, driver onboarding, fraud detection.
- Ràng buộc: matching p95 < 3s, location ping cadence 3-5s,
  chịu được mất một AZ.

## High-Level Architecture
[tham chiếu C4 Context diagram ở phase 7]

## Core Domain Entities
Rider, Driver, Trip, Location Ping, Payment, Rating — xem chi tiết ERD.

## Key Components & Responsibilities
Matching Service, Location/Tracking Service, Trip State Service,
Payment Service, Notification Service — xem bảng trách nhiệm chi tiết.

## Data Flow: Một Ride Request Điển Hình
1. Rider app gửi POST /rides.
2. Matching Service query Redis Geo tìm driver rảnh trong bán kính,
   chấm điểm và chọn driver tối ưu.
3. Trip State Service chuyển trạng thái requested → matched, publish event.
4. Driver app nhận push notification qua Notification Service.
   ...

## Key Architecture Decisions
ADR-004: Redis Geo cho geospatial index. [xem thêm các ADR khác]

## Known Limitations & Future Considerations
Chưa xử lý multi-stop ride; chưa có chiến lược surge pricing tự động
theo vùng; failover Redis cần được stress-test thêm trước khi go-live.

Tài liệu này không phải nơi lặp lại chi tiết của 7 phase trước — nó là bản đồ dẫn đường, trỏ tới các artifact chi tiết hơn (ERD, ADR, API spec) chứ không nhồi nhét lại toàn bộ nội dung.

Mẹo: Luôn giữ mục "Known Limitations & Future Considerations" trung thực và cụ thể — một System Overview Document "hoàn hảo không tì vết" thường là dấu hiệu tác giả (người hoặc AI) đang che giấu rủi ro chưa giải quyết, không phải dấu hiệu thiết kế tốt.

Rà Soát Toàn Bộ Kết Quả Thiết Kế

Bạn vừa đi qua 8 phase với AI, có trong tay một bộ tài liệu trông rất chuyên nghiệp — ERD đẹp, C4 diagram rõ ràng, ADR có cấu trúc chuẩn. Nhưng đây chính là lúc dễ mắc bẫy nhất: trông chuyên nghiệp không đồng nghĩa với đúng. Trước khi coi bộ tài liệu này là final, hãy chạy qua checklist rà soát sau — áp lại đúng các kỹ thuật đã học xuyên suốt module.

1. Steelman (dựng lập luận mạnh nhất có thể cho phương án đối lập) lại từng quyết định lớn (bài 1 của module). Với ADR-004 (Redis Geo), tự hỏi: nếu tôi phải bảo vệ phương án PostGIS-only trước ban kỹ thuật, tôi sẽ lập luận thế nào? Nếu bạn không dựng được một lập luận đối lập đủ mạnh, có thể bạn (và AI) chưa thật sự cân nhắc kỹ.

2. Stress-test lại domain model và data model (bài 2 và 3). Đặt câu hỏi: nếu một rider request 2 ride gần như đồng thời từ 2 thiết bị khác nhau, Trip aggregate xử lý ra sao? Nếu domain model ở phase 2 không có câu trả lời rõ, quay lại sửa trước khi implement.

3. Đối chiếu ngược với yêu cầu gốc ở phase 1. Lấy danh sách edge case đã liệt kê (driver mất kết nối giữa trip, không có driver rảnh, GPS nhảy bất thường) và tự hỏi: kiến trúc ở phase 3-5 có thực sự xử lý được từng cái, hay chỉ được nhắc tên rồi bị lãng quên? Đây là lỗi phổ biến nhất — requirements discovery rất đầy đủ nhưng component design ở phase 3 không quay lại giải quyết hết.

4. Kiểm tra tính nhất quán xuyên các artifact. Field trong ERD (phase 4) có khớp với body của API (phase 5) không? Component được liệt kê ở phase 3 có xuất hiện đầy đủ trong C4 diagram (phase 7) không? AI có xu hướng "quên" chi tiết giữa các lượt hỏi khác nhau trong cùng phiên — đây chính là lúc bạn, người giữ context toàn cục, phải bắt lỗi.

5. Validate với business rule thật, không phải giả định chung chung. Ví dụ AI giả định "matching ưu tiên driver gần nhất" — nhưng business thật của bạn có thể ưu tiên driver có rating cao hơn, hoặc driver đã chờ lâu nhất (fairness). Không có tài liệu AI-generated nào biết được rule nghiệp vụ riêng của công ty bạn nếu bạn không cung cấp.

6. Đưa cho một đồng nghiệp senior khác đọc thử. Test cuối cùng và đáng tin cậy nhất: nếu một engineer chưa từng tham gia phiên thiết kế này đọc System Overview Document và không có câu hỏi "vậy cái này xử lý sao?" nào bị bỏ ngỏ, tài liệu của bạn đã đạt chất lượng cần thiết.

Bộ tài liệu AI giúp bạn tạo ra nhanh gấp nhiều lần so với việc tự viết từ đầu — nhưng tốc độ đó chỉ có giá trị nếu bạn dành thời gian tiết kiệm được để rà soát kỹ hơn, chứ không phải để ship nhanh hơn mà bỏ qua bước phản biện.

Mẹo: Dành riêng một pass rà soát cuối chỉ để tìm mâu thuẫn giữa các phase (không phải trong từng phase) — đây là loại lỗi AI dễ tạo ra nhất và khó bị phát hiện nhất, vì mỗi phase đọc riêng lẻ đều có vẻ hợp lý.

Những điểm chính

  • Một phiên thiết kế hệ thống hoàn chỉnh với AI đi qua 8 giai đoạn nối tiếp: requirements discovery, domain modeling, component design, data model, API design, ADR, C4 context diagram, và system overview document.
  • Mỗi giai đoạn cần một prompt cụ thể, có ràng buộc rõ (phạm vi, format output) — prompt càng mơ hồ, output càng cần rà soát nhiều hơn.
  • Data model và C4 diagram không phải hoạt động trang trí — chúng là công cụ để lộ ra mâu thuẫn mà văn bản thuần không lộ ra được (ví dụ entity nào cần tách physical storage vì write throughput khác biệt).
  • ADR tốt phải liệt kê phương án bị loại và điều bị đánh đổi (mất đi), không chỉ lợi ích đạt được.
  • Rà soát cuối cùng không phải đọc lại từng phase riêng lẻ, mà là tìm mâu thuẫn giữa các phase — đối chiếu domain model với data model, đối chiếu component design với edge case đã liệt kê ở requirements discovery.
  • AI giúp bạn đi nhanh qua 8 giai đoạn, nhưng trách nhiệm cuối cùng về tính đúng đắn của kiến trúc — steelmanning, stress-testing, đối chiếu business rule thật — luôn thuộc về bạn, không phải AI.