Mermaid.js Diagram Templates cho System Design
“Một bức diagram tốt thay thế 1,000 dòng giải thích. Trong System Design Interview, vẽ trước rồi nói sau.”
Tags: system-design mermaid diagram template Liên quan: Tuan-01-Scale-From-Zero-To-Millions · Tuan-02-Back-of-the-envelope · Tuan-05-Load-Balancer · Tuan-06-Cache-Strategy · Tuan-07-Database-Sharding-Replication
Cách sử dụng trong Obsidian
Obsidian hỗ trợ Mermaid.js native. Chỉ cần tạo code block với ngôn ngữ mermaid:
```mermaid
flowchart LR
A[Client] --> B[Server]
```
Mẹo tùy chỉnh nhanh:
-
Copy template bên dưới, paste vào note của bạn
-
Thay đổi text trong
[],(),{}để đổi nội dung node -
Thay đổi
styleở cuối để đổi màu sắc -
LR= Left to Right,TD= Top Down,BT= Bottom to Top -
Comment bắt đầu bằng ` Template: Single Server Setup Template: Load Balanced Multi-Server Template: 3-Tier Architecture Template: Microservices Architecture Template: Load Balancer with Health Checks Template: Cache-Aside Pattern Template: Cache-Aside Sequence (chi tiết hơn) Template: Database Replication Template: Database Sharding Template: Message Queue Pattern Template: Rate Limiter (Token Bucket / Sliding Window) Template: CI/CD Pipeline Template: Monitoring Stack Template: Blue-Green Deployment Khi switch: Blue trở thành standby / rollback target
style Blue fill:#bbdefb,stroke:#1565c0,stroke-width:2px style Green fill:#c8e6c9,stroke:#2e7d32,stroke-width:2px,stroke-dasharray: 5 5
### 3.4 Canary Deployment
> Phát hành version mới cho một phần nhỏ user trước — giảm rủi ro.
```mermaid
flowchart TD
Customize: Đổi % canary, thêm metric check
Users["👥 Users (100%)"]
LB["Load Balancer<br/>(Traffic Splitting)"]
subgraph Stable["Stable — v1.2.0 (95% traffic)"]
S1["Server 1"]
S2["Server 2"]
S3["Server 3"]
S4["Server 4"]
end
subgraph Canary["Canary — v1.3.0 (5% traffic)"]
C1["Server 5<br/>(Canary)"]
end
Monitor["📊 Monitoring<br/>So sánh metrics:<br/>Error rate · Latency · CPU"]
Decision{{"Canary healthy?"}}
Users --> LB
LB -->|"95%"| Stable
LB -->|"5%"| Canary
Canary --> Monitor
Stable --> Monitor
Monitor --> Decision
Decision -->|"Yes → Tăng dần<br/>5% → 25% → 50% → 100%"| Stable
Decision -->|"No → Rollback<br/>100% về Stable"| LB
style Canary fill:#fff9c4,stroke:#f57f17,stroke-width:2px
style Stable fill:#c8e6c9,stroke:#2e7d32
4. Security Diagrams
4.1 OAuth2 Authorization Code Flow
Flow an toàn nhất cho server-side apps — Client không bao giờ thấy password.
sequenceDiagram Customize: Đổi provider (Google, GitHub), thêm PKCE cho mobile participant U as 👤 User (Browser) participant C as 🖥️ Client App<br/>(Backend) participant A as 🔐 Auth Server<br/>(Google / Auth0) participant R as 📦 Resource Server<br/>(API) U->>C: 1. Click "Login with Google" C->>U: 2. Redirect → Auth Server<br/>/authorize?client_id=xxx<br/>&redirect_uri=xxx<br/>&response_type=code<br/>&scope=profile email<br/>&state=random_csrf_token U->>A: 3. User đăng nhập + đồng ý quyền A->>U: 4. Redirect về Client<br/>/callback?code=AUTH_CODE&state=xxx U->>C: 5. Browser gửi AUTH_CODE về backend C->>A: 6. POST /token<br/>{code, client_id, client_secret, redirect_uri} A->>C: 7. {access_token, refresh_token, expires_in} C->>R: 8. GET /api/user<br/>Authorization: Bearer {access_token} R->>C: 9. {user data} C->>U: 10. Welcome, User! Note over C,A: access_token ngắn hạn (15 phút)<br/>refresh_token dài hạn (30 ngày)<br/>client_secret KHÔNG bao giờ lộ ra frontend
4.2 JWT Authentication Flow
Stateless authentication — server không cần lưu session, token tự chứa thông tin.
sequenceDiagram Customize: Thêm refresh token rotation, đổi expiry time participant C as 👤 Client participant A as 🔐 Auth Service participant API as 📦 API Server participant DB as 🗄️ Database C->>A: 1. POST /login {email, password} A->>DB: 2. Verify credentials<br/>(bcrypt compare) DB-->>A: 3. User valid ✅ Note over A: Tạo JWT Token:<br/>Header: {alg: RS256}<br/>Payload: {sub: user_id, role: admin, exp: +15min}<br/>Signature: sign(header.payload, private_key) A-->>C: 4. {access_token: "eyJ...", refresh_token: "xyz...", expires_in: 900} C->>API: 5. GET /api/orders<br/>Authorization: Bearer eyJ... Note over API: Verify JWT:<br/>1. Decode header + payload<br/>2. Verify signature (public_key)<br/>3. Check exp > now<br/>4. Check role permission API-->>C: 6. 200 OK {orders data} Note over C: Khi access_token hết hạn: C->>A: 7. POST /refresh {refresh_token: "xyz..."} A-->>C: 8. {new access_token, new refresh_token} Note over A: Refresh Token Rotation:<br/>Mỗi lần dùng refresh_token cũ bị revoke<br/>→ Phát hiện token theft nếu dùng lại token cũ
4.3 Zero Trust Network Architecture
“Never trust, always verify” — Mọi request đều phải được xác thực, kể cả internal traffic.
flowchart TD Customize: Thêm micro-segmentation, đổi policy engine subgraph External["External Network"] User["👤 User<br/>(Any location)"] Device["📱 Device"] end subgraph ZeroTrustLayer["Zero Trust Layer"] IdP["🔐 Identity Provider<br/>(Okta / Azure AD)<br/>MFA Required"] DeviceTrust["📋 Device Trust<br/>Check: OS patched?<br/>Disk encrypted?<br/>MDM enrolled?"] PolicyEngine["⚙️ Policy Engine<br/>Who + What + Where + When<br/>→ Allow / Deny / Step-up"] Proxy["🛡️ Access Proxy<br/>(BeyondCorp / Cloudflare Access)<br/>mTLS + JWT"] end subgraph InternalServices["Internal Services (Micro-segmented)"] ServiceA["Service A<br/>🔒 mTLS"] ServiceB["Service B<br/>🔒 mTLS"] ServiceC["Service C<br/>🔒 mTLS"] DB[("Database<br/>🔒 Encrypted at rest")] end subgraph Monitoring["Continuous Monitoring"] SIEM["📊 SIEM<br/>(Security Events)"] AnomalyDetect["🔍 Anomaly Detection<br/>Unusual access patterns"] end User --> IdP Device --> DeviceTrust IdP --> PolicyEngine DeviceTrust --> PolicyEngine PolicyEngine -->|"Allow"| Proxy Proxy -->|"mTLS"| ServiceA Proxy -->|"mTLS"| ServiceB Proxy -->|"mTLS"| ServiceC ServiceA <-->|"mTLS"| ServiceB ServiceB <-->|"mTLS"| ServiceC ServiceC --> DB ServiceA & ServiceB & ServiceC --> SIEM SIEM --> AnomalyDetect AnomalyDetect -->|"Revoke access"| PolicyEngine style ZeroTrustLayer fill:#ffecb3,stroke:#ff6f00,stroke-width:2px style InternalServices fill:#e8eaf6,stroke:#283593 style Monitoring fill:#fce4ec,stroke:#c62828
5. Case Study Templates
5.1 URL Shortener — High Level
Tham chiếu: Tuan-16-Design-URL-Shortener
flowchart TD Customize: Đổi hash strategy, thêm analytics, thêm expiry User["👤 User"] subgraph WriteFlow["Write Flow: Tạo short URL"] W_API["API Server<br/>POST /api/shorten"] W_IDGen["ID Generator<br/>(Snowflake / Base62 encode)"] W_DB[("URL Database<br/>short_code → original_url<br/>PostgreSQL")] end subgraph ReadFlow["Read Flow: Redirect"] R_API["API Server<br/>GET /abc123"] R_Cache[("Redis Cache<br/>Hot URLs<br/>TTL: 24h")] R_DB[("URL Database")] end subgraph Analytics["Analytics (Async)"] Queue[["Kafka<br/>click-events"]] ClickProcessor["Click Processor<br/>(Country, Device, Referrer)"] AnalyticsDB[("ClickHouse<br/>Analytics DB")] end User -->|"1. POST long_url"| W_API W_API --> W_IDGen W_IDGen -->|"2. Generate: abc123"| W_API W_API -->|"3. Store mapping"| W_DB W_API -->|"4. Return: short.ly/abc123"| User User -->|"5. GET /abc123"| R_API R_API -->|"6a. Check cache"| R_Cache R_Cache -->|"HIT"| R_API R_API -->|"6b. Cache MISS"| R_DB R_API -->|"7. 302 Redirect → original_url"| User R_API -->|"8. Log click event"| Queue Queue --> ClickProcessor --> AnalyticsDB style WriteFlow fill:#e3f2fd,stroke:#1565c0 style ReadFlow fill:#e8f5e9,stroke:#2e7d32 style Analytics fill:#fff3e0,stroke:#e65100
5.2 Chat System (WebSocket)
Tham chiếu: Tuan-17-Design-Chat-System
flowchart TD Customize: Thêm group chat logic, thêm read receipts, thêm typing indicator subgraph Clients["Clients"] UserA["👤 User A<br/>(WebSocket connected)"] UserB["👤 User B<br/>(WebSocket connected)"] UserC["👤 User C<br/>(Offline)"] end subgraph ChatService["Chat Infrastructure"] LB["Load Balancer<br/>(Sticky Session / WebSocket aware)"] WS1["WebSocket Server 1<br/>Connections: User A"] WS2["WebSocket Server 2<br/>Connections: User B"] subgraph Routing["Message Routing"] PubSub[["Redis Pub/Sub<br/>Channel per user"]] PresenceService["Presence Service<br/>(Online/Offline tracker)"] SessionStore[("Session Store<br/>user_id → ws_server_id")] end end subgraph Storage["Storage"] MsgQueue[["Kafka<br/>message-events"]] MsgDB[("Message DB<br/>(Cassandra)<br/>Partition key: chat_id")] PushService["Push Notification<br/>(FCM / APNs)"] end UserA -->|"1. Send message"| LB --> WS1 WS1 -->|"2. Publish to channel"| PubSub WS1 -->|"3. Persist async"| MsgQueue --> MsgDB PubSub -->|"4a. User B online"| WS2 WS2 -->|"5a. Deliver via WebSocket"| UserB PresenceService -->|"4b. User C offline"| PushService PushService -->|"5b. Push notification"| UserC style ChatService fill:#e3f2fd,stroke:#1565c0,stroke-width:2px style Storage fill:#fff3e0,stroke:#e65100
5.3 News Feed (Fan-out)
Fan-out on write (push model) vs Fan-out on read (pull model) — trade-off giữa write cost và read latency.
flowchart TD Customize: Đổi threshold follower count, thêm ranking algorithm Publisher["👤 Publisher<br/>tạo post mới"] subgraph FanOutService["Fan-out Service"] PostAPI["Post API<br/>POST /feed"] PostDB[("Post Storage<br/>(MySQL)")] FanOutWorker["Fan-out Worker"] GraphDB[("Social Graph<br/>follower list")] Decision{{"Followers > 10K?<br/>(Celebrity check)"}} end subgraph FanOutOnWrite["Fan-out on Write (Push)"] Queue1[["Task Queue"]] CacheWriter["Cache Writer"] FeedCache1[("Feed Cache<br/>user:feed:A → [post_ids]<br/>user:feed:B → [post_ids]")] end subgraph FanOutOnRead["Fan-out on Read (Pull)"] FeedCache2[("Celebrity Posts<br/>celebrity:123 → [post_ids]")] end subgraph ReadPath["Read Path — GET /feed"] FeedAPI["Feed API"] Merger["Feed Merger<br/>+ Ranking<br/>(Chronological + ML Score)"] Reader["👤 Reader"] end Publisher --> PostAPI --> PostDB PostAPI --> FanOutWorker FanOutWorker --> GraphDB GraphDB --> Decision Decision -->|"No (<10K followers)<br/>Push to each follower cache"| Queue1 Queue1 --> CacheWriter --> FeedCache1 Decision -->|"Yes (Celebrity)<br/>Lazy load on read"| FeedCache2 Reader --> FeedAPI FeedAPI --> FeedCache1 FeedAPI --> FeedCache2 FeedCache1 --> Merger FeedCache2 --> Merger Merger -->|"Sorted + Ranked feed"| Reader style FanOutOnWrite fill:#e8f5e9,stroke:#2e7d32 style FanOutOnRead fill:#fff3e0,stroke:#e65100 style Decision fill:#f9a825,stroke:#333,stroke-width:2px
5.4 Notification System (Multi-channel)
Gửi notification qua nhiều kênh: Push, SMS, Email — routing dựa trên user preference.
flowchart TD Customize: Thêm kênh, đổi priority logic, thêm template engine subgraph Triggers["Trigger Sources"] OrderSvc["Order Service<br/>order.completed"] PaymentSvc["Payment Service<br/>payment.failed"] SocialSvc["Social Service<br/>user.followed"] ScheduleSvc["Scheduler<br/>daily_digest"] end subgraph NotificationService["Notification Service"] Ingestion[["Kafka<br/>notification-events"]] Processor["Notification Processor"] Template["Template Engine<br/>(i18n support)"] UserPref[("User Preferences<br/>channel: [push, email]<br/>quiet_hours: 22:00-07:00")] RateLimiter["Rate Limiter<br/>Max 10 noti/user/hour"] Router["Channel Router"] end subgraph Channels["Delivery Channels"] Push["📱 Push<br/>(FCM / APNs)"] SMS["📲 SMS<br/>(Twilio)"] Email["📧 Email<br/>(SendGrid / SES)"] InApp["🔔 In-App<br/>(WebSocket)"] end subgraph Tracking["Delivery Tracking"] StatusDB[("Delivery Status DB<br/>sent / delivered / read / failed")] Retry[["Retry Queue<br/>(Failed deliveries)"]] end Triggers --> Ingestion Ingestion --> Processor Processor --> Template Processor --> UserPref Processor --> RateLimiter RateLimiter --> Router Router -->|"preference: push"| Push Router -->|"preference: sms"| SMS Router -->|"preference: email"| Email Router -->|"always"| InApp Push & SMS & Email & InApp --> StatusDB StatusDB -->|"status: failed"| Retry Retry --> Router style NotificationService fill:#e3f2fd,stroke:#1565c0,stroke-width:2px style Channels fill:#e8f5e9,stroke:#2e7d32
5.5 Distributed Key-Value Store (Hash Ring + Replication)
Consistent Hashing phân phối data — replication đảm bảo durability.
flowchart TD Customize: Đổi replication factor, thêm virtual nodes, đổi consistency level Client["Client<br/>PUT/GET key"] Coordinator["Coordinator Node<br/>(Bất kỳ node nào)"] subgraph HashRing["Consistent Hash Ring"] direction LR N1["Node A<br/>Range: 0–90"] N2["Node B<br/>Range: 91–180"] N3["Node C<br/>Range: 181–270"] N4["Node D<br/>Range: 271–360"] end subgraph Replication["Replication (N=3)"] direction TB Primary["Primary: Node B<br/>(hash(key) lands here)"] Rep1["Replica 1: Node C<br/>(Clockwise next)"] Rep2["Replica 2: Node D<br/>(Clockwise next+1)"] end subgraph Consistency["Consistency Levels"] W["Write: W=2<br/>(Ack from 2/3 nodes)"] R["Read: R=2<br/>(Read from 2/3 nodes)"] Note1["W + R > N → Strong Consistency<br/>2 + 2 > 3 ✅"] end Client --> Coordinator Coordinator -->|"hash(key) = 150<br/>→ lands in Node B range"| N2 N2 -->|"Write primary"| Primary Primary -->|"Replicate"| Rep1 Primary -->|"Replicate"| Rep2 style HashRing fill:#e8eaf6,stroke:#283593,stroke-width:2px style Replication fill:#e8f5e9,stroke:#2e7d32 style Consistency fill:#fff8e1,stroke:#f57f17
6. Sequence Diagrams
6.1 HTTP Request Lifecycle
Từ browser gõ URL đến khi render xong — mỗi bước một latency.
sequenceDiagram Customize: Thêm cache layers, thêm CDN, thêm middleware participant B as 🌐 Browser participant DNS as DNS Resolver participant CDN as CDN (Cloudflare) participant LB as Load Balancer participant App as App Server participant Cache as Redis Cache participant DB as Database B->>DNS: 1. DNS Lookup: api.example.com DNS-->>B: 2. IP: 203.0.113.10 (TTL: 300s) B->>CDN: 3. TCP Handshake (SYN → SYN-ACK → ACK) B->>CDN: 4. TLS Handshake (nếu HTTPS) B->>CDN: 5. GET /api/users/123 alt CDN Cache HIT CDN-->>B: 6a. 200 OK (from CDN cache, ~20ms) else CDN Cache MISS CDN->>LB: 6b. Forward request LB->>App: 7. Route to healthy server App->>Cache: 8. GET user:123 alt Cache HIT Cache-->>App: 9a. Data from cache (~1ms) else Cache MISS Cache-->>App: 9b. null App->>DB: 10. SELECT * FROM users WHERE id=123 DB-->>App: 11. Row data (~5ms) App->>Cache: 12. SET user:123 (TTL=300s) end App-->>LB: 13. 200 OK + JSON LB-->>CDN: 14. Response CDN-->>B: 15. Response + Cache-Control header end B->>B: 16. Parse JSON + Render UI Note over B,DB: Tổng latency tốt nhất: ~20ms (CDN hit)<br/>Trung bình: ~50ms (Cache hit)<br/>Xấu nhất: ~200ms (DB query)
6.2 TLS Handshake
Thiết lập kết nối mã hóa — 1-RTT cho TLS 1.3, 2-RTT cho TLS 1.2.
sequenceDiagram Customize: Đổi cipher suite, thêm 0-RTT resumption participant C as 👤 Client (Browser) participant S as 🖥️ Server Note over C,S: TCP Handshake đã hoàn thành C->>S: ClientHello<br/>- Supported TLS versions: [1.3, 1.2]<br/>- Cipher suites: [AES_256_GCM_SHA384, ...]<br/>- Key share: (ECDHE public key)<br/>- SNI: api.example.com S->>C: ServerHello<br/>- Selected: TLS 1.3<br/>- Cipher: AES_256_GCM_SHA384<br/>- Key share: (ECDHE public key) S->>C: EncryptedExtensions S->>C: Certificate (server cert chain) S->>C: CertificateVerify (signature) S->>C: Finished (MAC of handshake) Note over C: Verify certificate chain:<br/>1. Check CA signature<br/>2. Check expiry<br/>3. Check revocation (OCSP)<br/>4. Check hostname match SNI C->>S: Finished (MAC of handshake) Note over C,S: ✅ Handshake complete (1-RTT)<br/>Both sides derive symmetric keys<br/>from ECDHE shared secret<br/>→ All subsequent data encrypted C->>S: 🔒 GET /api/data (encrypted) S->>C: 🔒 200 OK {data} (encrypted)
6.3 Database Transaction with Retry
Xử lý optimistic concurrency — retry khi conflict, tránh data corruption.
sequenceDiagram Customize: Đổi max retry, đổi backoff strategy, đổi isolation level participant A as App Server participant DB as Database participant Log as Audit Log Note over A,DB: Scenario: Transfer $100 from Account A → Account B<br/>Isolation Level: SERIALIZABLE loop Retry (max 3 lần, exponential backoff) A->>DB: BEGIN TRANSACTION A->>DB: SELECT balance FROM accounts WHERE id='A' FOR UPDATE DB-->>A: balance = $500 A->>DB: SELECT balance FROM accounts WHERE id='B' FOR UPDATE DB-->>A: balance = $200 Note over A: Validate: $500 >= $100 ✅ A->>DB: UPDATE accounts SET balance = 400 WHERE id='A' A->>DB: UPDATE accounts SET balance = 300 WHERE id='B' A->>DB: INSERT INTO transactions (from, to, amount) VALUES ('A', 'B', 100) A->>DB: COMMIT alt Commit SUCCESS ✅ DB-->>A: COMMIT OK A->>Log: Log: Transfer A→B $100 SUCCESS Note over A: Break khỏi retry loop else Deadlock / Serialization Failure ❌ DB-->>A: ERROR: could not serialize access A->>DB: ROLLBACK Note over A: Wait: 100ms × 2^attempt<br/>(Exponential backoff)<br/>Attempt 1: 100ms<br/>Attempt 2: 200ms<br/>Attempt 3: 400ms else Max Retries Exceeded ❌ A->>Log: Log: Transfer A→B $100 FAILED after 3 retries A-->>A: Return error to caller end end
Quick Reference: Mermaid Syntax Cheat Sheet
| Syntax | Ý nghĩa | Ví dụ |
|---|---|---|
flowchart TD | Flow chart Top → Down | flowchart LR cho Left → Right |
A[Text] | Rectangle node | A["Node with special chars"] |
A(Text) | Rounded node | A("Rounded") |
A{Text} | Diamond (decision) | A{"Yes or No?"} |
A[(Text)] | Database cylinder | A[("PostgreSQL")] |
A[[Text]] | Subroutine | A[["Queue"]] |
A-->B | Solid arrow | A-->|"Label"|B cho arrow có label |
A-.->B | Dotted arrow | Dùng cho async/optional |
A==>B | Thick arrow | Dùng cho main flow |
A-.-xB | Dotted cross | Dùng cho rejected/blocked |
subgraph Title | Group nodes | subgraph S["Title"] |
style A fill:#fff | Styling | fill, stroke, stroke-width |
sequenceDiagram | Sequence diagram | Dùng cho request/response flows |
participant A as Name | Named participant | participant C as 👤 Client |
A->>B: message | Solid arrow (seq) | ->> solid, -->> dotted |
alt / else / end | Conditional (seq) | Dùng cho branching logic |
loop / end | Loop (seq) | Dùng cho retry logic |
Note over A,B | Note span (seq) | Note over A,B: Explanation |
Tham khảo
- Mermaid.js Official Docs
- Mermaid Live Editor — Test diagram trước khi paste vào Obsidian
- Tuan-01-Scale-From-Zero-To-Millions — Kiến trúc cơ bản
- Tuan-02-Back-of-the-envelope — Ước lượng hệ thống
- Tuan-05-Load-Balancer — Load Balancer chi tiết
- Tuan-06-Cache-Strategy — Cache patterns
- Tuan-07-Database-Sharding-Replication — Sharding & Replication
- Tuan-08-Message-Queue — Message Queue patterns
- Tuan-16-Design-URL-Shortener — URL Shortener case study
- Tuan-17-Design-Chat-System — Chat System case study