Files
pos-system/services/promotion-service-net/docs/vi/ARCHITECTURE.md

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 merchant
  • IX_Vouchers_CampaignId - Vouchers theo campaign
  • IX_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