# Kiến Trúc Membership Service

## Tổng Quan Hệ Thống

Membership Service quản lý dữ liệu membership của user bao gồm **Level**, **Experience (EXP)**, và **Preferences**. Profile information được lưu tại IAM Service.

```mermaid
graph TB
    subgraph Client["Client Layer"]
        WEB["🌐 Web App"]
        MOBILE["📱 Mobile App"]
    end
    
    subgraph Gateway["API Gateway"]
        TRAEFIK["⚡ Traefik"]
    end
    
    subgraph Services["Microservices"]
        IAM["🔐 IAM Service<br/>(User & Profile)"]
        MEMBER["👥 Membership Service<br/>(Level, EXP, Preferences)"]
        STORAGE["📦 Storage Service"]
        WALLET["💰 Wallet Service"]
    end
    
    subgraph Data["Data Layer"]
        PG[("🐘 PostgreSQL")]
    end
    
    WEB --> TRAEFIK
    MOBILE --> TRAEFIK
    TRAEFIK --> |"/api/v1/auth"| IAM
    TRAEFIK --> |"/api/v1/members"| MEMBER
    TRAEFIK --> |"/api/v1/files"| STORAGE
    
    IAM --> |"JWT Token"| MEMBER
    MEMBER --> |"HTTP Client"| IAM
    MEMBER --> PG
    MEMBER -.-> |"Level Up Event"| WALLET

    style IAM fill:#3b82f6,stroke:#1e40af,color:#fff
    style MEMBER fill:#22c55e,stroke:#15803d,color:#fff
    style STORAGE fill:#eab308,stroke:#a16207,color:#000
    style WALLET fill:#a855f7,stroke:#7e22ce,color:#fff
    style TRAEFIK fill:#6366f1,stroke:#4338ca,color:#fff
```

> [!IMPORTANT]
> **Phân chia trách nhiệm:**
> - **IAM Service**: Quản lý User identity và UserProfile (avatar, phone, address, DOB)
> - **Membership Service**: Quản lý membership data (level, experience, preferences, country code, gender)
> - **Wallet Service**: Nhận event khi Level Up để xử lý rewards

## Các Lớp Clean Architecture

```
┌────────────────────────────────────────────────────────────┐
│                   Lớp API (Presentation)                   │
│  Controllers, Commands, Queries, Validators, DTOs          │
├────────────────────────────────────────────────────────────┤
│                   Lớp Domain (Core)                        │
│  Entities, Aggregates, Value Objects, Domain Events        │
│  Member Aggregate, LevelDefinition Aggregate               │
├────────────────────────────────────────────────────────────┤
│                   Lớp Infrastructure                       │
│  DbContext, Repositories, Entity Configurations            │
│  External Services (IAM Client), Idempotency               │
└────────────────────────────────────────────────────────────┘
```

## Domain Model

### Tổng Quan Aggregates

```mermaid
classDiagram
    class Member {
        +Guid Id
        +Guid UserId
        +string CountryCode
        +string? Gender
        +int CurrentLevel
        +int CurrentExp
        +int TotalExpEarned
        +string? Preferences
        +bool IsDeleted
        +DateTime CreatedAt
        +DateTime UpdatedAt
        +AddExperience(points, levelRules)
        +UpdateGender()
        +UpdateCountryCode()
        +UpdatePreferences()
        +Delete()
        +Restore()
    }
    
    class LevelDefinition {
        +Guid Id
        +int LevelNumber
        +string Name
        +int RequiredExp
        +string? Description
        +string? IconUrl
        +string? Benefits
        +bool IsActive
        +DateTime CreatedAt
        +DateTime UpdatedAt
    }
    
    Member ..> LevelDefinition : uses rules

    style Member fill:#22c55e,stroke:#15803d,color:#fff
    style LevelDefinition fill:#3b82f6,stroke:#1e40af,color:#fff
```

### Member Aggregate (Rich Domain Model)

```csharp
public class Member : Entity, IAggregateRoot
{
    // Private fields để encapsulate state
    private int _currentLevel;
    private int _currentExp;
    private int _totalExpEarned;
    
    public int CurrentLevel => _currentLevel;
    public int CurrentExp => _currentExp;
    public int TotalExpEarned => _totalExpEarned;
    
    /// <summary>
    /// EN: Add experience and automatically level up if thresholds are met.
    /// VI: Thêm exp và tự động lên level nếu đạt ngưỡng.
    /// </summary>
    public void AddExperience(int points, IReadOnlyList<LevelDefinition> levelRules)
    {
        if (points <= 0)
            throw new DomainException("EXP points must be positive");
        
        int oldLevel = _currentLevel;
        _currentExp += points;
        _totalExpEarned += points;
        _updatedAt = DateTime.UtcNow;
        
        // Business logic: Calculate new level
        var newLevel = CalculateLevel(_currentExp, levelRules);
        
        if (newLevel > oldLevel)
        {
            _currentLevel = newLevel;
            // Raise domain event for side effects
            AddDomainEvent(new MemberLevelUpDomainEvent(this, oldLevel, newLevel));
        }
        
        AddDomainEvent(new MemberExperienceAddedDomainEvent(this, points));
    }
    
    private static int CalculateLevel(int exp, IReadOnlyList<LevelDefinition> rules)
    {
        return rules
            .Where(r => r.IsActive && r.RequiredExp <= exp)
            .OrderByDescending(r => r.LevelNumber)
            .FirstOrDefault()?.LevelNumber ?? 1;
    }
}
```

> [!NOTE]
> **Rich Domain Model**: Logic nghiệp vụ về EXP và Level được đóng gói trong Member entity, không phải ở Service Layer. Điều này tránh mô hình "Anemic Domain Model".

### LevelDefinition Aggregate (Customizable Levels)

```csharp
public class LevelDefinition : Entity, IAggregateRoot
{
    private readonly List<LevelBenefit> _benefits = new();
    
    public int LevelNumber { get; private set; }     // 1, 2, 3...
    public string Name { get; private set; }         // "Bronze", "Silver", "Gold"
    public int RequiredExp { get; private set; }     // 0, 100, 300, 600...
    public string? Description { get; private set; }
    public string? IconUrl { get; private set; }
    public string? BadgeColor { get; private set; }  // "#CD7F32", "#FFD700"
    public bool IsActive { get; private set; }
    
    public IReadOnlyCollection<LevelBenefit> Benefits => _benefits.AsReadOnly();
    
    public void AddBenefit(LevelBenefit benefit)
    {
        _benefits.Add(benefit);
        UpdatedAt = DateTime.UtcNow;
    }
    
    public void UpdateRequiredExp(int newRequiredExp)
    {
        if (newRequiredExp < 0)
            throw new DomainException("Required EXP cannot be negative");
        RequiredExp = newRequiredExp;
    }
}
```

### LevelBenefit Entity (Rewards cho mỗi Level)

```csharp
public class LevelBenefit : Entity
{
    public Guid LevelDefinitionId { get; private set; }
    public string BenefitType { get; private set; }       // "discount_percent", "free_shipping"
    public string BenefitValue { get; private set; }      // JSON: {"percent": 10}
    public string? Description { get; private set; }
    public bool IsActive { get; private set; }
}
```

**Benefit Types:**

| Type | Value Example | Description |
|------|---------------|-------------|
| `discount_percent` | `{"percent": 10}` | Giảm giá 10% |
| `free_shipping` | `{"enabled": true}` | Miễn phí ship |
| `priority_support` | `{"enabled": true}` | Hỗ trợ ưu tiên |
| `bonus_points` | `{"multiplier": 1.5}` | Nhân điểm 1.5x |
| `exclusive_access` | `{"feature": "early_sale"}` | Truy cập sớm |

### ExperienceTransaction Entity (EXP Tracking)

Entity để tracking nguồn gốc và lịch sử EXP:

```csharp
public class ExperienceTransaction : Entity
{
    public Guid MemberId { get; private set; }
    public int Points { get; private set; }
    public ExperienceSource Source { get; private set; }  // Purchase, Referral, Activity...
    public string? ReferenceId { get; private set; }      // Order ID, Referral Code, etc.
    public string? Metadata { get; private set; }         // JSON additional info
    public int LevelAtTime { get; private set; }          // Level khi nhận EXP
    public DateTime CreatedAt { get; private set; }
}
```

**Experience Sources:**

| Source | ID | Mô tả |
|--------|----|-------|
| `Purchase` | 1 | Từ đơn hàng |
| `Referral` | 2 | Giới thiệu bạn bè |
| `Activity` | 3 | Hoạt động trên app |
| `Promotion` | 4 | Khuyến mãi |
| `Review` | 5 | Đánh giá sản phẩm |
| `CheckIn` | 6 | Check-in hàng ngày |
| `Admin` | 7 | Admin manual grant |

> [!NOTE]
> **Audit Trail**: Mỗi lần member nhận EXP đều được lưu vào `experience_transactions` với đầy đủ thông tin nguồn gốc để audit và analytics.

### Domain Events

| Event | Trigger | Mục đích |
|-------|---------|----------|
| `MemberCreatedDomainEvent` | Tạo thành viên mới | Email chào mừng, analytics |
| `MemberUpdatedDomainEvent` | Cập nhật hồ sơ | Đồng bộ với services khác |
| `MemberExperienceAddedDomainEvent` | Nhận EXP | Logging, analytics |
| `MemberLevelUpDomainEvent` | Lên level | **Rewards, notifications, unlock features** |

```mermaid
sequenceDiagram
    participant Client
    participant Handler
    participant Member
    participant EventDispatcher
    participant WalletService
    
    Client->>Handler: AddExperienceCommand
    Handler->>Member: AddExperience(100, rules)
    Member->>Member: _currentExp += 100
    Member->>Member: CalculateLevel()
    
    alt Level Up
        Member->>Member: AddDomainEvent(LevelUpEvent)
    end
    
    Handler->>EventDispatcher: SaveEntitiesAsync()
    EventDispatcher->>WalletService: Publish LevelUpEvent
    WalletService-->>WalletService: Grant Rewards
```

## CQRS Pattern

### Write Side (Commands)

```mermaid
flowchart LR
    subgraph Commands["✏️ Commands"]
        CMD1["AddExperienceCommand"]
        CMD2["CreateMemberCommand"]
        CMD3["UpdateMemberProfileCommand"]
        CMD4["CreateLevelDefinitionCommand"]
    end
    
    subgraph Handlers["⚙️ Command Handlers"]
        H1["AddExperienceHandler"]
        H2["CreateMemberHandler"]
        H3["UpdateProfileHandler"]
        H4["CreateLevelHandler"]
    end
    
    subgraph Domain["🏛️ Domain Model"]
        DM["Rich Domain Model<br/>EF Core"]
    end
    
    DB[("🐘 Database")]
    
    CMD1 --> H1 --> DM --> DB
    CMD2 --> H2 --> DM
    CMD3 --> H3 --> DM
    CMD4 --> H4 --> DM

    style CMD1 fill:#ef4444,stroke:#b91c1c,color:#fff
    style CMD2 fill:#ef4444,stroke:#b91c1c,color:#fff
    style CMD3 fill:#ef4444,stroke:#b91c1c,color:#fff
    style CMD4 fill:#ef4444,stroke:#b91c1c,color:#fff
```

### Read Side (Queries)

```mermaid
flowchart LR
    subgraph Queries["📖 Queries"]
        Q1["GetMemberByIdQuery"]
        Q2["GetMemberProgressQuery"]
        Q3["GetLevelDefinitionsQuery"]
    end
    
    subgraph Handlers["🔍 Query Handlers"]
        QH1["GetMemberByIdHandler"]
        QH2["GetMemberProgressHandler"]
        QH3["GetLevelDefinitionsHandler"]
    end
    
    subgraph ReadModel["📊 Read Model"]
        RM["Dapper / Raw SQL"]
    end
    
    DB[("🐘 Database")]
    
    Q1 --> QH1 --> RM --> DB
    Q2 --> QH2 --> RM
    Q3 --> QH3 --> RM

    style Q1 fill:#22c55e,stroke:#15803d,color:#fff
    style Q2 fill:#22c55e,stroke:#15803d,color:#fff
    style Q3 fill:#22c55e,stroke:#15803d,color:#fff
```

## Luồng Request

### Luồng Add Experience

```mermaid
sequenceDiagram
    participant Client
    participant Controller
    participant MediatR
    participant Handler
    participant MemberRepo
    participant LevelRepo
    participant DB
    
    Client->>Controller: POST /api/v1/members/{id}/experience
    Note over Client,Controller: { "points": 100, "source": "purchase" }
    
    Controller->>MediatR: Send(AddExperienceCommand)
    MediatR->>Handler: Handle(command)
    
    Handler->>MemberRepo: GetAsync(memberId)
    MemberRepo->>DB: SELECT member
    DB-->>MemberRepo: member data
    
    Handler->>LevelRepo: GetAllActiveAsync()
    LevelRepo->>DB: SELECT level_definitions
    DB-->>LevelRepo: level rules
    
    Handler->>Handler: member.AddExperience(100, rules)
    Note over Handler: Business logic in Domain
    
    Handler->>MemberRepo: UnitOfWork.SaveEntitiesAsync()
    MemberRepo->>DB: UPDATE + Dispatch Domain Events
    
    DB-->>Handler: OK
    Handler-->>Controller: AddExperienceResult
    Controller-->>Client: 200 OK
    Note over Client,Controller: { "currentExp": 150, "currentLevel": 2, "leveledUp": true }
```

### Luồng Get Member Progress

```mermaid
sequenceDiagram
    participant Client
    participant Controller
    participant Handler
    participant Dapper
    participant DB
    
    Client->>Controller: GET /api/v1/members/{id}/progress
    Controller->>Handler: GetMemberProgressQuery
    Handler->>Dapper: Raw SQL Query
    
    Note over Dapper,DB: SELECT m.current_exp, m.current_level,<br/>l.name, l.required_exp, next_level.required_exp<br/>FROM members m<br/>JOIN level_definitions l ON ...
    
    Dapper->>DB: Execute
    DB-->>Dapper: Result
    Dapper-->>Handler: MemberProgressDto
    Handler-->>Controller: Result
    Controller-->>Client: 200 OK
    Note over Client,Controller: { "currentLevel": 2, "levelName": "Silver",<br/>"currentExp": 150, "expToNextLevel": 150,<br/>"progressPercent": 50 }
```

## Database Schema

```mermaid
erDiagram
    MEMBERS ||--o{ EXPERIENCE_TRANSACTIONS : has
    MEMBERS }o--|| LEVEL_DEFINITIONS : current_level
    LEVEL_DEFINITIONS ||--o{ LEVEL_BENEFITS : has
    
    MEMBERS {
        uuid id PK "Same as IAM UserId"
        varchar country_code "ISO 3166-1 alpha-2"
        varchar gender "male/female/other"
        int current_level "Current member level"
        int current_exp "Current experience points"
        int total_exp_earned "Total EXP ever earned"
        jsonb preferences "User preferences"
        boolean is_deleted "Soft delete flag"
        timestamp created_at
        timestamp updated_at
    }
    
    LEVEL_DEFINITIONS {
        uuid id PK
        int level_number UK "Level 1, 2, 3..."
        varchar name "Bronze, Silver, Gold..."
        int required_exp "EXP needed for this level"
        text description
        varchar icon_url
        varchar badge_color "Hex color code"
        boolean is_active
        timestamp created_at
        timestamp updated_at
    }
    
    LEVEL_BENEFITS {
        uuid id PK
        uuid level_definition_id FK
        varchar benefit_type "discount_percent, free_shipping..."
        jsonb benefit_value "Benefit configuration"
        text description
        boolean is_active
        timestamp created_at
    }
    
    EXPERIENCE_TRANSACTIONS {
        uuid id PK
        uuid member_id FK
        int points "EXP points earned"
        int source_id "ExperienceSource enum"
        varchar reference_id "Order ID, Referral Code..."
        jsonb metadata "Additional info"
        int level_at_time "Member level when earned"
        timestamp created_at
    }
```

### SQL Schema

```sql
-- 1. Bảng Level Definitions (Admin configurable)
CREATE TABLE level_definitions (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    level_number INT NOT NULL UNIQUE,
    name VARCHAR(100) NOT NULL,
    required_exp INT NOT NULL DEFAULT 0,
    description TEXT,
    icon_url VARCHAR(500),
    badge_color VARCHAR(20),
    is_active BOOLEAN NOT NULL DEFAULT TRUE,
    created_at TIMESTAMP NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMP NOT NULL DEFAULT NOW()
);

-- 2. Bảng Level Benefits (Rewards cho mỗi level)
CREATE TABLE level_benefits (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    level_definition_id UUID NOT NULL REFERENCES level_definitions(id),
    benefit_type VARCHAR(50) NOT NULL,
    benefit_value JSONB NOT NULL,
    description TEXT,
    is_active BOOLEAN NOT NULL DEFAULT TRUE,
    created_at TIMESTAMP NOT NULL DEFAULT NOW()
);

-- 3. Bảng Members
CREATE TABLE members (
    id UUID PRIMARY KEY,
    country_code VARCHAR(2) NOT NULL DEFAULT 'VN',
    gender VARCHAR(10),
    current_level INT NOT NULL DEFAULT 1,
    current_exp INT NOT NULL DEFAULT 0,
    total_exp_earned INT NOT NULL DEFAULT 0,
    preferences JSONB,
    is_deleted BOOLEAN NOT NULL DEFAULT FALSE,
    created_at TIMESTAMP NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMP NOT NULL DEFAULT NOW()
);

-- 4. Bảng Experience Transactions (EXP History/Tracking)
CREATE TABLE experience_transactions (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    member_id UUID NOT NULL REFERENCES members(id),
    points INT NOT NULL,
    source_id INT NOT NULL,
    reference_id VARCHAR(100),
    metadata JSONB,
    level_at_time INT NOT NULL,
    created_at TIMESTAMP NOT NULL DEFAULT NOW()
);

-- Indexes
CREATE INDEX ix_level_definitions_level_number ON level_definitions(level_number);
CREATE INDEX ix_level_definitions_is_active ON level_definitions(is_active);
CREATE INDEX ix_level_benefits_level_definition_id ON level_benefits(level_definition_id);
CREATE INDEX ix_members_current_level ON members(current_level);
CREATE INDEX ix_members_current_exp ON members(current_exp);
CREATE INDEX ix_experience_transactions_member_id ON experience_transactions(member_id);
CREATE INDEX ix_experience_transactions_created_at ON experience_transactions(created_at);
CREATE INDEX ix_experience_transactions_source ON experience_transactions(source_id);

-- Default Level Definitions
INSERT INTO level_definitions (level_number, name, required_exp, description, badge_color, is_active)
VALUES 
    (1, 'Bronze', 0, 'Starting level - Welcome!', '#CD7F32', true),
    (2, 'Silver', 100, 'Reach 100 EXP', '#C0C0C0', true),
    (3, 'Gold', 300, 'Reach 300 EXP', '#FFD700', true),
    (4, 'Platinum', 600, 'Reach 600 EXP', '#E5E4E2', true),
    (5, 'Diamond', 1000, 'Reach 1000 EXP - Elite!', '#B9F2FF', true);

-- Default Level Benefits
INSERT INTO level_benefits (level_definition_id, benefit_type, benefit_value, description)
SELECT id, 'discount_percent', '{"percent": 5}', '5% discount on all orders'
FROM level_definitions WHERE level_number = 2;

INSERT INTO level_benefits (level_definition_id, benefit_type, benefit_value, description)
SELECT id, 'discount_percent', '{"percent": 10}', '10% discount on all orders'
FROM level_definitions WHERE level_number = 3;

INSERT INTO level_benefits (level_definition_id, benefit_type, benefit_value, description)
SELECT id, 'free_shipping', '{"enabled": true}', 'Free shipping on all orders'
FROM level_definitions WHERE level_number = 4;

INSERT INTO level_benefits (level_definition_id, benefit_type, benefit_value, description)
SELECT id, 'priority_support', '{"enabled": true}', 'Priority customer support'
FROM level_definitions WHERE level_number = 5;
```

## Thiết Kế API

### RESTful Endpoints

#### Member Endpoints

```
GET    /api/v1/members                  # Danh sách (phân trang, search)
GET    /api/v1/members/{id}             # Lấy theo ID
GET    /api/v1/members/me               # Lấy user hiện tại
GET    /api/v1/members/{id}/progress    # Lấy tiến trình level
GET    /api/v1/members/{id}/experience  # Lấy lịch sử EXP (tracking)
POST   /api/v1/members                  # Tạo mới
POST   /api/v1/members/{id}/experience  # Thêm EXP
PUT    /api/v1/members/{id}             # Cập nhật hồ sơ
```

#### Level Endpoints (Admin)

```
GET    /api/v1/levels               # Danh sách level definitions
POST   /api/v1/levels               # Tạo level definition (Admin)
PUT    /api/v1/levels/{id}          # Cập nhật level definition (Admin)
DELETE /api/v1/levels/{id}          # Deactivate level (Admin)
```

### Response Formats

#### Member Response

```json
{
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "userId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "countryCode": "VN",
  "gender": "male",
  "currentLevel": 2,
  "currentExp": 150,
  "totalExpEarned": 150,
  "preferences": "{\"theme\": \"dark\"}",
  "createdAt": "2024-01-15T10:30:00Z",
  "updatedAt": "2024-01-15T10:30:00Z"
}
```

#### Member Progress Response

```json
{
  "memberId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "currentLevel": 2,
  "levelName": "Silver",
  "levelIconUrl": "/icons/silver.png",
  "currentExp": 150,
  "expToNextLevel": 150,
  "nextLevelRequiredExp": 300,
  "progressPercent": 50.0
}
```

#### Add Experience Response

```json
{
  "currentExp": 250,
  "currentLevel": 2,
  "totalExpEarned": 250,
  "leveledUp": false,
  "newLevel": null
}
```

#### Level Definition Response

```json
{
  "id": "a1b2c3d4-...",
  "levelNumber": 3,
  "name": "Gold",
  "requiredExp": 300,
  "description": "Reach 300 EXP to unlock Gold status",
  "iconUrl": "/icons/gold.png",
  "benefits": {
    "discountPercent": 10,
    "prioritySupport": true,
    "freeShipping": false
  },
  "isActive": true
}
```

## Inter-Service Communication

### IAM Service Client

Membership Service giao tiếp với IAM Service thông qua HTTP Client để validate users.

### Integration Events (Planned)

Khi member lên level, publish Integration Event để các services khác xử lý:

```mermaid
flowchart LR
    MEMBER["👥 Membership Service"]
    BUS["📬 Event Bus<br/>(RabbitMQ)"]
    WALLET["💰 Wallet Service"]
    NOTIF["🔔 Notification Service"]
    
    MEMBER -->|MemberLevelUpIntegrationEvent| BUS
    BUS -->|Subscribe| WALLET
    BUS -->|Subscribe| NOTIF
    
    WALLET -->|Grant Rewards| WALLET
    NOTIF -->|Send Notification| NOTIF

    style MEMBER fill:#22c55e,stroke:#15803d,color:#fff
    style BUS fill:#ef4444,stroke:#b91c1c,color:#fff
    style WALLET fill:#a855f7,stroke:#7e22ce,color:#fff
    style NOTIF fill:#eab308,stroke:#a16207,color:#000
```

## Bảo Mật

### Authorization

- Tất cả endpoints yêu cầu xác thực JWT Bearer
- Token được xác thực với IAM Service authority
- UserId được trích xuất từ JWT claims (`sub` hoặc `id`)
- **Admin endpoints** (`/api/v1/levels` POST/PUT/DELETE) yêu cầu role `admin`

### Experience Source Validation

```csharp
// Validate experience source để tránh abuse
public enum ExperienceSource
{
    Purchase,      // Từ đơn hàng
    Referral,      // Giới thiệu bạn bè
    Activity,      // Hoạt động trên app
    Promotion,     // Khuyến mãi
    Admin          // Admin manual grant
}
```

## Hạ Tầng

### Docker Compose Setup

```yaml
membership-service-net:
  build:
    context: ../..
    dockerfile: services/membership-service-net/Dockerfile
  environment:
    - DATABASE_URL=postgresql://user:pass@db:5432/membership
    - Jwt__Authority=http://iam-service-net:8080
    - IamService__BaseUrl=http://iam-service-net:8080
  ports:
    - "5002:8080"
  labels:
    - "traefik.http.routers.membership.rule=PathPrefix(`/api/v1/members`) || PathPrefix(`/api/v1/levels`)"
```

### Kubernetes Deployment

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: membership-service
spec:
  replicas: 3
  template:
    spec:
      containers:
      - name: membership-service
        image: goodgo/membership-service:latest
        ports:
        - containerPort: 8080
        resources:
          requests:
            memory: "256Mi"
            cpu: "250m"
          limits:
            memory: "512Mi"
            cpu: "500m"
        livenessProbe:
          httpGet:
            path: /health/live
            port: 8080
        readinessProbe:
          httpGet:
            path: /health/ready
            port: 8080
```

## Giám Sát

### Health Checks

| Endpoint | Kiểm tra | Mục đích |
|----------|----------|----------|
| `/health` | Tất cả dependencies | Trạng thái đầy đủ |
| `/health/live` | App đang chạy | Kubernetes liveness |
| `/health/ready` | DB kết nối | Kubernetes readiness |

### Metrics (Prometheus)

- `membership_experience_added_total` - Tổng EXP đã cộng
- `membership_level_ups_total` - Số lần lên level
- `membership_requests_total` - Số lượng request
- `membership_request_duration_seconds` - Độ trễ

### Logging

Structured Serilog logging với:
- Request/Response logging
- Domain event logging (Level Up, EXP Added)
- Error tracking với stack traces

## Xử Lý Lỗi

### Error Response Format

```json
{
  "type": "https://tools.ietf.org/html/rfc7231#section-6.5.4",
  "title": "Bad Request",
  "status": 400,
  "detail": "EXP points must be positive.",
  "traceId": "00-1234567890abcdef-1234567890abcdef-00"
}
```

### Mã Lỗi

| HTTP Status | Tình huống |
|-------------|------------|
| 400 | Validation error (EXP âm, etc.) |
| 401 | Thiếu/sai token |
| 403 | Không có quyền (Admin endpoints) |
| 404 | Không tìm thấy member/level |
| 409 | Level number đã tồn tại |
| 500 | Lỗi server nội bộ |

## Cải Tiến Tương Lai

- [ ] Redis caching cho level definitions
- [ ] Event sourcing cho EXP history audit trail
- [ ] Leaderboard với ranking theo EXP
- [ ] Achievement/Badge system
- [ ] EXP decay (giảm EXP nếu không hoạt động)
- [ ] Seasonal levels (reset theo mùa)
- [ ] Integration Events với RabbitMQ
