9.9 KiB
9.9 KiB
Kiến Trúc Promotion Service
Tổng Quan
Promotion Service quản lý chiến dịch khuyến mãi, Voucher và Gift Card cho GoodGo Platform.
┌─────────────────────────────────────────────────────────────┐
│ Promotion Service │
├─────────────────────────────────────────────────────────────┤
│ API Layer (Controllers, CQRS) │
├─────────────────────────────────────────────────────────────┤
│ Domain Layer (Campaign, Voucher Aggregates) │
├─────────────────────────────────────────────────────────────┤
│ Infrastructure Layer (EF Core, Repositories, Events) │
├─────────────────────────────────────────────────────────────┤
│ PostgreSQL Database │
└─────────────────────────────────────────────────────────────┘
Decoupling Concept
graph TB
subgraph "Voucher Creation"
BA["Backing Asset<br/>(Tài sản đảm bảo)"] --> V["Voucher"]
AM["Acquisition Method<br/>(Phương thức sở hữu)"] --> V
end
subgraph "Backing Assets"
BA1["CURRENCY (VND, USD)"]
BA2["POINT (PPoint)"]
end
subgraph "Acquisition Methods"
AM1["FREE"]
AM2["EXCHANGE_POINTS"]
AM3["PURCHASE"]
end
Các Pattern Kiến Trúc
Domain-Driven Design (DDD)
- Aggregates: Campaign, Voucher (nested in Campaign)
- Entities: Voucher, Redemption
- Value Objects: AssetType, AcquisitionType
- Domain Events: CampaignCreated, VoucherClaimed, VoucherRedeemed
CQRS Pattern
Commands (Ghi) Queries (Đọc)
│ │
▼ ▼
CreateCampaignCommand GetCampaignQuery
ActivateCampaignCommand GetCampaignsQuery
ClaimVoucherCommand GetVoucherQuery
ExchangeVoucherCommand GetUserVouchersQuery
RedeemVoucherCommand ValidateVoucherQuery
CancelCampaignCommand GetCampaignStatisticsQuery
Domain Model
Campaign Aggregate
classDiagram
class Campaign {
+Guid Id
+Guid MerchantId
+string Name
+AssetType BackingAssetType
+string BackingAssetCode
+decimal FaceValue
+AcquisitionType AcquisitionType
+decimal AcquisitionPrice
+Guid EscrowHoldId
+int TotalVouchers
+int IssuedVouchers
+CampaignStatus Status
+List~Voucher~ Vouchers
+Activate()
+Pause()
+Cancel()
+IssueVoucher(userId)
}
class Voucher {
+Guid Id
+string Code
+Guid? OwnerId
+VoucherStatus Status
+decimal RemainingValue
+DateTime? ClaimedAt
+DateTime? RedeemedAt
+Claim(userId)
+Redeem(amount)
+Expire()
}
class Redemption {
+Guid Id
+Guid VoucherId
+Guid? OrderId
+decimal AmountUsed
+decimal AmountRefunded
+DateTime RedeemedAt
}
Campaign "1" --> "*" Voucher
Voucher "1" --> "*" Redemption
Enums
public enum AssetType
{
Currency = 1, // VND, USD
Point = 2 // PPoint
}
public enum AcquisitionType
{
Free = 1, // Tặng miễn phí
ExchangePoints = 2, // Đổi điểm
Purchase = 3 // Mua bằng tiền
}
public enum CampaignStatus
{
Draft = 1,
Active = 2,
Paused = 3,
Completed = 4,
Cancelled = 5
}
public enum VoucherStatus
{
Available = 1,
Claimed = 2,
PartiallyRedeemed = 3,
FullyRedeemed = 4,
Expired = 5
}
Database Schema
Các Bảng
| Bảng | Mô Tả |
|---|---|
campaigns |
Chiến dịch của Merchant |
vouchers |
Mã voucher/gift card |
redemptions |
Lịch sử sử dụng |
Schema Chi Tiết
CREATE TABLE campaigns (
id UUID PRIMARY KEY,
merchant_id UUID NOT NULL,
name VARCHAR(255) NOT NULL,
backing_asset_type INT NOT NULL,
backing_asset_code VARCHAR(10) NOT NULL,
face_value DECIMAL(18,2) NOT NULL,
acquisition_type INT NOT NULL,
acquisition_price DECIMAL(18,2) DEFAULT 0,
escrow_hold_id UUID,
total_vouchers INT NOT NULL,
issued_vouchers INT DEFAULT 0,
start_date TIMESTAMP NOT NULL,
end_date TIMESTAMP NOT NULL,
status INT NOT NULL,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
CREATE TABLE vouchers (
id UUID PRIMARY KEY,
campaign_id UUID NOT NULL REFERENCES campaigns(id),
code VARCHAR(20) UNIQUE NOT NULL,
owner_id UUID,
status INT NOT NULL,
remaining_value DECIMAL(18,2) NOT NULL,
claimed_at TIMESTAMP,
redeemed_at TIMESTAMP,
expires_at TIMESTAMP,
created_at TIMESTAMP DEFAULT NOW()
);
CREATE TABLE redemptions (
id UUID PRIMARY KEY,
voucher_id UUID NOT NULL REFERENCES vouchers(id),
order_id UUID,
amount_used DECIMAL(18,2) NOT NULL,
amount_refunded DECIMAL(18,2) DEFAULT 0,
redeemed_at TIMESTAMP DEFAULT NOW()
);
Indexes Chính
IX_Campaigns_MerchantId- Campaigns theo merchantIX_Vouchers_CampaignId- Vouchers theo campaignIX_Vouchers_Code- Tra cứu theo mãIX_Vouchers_OwnerId- Vouchers theo user
Tích Hợp Giữa Các Service
Kiến Trúc Tổng Quan
graph TB
APP["Mobile/Web App"]
PS["Promotion Service"]
WS["Wallet Service"]
MS["Merchant Service"]
MQ["RabbitMQ"]
APP --> PS
PS --> WS
PS --> MS
PS --> MQ
WS --> MQ
style PS fill:#90EE90
Tích Hợp Wallet Service (Escrow)
Promotion Service ──────► Wallet Service
│ │
│ Hold/Execute/ │
│ Release │
▼ ▼
Campaign Escrow Balance
Created Locked/Released
API Calls:
| Action | Wallet API | Mô Tả |
|---|---|---|
| Tạo Campaign | POST /holds |
Lock tiền/điểm vào escrow |
| Sử dụng Voucher | POST /holds/{id}/execute |
Trừ tiền từ escrow |
| Hoàn dư | POST /holds/{id}/release |
Trả lại tiền dư |
| Hủy Campaign | POST /holds/{id}/release |
Hoàn toàn bộ escrow |
Luồng Xử Lý
1. Tạo Campaign
1. Merchant → POST /api/v1/campaigns
2. Validate thông tin campaign
3. Gọi Wallet Service: Hold(amount, merchantWallet)
4. Lưu Campaign với escrowHoldId
5. Sinh voucher codes
6. Response → Campaign created
2. User Lấy Voucher (Free)
1. User → POST /api/v1/vouchers/claim
2. Validate campaign đang active
3. Kiểm tra user chưa có voucher này
4. Cập nhật voucher.ownerId = userId
5. Response → Voucher code
3. User Sử Dụng Voucher
1. Checkout → POST /api/v1/vouchers/redeem
2. Validate voucher còn hiệu lực
3. Tính toán: amountUsed = min(faceValue, orderAmount)
4. Tính toán: surplus = faceValue - amountUsed
5. Gọi Wallet: Execute(holdId, amountUsed)
6. Nếu surplus > 0: Gọi Wallet: Release(holdId, surplus)
7. Cập nhật voucher.status = Redeemed
8. Lưu redemption history
9. Response → Redemption confirmed
Integration Events
Outgoing Events
// Khi campaign được tạo
public record CampaignCreatedIntegrationEvent(
Guid CampaignId,
Guid MerchantId,
string AssetCode,
decimal TotalEscrow);
// Khi voucher được sử dụng
public record VoucherRedeemedIntegrationEvent(
Guid VoucherId,
Guid CampaignId,
Guid UserId,
Guid? OrderId,
decimal AmountUsed,
decimal AmountRefunded);
Incoming Events
// Từ Wallet Service
public record EscrowHeldIntegrationEvent(
Guid HoldId,
Guid WalletId,
Guid ReferenceId,
decimal Amount);
public record EscrowExecutedIntegrationEvent(
Guid HoldId,
decimal ExecutedAmount,
string ExecutionRef);
Bảo Mật
Xác Thực
- JWT Bearer token từ IAM Service
- Merchant context từ Merchant Service
Phân Quyền
- Merchant chỉ quản lý campaigns của mình
- User chỉ xem/sử dụng vouchers của mình
- Admin có full access
Validation
- FluentValidation cho tất cả commands
- Rate limiting cho claim/redeem APIs
Triển Khai
Docker Compose
promotion-service:
build:
context: ../..
dockerfile: services/promotion-service-net/Dockerfile
environment:
- DATABASE_URL=${PROMOTION_DATABASE_URL}
- WALLET_SERVICE_URL=http://wallet-service:5000
- RABBITMQ_URL=${RABBITMQ_URL}
- JWT_AUTHORITY=${IAM_SERVICE_URL}
labels:
- traefik.http.routers.promotion.rule=PathPrefix(`/api/v1/campaigns`) || PathPrefix(`/api/v1/vouchers`)
Health Checks
| Endpoint | Kiểm Tra |
|---|---|
/health/live |
Service đang chạy |
/health/ready |
Database + RabbitMQ + Wallet Service |
/health |
Trạng thái đầy đủ |
Giám Sát
Metrics
- Số lượng campaigns active
- Vouchers issued/redeemed per campaign
- Tỷ lệ redemption
- Thời gian response
Logging
- Serilog structured logging
- Correlation IDs cho tracing
- Audit log cho financial transactions