This commit is contained in:
Ho Ngoc Hai
2026-05-23 18:37:02 +07:00
parent f15d91ee29
commit 76d75c753b
3993 changed files with 403 additions and 0 deletions

View File

@@ -0,0 +1,372 @@
---
name: multi-vertical-architecture
description: Multi-vertical Architecture patterns cho đa ngành hàng (Retail, F&B, Services). Use for Order Service orchestration, Strategy Pattern, Satellite Services, và Shop feature configuration.
compatibility: ".NET 10, PostgreSQL, JSONB"
metadata:
author: Velik Ho
version: "1.0"
references: "Clean Architecture, Domain-Driven Design"
---
# Multi-vertical Architecture / Kiến Trúc Đa Ngành Hàng
## When to Use This Skill / Khi Nào Sử Dụng
Use this skill when:
- Thiết kế hệ thống hỗ trợ nhiều ngành kinh doanh (Retail, F&B, Services)
- Tạo Order Service với Strategy Pattern để xử lý đơn hàng đa loại
- Thêm Vertical Engine mới (Inventory, Booking, F&B Engine...)
- Cấu hình Shop features và business settings động
## Core Principle / Nguyên Tắc Cốt Lõi
> **Merchant Service quản lý "AI" (Identity) và "LUẬT" (Configuration), còn "CÁCH" (Execution) được ủy quyền cho các service vệ tinh.**
## Architecture Layers / Các Tầng Kiến Trúc
```
┌─────────────────────────────────────────────────────────┐
│ 📱 Client Layer │
│ (POS, Web Dashboard, Mobile App) │
└─────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────┐
│ ⚡ API Gateway (Traefik) │
└─────────────────────────────────────────────────────────┘
┌───────────────────┼───────────────────┐
▼ ▼ ▼
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
│ 🟡 Core Layer │ │ 🔴 Orchestration│ │ 🔵 Vertical │
│ │ │ Layer │ │ Engines │
│ • IAM │ │ • Catalog │ │ • Inventory │
│ • Merchant │ │ • Order │ │ • Booking │
│ • Wallet │ │ │ │ • F&B Engine │
└───────────────┘ └───────────────┘ └───────────────┘
```
### Layer Responsibilities
| Layer | Trách Nhiệm | Services |
|-------|-------------|----------|
| **Core** | Identity & Configuration | IAM, Merchant, Wallet |
| **Orchestration** | Business logic coordination | Catalog, Order |
| **Vertical Engines** | Domain-specific execution | Inventory, Booking, F&B |
## Key Patterns / Mẫu Chính
### 1. Shop Feature Flags
Sử dụng JSONB để lưu cấu hình động:
```csharp
// Value Object mapping với JSONB
public record ShopFeatures(
bool HasInventory = false, // Bật Inventory Service
bool HasBooking = false, // Bật Booking Service
bool HasTables = false, // Bật quản lý bàn
bool HasKitchen = false, // Bật KDS
bool HasShipping = false, // Bật vận chuyển
bool HasDelivery = false // Bật giao hàng
);
// Entity sử dụng
public class Shop : Entity, IAggregateRoot
{
public BusinessCategory Category { get; private set; }
public ShopFeatures Features { get; private set; }
public JsonDocument? Settings { get; private set; }
}
```
### 2. Business Category
```csharp
public enum BusinessCategory
{
Retail = 1, // Bán lẻ hàng hóa
FoodBeverage = 2, // Nhà hàng, quán cafe
Services = 3, // Spa, salon, phòng khám
Other = 99 // Hybrid
}
```
### 3. Strategy Pattern for Order Processing
**Interface:**
```csharp
public interface ILineItemStrategy
{
ProductType SupportedType { get; }
Task ValidateAsync(OrderItem item, Guid shopId);
Task ExecuteAsync(OrderItem item, Guid shopId);
}
```
**Retail Strategy:**
```csharp
public class RetailStrategy : ILineItemStrategy
{
private readonly IInventoryService _inventory;
public ProductType SupportedType => ProductType.Physical;
public async Task ValidateAsync(OrderItem item, Guid shopId)
{
// Gọi Inventory Service: Kiểm tra tồn kho
var stock = await _inventory.CheckStockAsync(item.ProductId, shopId);
if (stock.Available < item.Quantity)
throw new InsufficientStockException(item.ProductId);
}
public async Task ExecuteAsync(OrderItem item, Guid shopId)
{
// Gọi Inventory Service: Trừ tồn kho
await _inventory.DeductStockAsync(item.ProductId, item.Quantity, shopId);
}
}
```
**Service Strategy (Spa/Salon):**
```csharp
public class ServiceStrategy : ILineItemStrategy
{
private readonly IBookingService _booking;
public ProductType SupportedType => ProductType.Service;
public async Task ValidateAsync(OrderItem item, Guid shopId)
{
// Kiểm tra slot + nhân viên khả dụng
var available = await _booking.CheckAvailabilityAsync(
item.ServiceId,
item.RequestedTime,
shopId);
if (!available)
throw new SlotNotAvailableException(item.RequestedTime);
}
public async Task ExecuteAsync(OrderItem item, Guid shopId)
{
// Tạo appointment
await _booking.CreateAppointmentAsync(item, shopId);
}
}
```
**F&B Strategy:**
```csharp
public class FnbStrategy : ILineItemStrategy
{
private readonly IEventBus _eventBus;
public ProductType SupportedType => ProductType.PreparedFood;
public async Task ExecuteAsync(OrderItem item, Guid shopId)
{
// Bắn event xuống bếp
await _eventBus.PublishAsync(new KitchenTicketCreated
{
OrderItemId = item.Id,
ProductName = item.ProductName,
Quantity = item.Quantity,
Notes = item.Notes,
Station = item.KitchenStation
});
}
}
```
### 4. Order Handler with Strategy Factory
```csharp
public class CreateOrderHandler : IRequestHandler<CreateOrderCommand, OrderResponse>
{
private readonly IEnumerable<ILineItemStrategy> _strategies;
public async Task<OrderResponse> Handle(CreateOrderCommand request, CancellationToken ct)
{
var order = Order.Create(request.ShopId, request.CustomerId);
// Phase 1: Validate all items
foreach (var item in request.Items)
{
var strategy = _strategies.First(s => s.SupportedType == item.ProductType);
await strategy.ValidateAsync(item, request.ShopId);
order.AddItem(item);
}
// Phase 2: Process payment
await ProcessPaymentAsync(order);
// Phase 3: Execute all items
foreach (var item in order.Items)
{
var strategy = _strategies.First(s => s.SupportedType == item.ProductType);
await strategy.ExecuteAsync(item, request.ShopId);
}
return order.ToResponse();
}
}
```
## Database Patterns / Mẫu Database
### JSONB Columns for Dynamic Config
```sql
ALTER TABLE shops
ADD COLUMN features_config JSONB DEFAULT '{}',
ADD COLUMN vertical_settings JSONB DEFAULT '{}',
ADD COLUMN business_category VARCHAR(50) DEFAULT 'Other';
-- GIN index for JSONB queries
CREATE INDEX idx_shops_features ON shops USING GIN (features_config);
```
### EF Core JSONB Configuration
```csharp
public class ShopConfiguration : IEntityTypeConfiguration<Shop>
{
public void Configure(EntityTypeBuilder<Shop> builder)
{
builder.Property(s => s.Features)
.HasColumnName("features_config")
.HasColumnType("jsonb")
.HasConversion(
v => JsonSerializer.Serialize(v, JsonSerializerOptions.Default),
v => JsonSerializer.Deserialize<ShopFeatures>(v, JsonSerializerOptions.Default)!
);
builder.Property(s => s.Settings)
.HasColumnName("vertical_settings")
.HasColumnType("jsonb");
}
}
```
## Vertical Engines Overview / Tổng Quan Engines
### Inventory Service
| Feature | Retail | F&B |
|---------|--------|-----|
| Stock tracking | ✅ Thành phẩm | ✅ Nguyên liệu |
| Deduction logic | 1:1 (Bán 1 trừ 1) | Recipe-based (Định lượng) |
| Low stock alerts | ✅ | ✅ |
### Booking Service
| Entity | Mô Tả |
|--------|-------|
| StaffSchedule | Lịch làm việc nhân viên |
| Resource | Tài nguyên (Giường, Phòng) |
| Appointment | Cuộc hẹn |
| TimeSlot | Slot thời gian khả dụng |
### F&B Engine
| Entity | Mô Tả |
|--------|-------|
| Table | Sơ đồ bàn |
| Session | Phiên ăn (khách ngồi tại bàn) |
| KitchenTicket | Phiếu bếp cho KDS |
| Recipe | Định lượng nguyên liệu |
## Common Mistakes / Lỗi Thường Gặp
### 1. Đặt logic nghiệp vụ trong Core Layer
```csharp
// ❌ BAD: Logic inventory trong Merchant Service
public class ShopService
{
public async Task<bool> ProcessSale(Guid productId, int quantity)
{
// Logic tồn kho không thuộc về Merchant Service!
var stock = await _db.Stocks.FindAsync(productId);
stock.Quantity -= quantity;
}
}
// ✅ GOOD: Merchant Service chỉ trả về cấu hình
public class ShopService
{
public async Task<ShopFeatures> GetShopFeaturesAsync(Guid shopId)
{
var shop = await _shopRepository.GetByIdAsync(shopId);
return shop.Features; // Order Service sẽ gọi Inventory Service
}
}
```
### 2. Dùng if-else thay vì Strategy Pattern
```csharp
// ❌ BAD: If-else spaghetti
public async Task ProcessOrder(Order order)
{
foreach (var item in order.Items)
{
if (item.Type == "Physical")
await _inventory.DeductStock(item);
else if (item.Type == "Service")
await _booking.CreateAppointment(item);
else if (item.Type == "PreparedFood")
await _kitchen.CreateTicket(item);
// Thêm ngành mới = thêm else if
}
}
// ✅ GOOD: Strategy Pattern
public async Task ProcessOrder(Order order)
{
foreach (var item in order.Items)
{
var strategy = _strategies.First(s => s.SupportedType == item.ProductType);
await strategy.ExecuteAsync(item, order.ShopId);
// Thêm ngành mới = thêm Strategy class mới
}
}
```
### 3. Không sử dụng Feature Flags
```csharp
// ❌ BAD: Gọi service không kiểm tra feature
public async Task ProcessOrder(Order order)
{
await _inventory.DeductStock(order); // Sẽ lỗi nếu shop không có Inventory!
}
// ✅ GOOD: Kiểm tra feature trước
public async Task ProcessOrder(Order order)
{
var features = await _merchantService.GetShopFeaturesAsync(order.ShopId);
if (features.HasInventory)
await _inventory.DeductStock(order);
}
```
## Quick Reference / Tham Chiếu Nhanh
| Concept | Pattern | Áp Dụng |
|---------|---------|---------|
| Cấu hình động | JSONB + Value Object | ShopFeatures, Settings |
| Xử lý đa loại | Strategy Pattern | ILineItemStrategy |
| Giao tiếp service | Integration Events | KitchenTicketCreated |
| Mở rộng ngành | Satellite Architecture | Vertical Engines |
## Resources / Tài Nguyên
- [Implementation Plan](file:///Users/velikho/.gemini/antigravity/brain/fc592f5c-fd0a-4a19-87d0-d09926a1cdde/implementation_plan.md)
- [Architecture Doc (EN)](file:///Users/velikho/Desktop/WORKING/Base/docs/en/architecture/multi-vertical-architecture.md)
- [Architecture Doc (VI)](file:///Users/velikho/Desktop/WORKING/Base/docs/vi/architecture/multi-vertical-architecture.md)
- [Domain-Driven Design](../domain-driven-design/SKILL.md)
- [CQRS + MediatR](../cqrs-mediatr/SKILL.md)
- [Inter-Service Communication](../inter-service-communication/SKILL.md)
- [Repository Pattern](../repository-pattern/SKILL.md)