Khi chuyển đổi từ một nền tảng nguyên khối (monolith) sang một hệ thống microservice phân tán, câu hỏi khó nhất không phải là “Chúng ta viết code như thế nào?” — mà là “Làm sao để các mảnh ghép di động này nói chuyện với nhau một cách an toàn, và tại sao mỗi ranh giới lại được vẽ chính xác ở vị trí đó?”
Bài viết này là mỏ neo kiến trúc cho toàn bộ series về composable commerce. Nó trình bày bản vẽ hệ thống tổng thể và giải thích lý do đằng sau mỗi ranh giới domain. Để tìm hiểu sâu về từng tầng cụ thể, mỗi phần đều có link dẫn đến bài viết chuyên đề trong series.
6 Domain Nghiệp vụ (Business Domains)
Trước khi vẽ bất kỳ một đường thẳng nào lên biểu đồ, chúng tôi đã khoanh vùng hệ sinh thái dựa trên các nguyên tắc của Thiết kế Hướng Domain (Domain-Driven Design - DDD). Mỗi domain sở hữu cơ sở dữ liệu Postgres riêng. Tuyệt đối không có các câu query chéo giữa các domain. Mọi giao tiếp diễn ra độc quyền thông qua sự kiện (events) hoặc các hợp đồng gRPC rõ ràng.
6 domain và 21 service tương ứng:
| Domain | Services | Sở hữu |
|---|---|---|
| Commerce Flow | Checkout, Order, Payment | Luồng tiền bạc — độ quan trọng cao nhất |
| Product & Content | Catalog, Pricing, Promotion, Search | Đọc nhiều (Read-heavy), độ trễ < 50ms |
| Logistics | Warehouse, Fulfillment, Shipping | Tích hợp với thế giới vật lý |
| Post-Purchase | Returns, Loyalty | Giữ chân khách hàng sau khi giao hàng |
| Identity & Access | Auth, User, Customer | Ranh giới bảo mật giữa nhân viên nội bộ và khách hàng |
| Platform Operations | Gateway, Analytics, Notification | Các tiện ích hạ tầng dùng chung |
Lý do đằng sau việc tách biệt User (Người dùng nội bộ) và Customer (Khách hàng) đáng để nói rõ: nhân viên nội bộ và người mua bên ngoài có pattern truy cập, cấu trúc dữ liệu, và yêu cầu pháp lý hoàn toàn khác biệt. Gộp chung chúng lại sẽ tạo ra một schema không phục vụ tốt cho bên nào cả, và tạo ra một bề mặt rủi ro bảo mật nơi một lỗi RBAC nội bộ có thể làm lộ thông tin cá nhân (PII) của khách hàng.
Để xem chi tiết nhiệm vụ của từng service, xem Bóc tách Hệ sinh thái: Chi tiết Service theo Domain.
Kiến trúc Tổng quan (High-Level Architecture)
graph TD
subgraph "🌐 Clients Bên Ngoài"
FE["Website Khách Hàng (Next.js)"]
ADMIN["Admin Dashboard (React)"]
end
subgraph "🚪 Tầng API Gateway"
GW["Global Gateway<br>(Auth · Rate Limit · Circuit Breaker · BFF)"]
end
subgraph "🔐 Identity & Access"
AUTH["Auth Service<br>(RS256 JWT · OAuth2 · MFA)"]
USR["User Service<br>(Internal RBAC)"]
CUST["Customer Profile<br>(LTV · Segmentation)"]
end
subgraph "📦 Product & Content"
CAT["Catalog (PIM)<br>(EAV → Normalized)"]
PRC["Pricing Rules<br>(Đa tiền tệ · Thuế)"]
PROMO["Promotions<br>(BOGO · Coupons)"]
SEARCH["Search (CQRS)<br>(Elasticsearch)"]
end
subgraph "🛒 Commerce Engine"
CK["Checkout Orchestrator<br>(Khởi tạo Saga)"]
ORD["Order Lifecycle<br>(8-state FSM)"]
PAY["Payment Gateway<br>(Stripe · VNPay · MoMo)"]
end
subgraph "🚚 Logistics & Fulfillment"
WH["Warehouse (WMS)<br>(OCC · Idempotency)"]
FF["Fulfillment<br>(Nhặt · Đóng gói · Giao)"]
SH["Shipping Hub<br>(Chuẩn hóa Carrier)"]
end
subgraph "🎁 Post-Purchase"
RET["Returns (RMA)<br>(Nhập lại kho · Refund gRPC)"]
LR["Loyalty & Rewards<br>(Outbox · Sổ điểm)"]
end
subgraph "📡 Platform Services"
AN["Analytics Engine<br>(Quan sát thụ động)"]
NOTIF["Notification Hub<br>(SendGrid · Twilio)"]
end
%% External traffic
FE & ADMIN --> GW
%% Synchronous paths (solid)
GW --> AUTH & CUST & CAT & SEARCH & CK & ORD
%% gRPC internal calls (dotted)
CK -.->|"gRPC: CreateOrder"| ORD
CK -.->|"gRPC: Authorize"| PAY
RET -.->|"gRPC: Refund"| PAY
%% Async event paths (double)
ORD ==>|"order.confirmed"| WH
ORD ==>|"order.paid"| FF
FF ==>|"fulfillment.completed"| SH
SH ==>|"shipping.delivered"| ORD
ORD ==>|"order.cancelled"| LR & PROMO
CAT ==>|"product.updated"| SEARCH & AN
ORD ==>|"order.created"| AN & CUST & NOTIF
Nét liền = đồng bộ HTTP/gRPC. Nét đôi (==>) = sự kiện bất đồng bộ qua Dapr PubSub.
Giải phẫu Traffic: Ba Luồng Tách biệt
Luồng 1 — Tấm khiên Gateway (Đường Read)
Mọi traffic bên ngoài đều đi vào thông qua API Gateway. Gateway này ép buộc:
- Xác thực JWT — giảm tải cho các service bên dưới; mọi request đều được xác thực ngay ở vùng biên.
- Giới hạn tốc độ (Rate limiting) — dựa trên IP và user, bảo vệ hệ thống khỏi bot cào dữ liệu và các vụ tấn công checkout.
- Circuit breaking (Cầu dao điện) — nếu Catalog service bị suy giảm hiệu năng, Gateway sẽ báo lỗi ngay lập tức (fail fast) thay vì để các request kẹt lại và xếp chồng lên nhau.
Các hành động đọc liên tục (danh sách sản phẩm, tìm kiếm, profile user) được phân giải ở đây với độ trễ dưới 50ms bởi vì Search service duy trì một Elasticsearch CQRS read model đã được index sẵn, và được cập nhật gần như realtime thông qua các sự kiện từ Catalog và Pricing.
Luồng 2 — Checkout Saga (Đường Write)
Đây là luồng quan trọng và phức tạp nhất. Khi khách hàng tiến hành checkout:
sequenceDiagram
participant GW as API Gateway
participant CK as Checkout
participant WH as Warehouse
participant PAY as Payment
participant ORD as Order
participant DAPR as Dapr PubSub
GW->>CK: POST /checkout/confirm
CK->>WH: gRPC: ReserveStock (Giữ hàng TTL 15min)
WH-->>CK: ✅ Đã giữ hàng
CK->>PAY: gRPC: Authorize payment
PAY-->>CK: ✅ Cấp quyền
CK->>ORD: gRPC: CreateOrder
ORD-->>CK: ✅ Đơn #10482 đã tạo
ORD->>DAPR: Publish order.confirmed
Note over DAPR: Async bắt đầu từ đây — Checkout trả về 200 cho user
DAPR-->>WH: Khấu trừ tồn kho vĩnh viễn
DAPR-->>CK: Kích hoạt luồng fulfillment
Việc xử lý thất bại sử dụng cơ chế Compensating Transactions (Giao dịch Bù trừ): nếu Payment service từ chối thẻ ngân hàng sau khi tồn kho đã bị khóa, Checkout service sẽ kích hoạt sự kiện checkout.failed. Warehouse service sẽ lắng nghe sự kiện này và giải phóng hàng đang bị giữ. Không có các giao dịch database kéo dài lê thê, không bị cạn kiệt connection pool dưới tải nặng.
Để xem bản triển khai hoàn chỉnh — bao gồm SQL Optimistic Concurrency Control xử lý race conditions của kho hàng và pattern khóa lũy đẳng — hãy đọc Thiết kế Hệ sinh thái Thương mại điện tử 21 Microservices với Golang & DDD.
Luồng 3 — Lưới sự kiện Async (Post-Checkout)
Một khi sự kiện order.paid bắn vào lưới sự kiện Dapr, việc thực thi đồng bộ chấm dứt từ góc nhìn của khách hàng. Các service phía sau sẽ chạy song song:
- Warehouse khấu trừ vĩnh viễn tồn kho đã giữ.
- Fulfillment kích hoạt luồng nhặt-đóng gói-giao hàng.
- Analytics tăng doanh thu trên dashboard.
- Customer cập nhật LTV và lịch sử mua hàng.
- Notification bắn email/SMS xác nhận đơn hàng.
Sự cố ở bất kỳ service nào trong nhóm này (Notification không gọi được, Analytics bị chậm) hoàn toàn không ảnh hưởng đến trải nghiệm checkout của khách hàng hay việc ghi nhận đơn hàng. Sự cách ly được ép buộc ở tầng hạ tầng — mỗi service sở hữu database riêng.
Để hiểu về các quy ước đặt tên sự kiện Dapr, Dead Letter Queue, và thiết kế lũy đẳng, hãy xem Làm chủ Kiến trúc Hướng Sự kiện với Dapr Pub/Sub.
Pattern CQRS cho Tìm kiếm (Search)
Tính năng tìm kiếm sản phẩm xứng đáng có một mục riêng vì nó giải quyết vấn đề mà mô hình EAV của Magento gặp khó khi scale: việc JOIN 5+ bảng tại thời điểm query chỉ để hiển thị một danh sách sản phẩm là quá chậm.
Search service duy trì một mô hình đọc bằng Elasticsearch được build lại từ các sự kiện, chứ không query từ database gốc:
flowchart LR
CAT["Catalog Service<br>(source of truth)"] -- "catalog.product.updated" --> DAPR[Dapr PubSub]
PRC["Pricing Service"] -- "pricing.price.updated" --> DAPR
WH["Warehouse Service"] -- "warehouse.stock.changed" --> DAPR
DAPR --> SW["Search Worker<br>(subscriber)"]
SW --> ES[("Elasticsearch<br>Flat documents")]
ES -- "đọc < 50ms" --> GW["API Gateway"]
Khi team Catalog cập nhật một sản phẩm, họ ghi vào database Postgres của riêng họ và publish sự kiện catalog.product.updated. Search Worker nhận sự kiện đó và build lại document Elasticsearch cho SKU này — gộp cả giá mới nhất từ Pricing service và tồn kho mới nhất từ Warehouse service vào một document phẳng (flat document) duy nhất.
Không cần cron jobs. Không cần reindex toàn bộ. Không có khoảng trễ dữ liệu cũ. Catalog, Pricing, và Warehouse sở hữu dữ liệu domain của chúng; Search sở hữu bản đồ chiếu đọc (read projection).
Tầng Triển khai (Deployment Layer)
Toàn bộ nền tảng 21-service được deploy thông qua GitOps sử dụng ArgoCD và Kustomize overlays. Không có bất kỳ kỹ sư nào được phép chạm trực tiếp vào production cluster — mọi thay đổi phải chảy qua Git, và ArgoCD ép buộc việc chống trôi lệch cấu hình thông qua tính năng selfHeal: true trên mọi Application ở production.
Để xem toàn bộ thiết lập GitOps — bao gồm pattern App-of-Apps, cấu trúc Kustomize base/overlay, và sổ tay rollback bằng lệnh git revert — hãy xem GitOps at Scale: Điều phối 21 Microservices với Kubernetes & ArgoCD.
Tại sao lại không phải là Distributed Monolith?
Chế độ thất bại phổ biến nhất khi các team chuyển sang microservices là việc xây dựng ra một Distributed Monolith (Kiến trúc nguyên khối phân tán): các service được deploy riêng rẽ nhưng lại dính chặt vào nhau thông qua các chuỗi HTTP đồng bộ, chung database, hoặc chung pipeline deploy.
Kiến trúc phía trên tránh được điều này thông qua 3 quy tắc thép:
- Tuyệt đối không truy cập database chéo domain. Nếu Order service cần dữ liệu sản phẩm, nó hoặc là phải lưu một bản copy chuẩn hóa, hoặc là gọi gRPC API của Catalog service.
- Không có lệnh gọi đồng bộ nào nằm trong luồng event bất đồng bộ. Một khi sự kiện bước vào lưới Dapr, nó được xử lý độc lập. Các event consumers tuyệt đối không call-back (gọi ngược lại) producer.
- Không dùng chung pipeline deployment — mỗi service có ArgoCD Application riêng, đường dẫn registry riêng, và chu kỳ release riêng. Một lỗi ở Loyalty service không thể khóa chết việc release của Checkout.
Để xem toàn bộ lập luận về việc khi nào sự phức tạp này là xứng đáng — và khi nào thì không — hãy đọc Vì sao bạn nên Migrate từ Magento sang Microservices (Và khi nào thì không nên).
Điều hướng Series
| Bài viết | Nội dung bao trùm |
|---|---|
| Bài viết này | Bản vẽ toàn hệ thống, ranh giới domain, luồng traffic |
| Chi tiết Service theo Domain | Trách nhiệm và quyền sở hữu của từng service |
| Golang DDD Deep-Dive | Kratos clean arch, triển khai Saga, OCC, lũy đẳng |
| Hướng sự kiện với Dapr | Quy ước đặt tên, Saga pattern, thiết kế DLQ |
| GitOps với ArgoCD | App-of-Apps, Kustomize overlays, sổ tay rollback |
| Magento sang Microservices: Tại sao | Khung quyết định: khi nào nên migrate, khi nào không |
| Magento sang Microservices: Như thế nào | Strangler Fig 3 giai đoạn, Debezium CDC, đồng bộ 2 chiều |