8.7 KiB
8.7 KiB
Wallet Service Architecture
Overview
The Wallet Service manages digital wallets and loyalty point accounts for the GoodGo Platform.
flowchart TB
subgraph API["🌐 API Layer"]
Controllers["Controllers"]
Commands["Commands"]
Queries["Queries"]
end
subgraph Domain["💎 Domain Layer"]
Wallet["Wallet Aggregate"]
PointAccount["PointAccount Aggregate"]
end
subgraph Infra["⚙️ Infrastructure Layer"]
Repos["Repositories"]
EF["EF Core"]
end
subgraph DB["💾 PostgreSQL"]
Tables["Tables"]
end
API --> Domain
Domain --> Infra
Infra --> DB
style API fill:#3498DB,color:#ECF0F1,stroke:#2980B9,stroke-width:3px
style Domain fill:#8E44AD,color:#ECF0F1,stroke:#7D3C98,stroke-width:2px
style Infra fill:#E67E22,color:#ECF0F1,stroke:#D35400,stroke-width:2px
style DB fill:#34495E,color:#ECF0F1,stroke:#2C3E50,stroke-width:2px
Architecture Patterns
Domain-Driven Design (DDD)
- Aggregates: Wallet, PointAccount
- Entities: WalletTransaction, PointTransaction, HoldItem, WalletItem
- Value Objects: Money, CurrencyType
- Domain Events: WalletCreated, BalanceChanged, PointsEarned, EscrowHeld, EscrowExecuted
CQRS Pattern
flowchart LR
subgraph Commands["Commands (Write)"]
C1["CreateWallet"]
C2["Deposit/Withdraw"]
C3["Exchange"]
C4["Hold/Execute/Release"]
C5["EarnPoints/SpendPoints"]
C6["Admin Commands"]
end
subgraph Queries["Queries (Read)"]
Q1["GetWallet"]
Q2["GetTransactions"]
Q3["GetPointAccount"]
Q4["GetStatistics"]
Q5["Admin Queries"]
end
style Commands fill:#27AE60,color:#ECF0F1,stroke:#229954,stroke-width:2px
style Queries fill:#3498DB,color:#ECF0F1,stroke:#2980B9,stroke-width:2px
Domain Model
Wallet Aggregate
classDiagram
class Wallet {
+Guid Id
+Guid UserId
+WalletStatus Status
+CurrencyType DefaultCurrency
+List~WalletItem~ Items
+List~HoldItem~ Holds
+Deposit(amount, currency, desc, ref)
+Withdraw(amount, currency, desc, ref)
+Exchange(fromAmount, fromCurrency, toCurrency)
+Hold(amount, currency, refType, refId, desc)
+ExecuteHold(holdId, amount, ref)
+ReleaseHold(holdId, amount)
+Freeze()
+Unfreeze()
}
class WalletItem {
+Guid Id
+CurrencyType Currency
+decimal Balance
+decimal HeldBalance
+decimal AvailableBalance
}
class HoldItem {
+Guid Id
+decimal OriginalAmount
+decimal RemainingAmount
+decimal ExecutedAmount
+decimal ReleasedAmount
+HoldStatus Status
+string ReferenceType
+Guid ReferenceId
+Execute(amount, ref)
+Release(amount)
+Cancel()
}
class WalletTransaction {
+Guid Id
+TransactionType Type
+decimal Amount
+CurrencyType Currency
+decimal BalanceAfter
+string Description
+DateTime CreatedAt
}
class CurrencyType {
+int Id
+string Name
+decimal BaseExchangeRate
+VND
+USD
+PPoint
+GetExchangeRateTo(currency)
+ConvertTo(amount, currency)
}
Wallet "1" --> "*" WalletItem
Wallet "1" --> "*" HoldItem
Wallet "1" --> "*" WalletTransaction
WalletItem --> CurrencyType
HoldItem --> CurrencyType
PointAccount Aggregate
classDiagram
class PointAccount {
+Guid Id
+Guid UserId
+long TotalPoints
+long AvailablePoints
+long PendingPoints
+EarnPoints(points, source, desc, expires)
+SpendPoints(points, source, desc)
+AdjustPoints(points, source, desc)
}
class PointTransaction {
+Guid Id
+PointTransactionType Type
+long Points
+long BalanceAfter
+string Source
+DateTime? ExpiresAt
}
PointAccount "1" --> "*" PointTransaction
Database Schema
Tables
| Table | Description |
|---|---|
Wallets |
User wallet accounts |
WalletItems |
Currency balances per wallet |
WalletTransactions |
Wallet transaction history |
HoldItems |
Escrow holds |
PointAccounts |
User point accounts |
PointTransactions |
Point transaction history |
Key Indexes
IX_Wallets_UserId- Fast lookup by userIX_WalletItems_WalletId_CurrencyTypeId- Balance by currencyIX_WalletTransactions_WalletId- Transaction historyIX_HoldItems_WalletId_Status- Active holdsIX_PointAccounts_UserId- Fast lookup by user
API Flow
Deposit Flow
sequenceDiagram
participant C as Client
participant API as Controller
participant H as Handler
participant W as Wallet
participant DB as Database
C->>API: POST /wallets/{userId}/deposit
API->>H: DepositCommand
H->>W: wallet.Deposit(amount)
W->>W: Create Transaction
W->>W: Raise DomainEvent
H->>DB: SaveChanges
DB-->>API: Success
API-->>C: Updated Balance
Escrow Flow
sequenceDiagram
participant C as Client
participant API as Controller
participant W as Wallet
participant H as HoldItem
C->>API: POST /holds (Create)
API->>W: wallet.Hold(amount)
W->>H: Create HoldItem
W-->>C: HoldId
C->>API: POST /holds/{id}/execute
API->>W: wallet.ExecuteHold(id, amount)
W->>H: hold.Execute(amount)
H-->>C: Executed Amount
C->>API: POST /holds/{id}/release
API->>W: wallet.ReleaseHold(id, amount)
W->>H: hold.Release(amount)
H-->>C: Released Amount
Currency Exchange Flow
sequenceDiagram
participant C as Client
participant API as Controller
participant W as Wallet
participant CT as CurrencyType
C->>API: POST /exchange
API->>W: wallet.Exchange(100 USD, VND)
W->>CT: GetExchangeRate(USD → VND)
CT-->>W: Rate = 25000
W->>W: Withdraw 100 USD
W->>W: Deposit 2,500,000 VND
W->>W: Raise WalletExchangedEvent
W-->>C: Exchange Result
Domain Events
| Event | Trigger | Data |
|---|---|---|
WalletCreatedDomainEvent |
Wallet creation | WalletId, UserId |
WalletBalanceChangedDomainEvent |
Deposit/Withdraw | WalletId, Amount, Type |
WalletExchangedDomainEvent |
Currency exchange | FromCurrency, ToCurrency, Rate |
EscrowHeldDomainEvent |
Hold creation | HoldId, Amount, RefType |
EscrowExecutedDomainEvent |
Hold execution | HoldId, Amount |
EscrowReleasedDomainEvent |
Hold release | HoldId, Amount |
PointsEarnedDomainEvent |
Points earned | AccountId, Points |
PointsSpentDomainEvent |
Points spent | AccountId, Points |
Inter-Service Communication
IAM Service Integration
flowchart LR
WS["🔐 Wallet Service"] --> IAM["👤 IAM Service"]
IAM --> UV["User Validation"]
IAM --> JV["JWT Verification"]
style WS fill:#3498DB,color:#ECF0F1,stroke:#2980B9,stroke-width:2px
style IAM fill:#8E44AD,color:#ECF0F1,stroke:#7D3C98,stroke-width:2px
style UV fill:#27AE60,color:#ECF0F1,stroke:#229954,stroke-width:2px
style JV fill:#27AE60,color:#ECF0F1,stroke:#229954,stroke-width:2px
Authentication Flow
- Client sends JWT token in Authorization header
- Wallet Service validates JWT with IAM Service
- Extract userId from JWT claims
- Process wallet operation for user
Deployment
Docker Compose Configuration
wallet-service:
build:
context: ../..
dockerfile: services/wallet-service-net/Dockerfile
environment:
- DATABASE_URL=${WALLET_DATABASE_URL}
- JWT_AUTHORITY=${IAM_SERVICE_URL}
labels:
- traefik.http.routers.wallet.rule=PathPrefix(`/api/v1/wallets`)
Health Checks
| Endpoint | Check |
|---|---|
/health/live |
Service is running |
/health/ready |
Database connected |
/health |
Full status |
Security
Authentication
- JWT Bearer token validation
- IAM Service integration
Authorization
- User can only access own wallet
- Admin endpoints require Admin/SuperAdmin role
Data Protection
- All amounts stored with precision
- Transaction audit trail
- Soft delete for wallets
Performance
Optimizations
- Connection pooling (EF Core)
- Index on frequent queries
- Pagination for transaction history
Scaling
- Horizontal scaling with load balancer
- Read replicas for queries
- Redis caching (future)
Monitoring
Metrics
- Request duration
- Transaction counts
- Error rates
Logging
- Serilog structured logging
- Correlation IDs for tracing
- Seq/Loki integration