Files
pos-system/services/mining-service-net/docs/en/ARCHITECTURE.md

12 KiB
Raw Blame History

Mining Service Architecture

Overview

The Mining Service provides a gamified point mining system inspired by Pi Network, enabling users to accumulate Mining Points through daily engagement and community building.

%%{init: {'theme':'dark'}}%%
graph TD
    subgraph API["🌐 API Layer"]
        Controllers[Controllers]
        Commands[Commands]
        Queries[Queries]
    end
    
    subgraph Domain["⚙️ Domain Layer"]
        Miner[Miner Aggregate]
        Circle[Circle Aggregate]
        Referral[Referral Aggregate]
    end
    
    subgraph Infra["💾 Infrastructure Layer"]
        EF[EF Core]
        Redis[(Redis Cache)]
        RabbitMQ[RabbitMQ]
    end
    
    subgraph Data["🗄️ Data Storage"]
        DB[(PostgreSQL)]
    end
    
    API --> Domain
    Domain --> Infra
    EF --> 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:#34495E,color:#ECF0F1,stroke:#2C3E50,stroke-width:2px
    style Data fill:#27AE60,color:#ECF0F1,stroke:#229954,stroke-width:2px

Architecture Patterns

Domain-Driven Design (DDD)

  • Aggregates: Miner, Circle, Referral
  • Entities: MiningSession, MiningHistory, CircleMember
  • Value Objects: MiningRate, MiningPoints, MiningSession
  • Domain Events: MiningSessionStarted, PointsMined, CircleCompleted, ReferralActivated

CQRS Pattern

%%{init: {'theme':'dark'}}%%
flowchart LR
    subgraph Write["📝 Commands"]
        C1[StartMiningCommand]
        C2[ClaimRewardCommand]
        C3[CreateCircleCommand]
        C4[InviteToCircleCommand]
        C5[ApplyReferralCommand]
    end
    
    subgraph Read["📖 Queries"]
        Q1[GetMinerStatusQuery]
        Q2[GetMiningHistoryQuery]
        Q3[GetCircleQuery]
        Q4[GetReferralsQuery]
        Q5[GetLeaderboardQuery]
    end
    
    style Write fill:#E67E22,color:#ECF0F1,stroke:#D35400,stroke-width:2px
    style Read fill:#3498DB,color:#ECF0F1,stroke:#2980B9,stroke-width:2px

Domain Model

Miner Aggregate

%%{init: {'theme':'dark'}}%%
classDiagram
    class Miner {
        +Guid Id
        +Guid UserId
        +MinerRole Role
        +decimal TotalMinedPoints
        +MiningRate CurrentRate
        +MiningSession ActiveSession
        +MiningStreak Streak
        +string ReferralCode
        +Guid ReferredBy
        +MinerStatus Status
        +StartMiningSession()
        +ClaimMiningReward()
        +UpgradeRole()
        +RecalculateMiningRate()
    }
    
    class MiningSession {
        +Guid SessionId
        +DateTime StartTime
        +DateTime EndTime
        +decimal AccumulatedPoints
        +SessionStatus Status
    }
    
    class MiningRate {
        +decimal BaseRate
        +decimal CircleBonus
        +decimal ReferralBonus
        +decimal RoleBonus
        +decimal StreakBonus
        +decimal TotalRate
    }
    
    class MiningStreak {
        +int CurrentStreak
        +int LongestStreak
        +DateTime LastMiningDate
        +int FreezeTokens
        +bool IsGracePeriod
        +decimal BonusMultiplier
    }
    
    class MiningHistory {
        +Guid Id
        +decimal PointsEarned
        +string Source
        +DateTime EarnedAt
    }
    
    Miner "1" --> "0..1" MiningSession : has
    Miner --> MiningRate : uses
    Miner --> MiningStreak : tracks
    Miner "1" --> "*" MiningHistory : tracks

Circle Aggregate

%%{init: {'theme':'dark'}}%%
classDiagram
    class Circle {
        +Guid Id
        +Guid OwnerId
        +string Name
        +List~CircleMember~ Members
        +decimal TrustScore
        +decimal BonusMultiplier
        +CircleStatus Status
        +AddMember()
        +RemoveMember()
        +CalculateTrustScore()
        +Validate()
    }
    
    class CircleMember {
        +Guid Id
        +Guid MinerId
        +DateTime JoinedAt
        +bool IsActive
    }
    
    Circle "1" --> "3..5" CircleMember : contains

Referral Aggregate

%%{init: {'theme':'dark'}}%%
classDiagram
    class Referral {
        +Guid Id
        +Guid ReferrerId
        +Guid ReferredId
        +string ReferralCode
        +decimal BonusRate
        +bool IsActive
        +int Level
        +DateTime CreatedAt
        +Activate()
        +Deactivate()
        +CalculateBonus()
    }

Mining Rate Formula

%%{init: {'theme':'dark'}}%%
flowchart LR
    Base["🎯 Base Rate<br/>0.25 MP/hour"] --> Multiply1((×))
    Role["👤 Role Bonus<br/>+0-50%"] --> Multiply1
    Multiply1 --> Multiply2((×))
    Circle["🔵 Circle Bonus<br/>+25%"] --> Multiply2
    Multiply2 --> Multiply3((×))
    Referral["👥 Referral Bonus<br/>+25%/each"] --> Multiply3
    Multiply3 --> Total["✅ Total Rate<br/>MP/hour"]
    
    style Base fill:#2C3E50,color:#ECF0F1,stroke:#34495E,stroke-width:2px
    style Role fill:#8E44AD,color:#ECF0F1,stroke:#7D3C98,stroke-width:2px
    style Circle fill:#3498DB,color:#ECF0F1,stroke:#2980B9,stroke-width:2px
    style Referral fill:#E67E22,color:#ECF0F1,stroke:#D35400,stroke-width:2px
    style Total fill:#27AE60,color:#ECF0F1,stroke:#229954,stroke-width:3px

Example Calculation:

Component Value Multiplier
Base Rate 0.25 MP/hour -
Role (Ambassador) +25% × 1.25
Valid Circle +25% × 1.25
2 Referrals +50% × 1.50
Total 0.585 MP/hour 14.04 MP/day

Database Schema

ER Diagram

%%{init: {'theme':'dark'}}%%
erDiagram
    Miner ||--o{ MiningSession : has
    Miner ||--o{ MiningHistory : tracks
    Miner ||--o| Circle : owns
    Circle ||--|{ CircleMember : contains
    Miner ||--o{ Referral : refers
    Miner ||--o| Referral : referredBy
    
    Miner {
        uuid Id PK
        uuid UserId UK
        string Role
        decimal TotalMinedPoints
        string ReferralCode UK
        uuid ReferredBy FK
        string Status
        datetime CreatedAt
    }
    
    MiningSession {
        uuid Id PK
        uuid MinerId FK
        datetime StartTime
        datetime EndTime
        decimal AccumulatedPoints
        string Status
    }
    
    MiningHistory {
        uuid Id PK
        uuid MinerId FK
        decimal PointsEarned
        string Source
        datetime EarnedAt
    }
    
    Circle {
        uuid Id PK
        uuid OwnerId FK
        string Name
        decimal TrustScore
        decimal BonusMultiplier
        string Status
    }
    
    CircleMember {
        uuid Id PK
        uuid CircleId FK
        uuid MinerId FK
        datetime JoinedAt
        bool IsActive
    }
    
    Referral {
        uuid Id PK
        uuid ReferrerId FK
        uuid ReferredId FK
        string ReferralCode
        decimal BonusRate
        bool IsActive
        int Level
    }

Key Indexes

Index Columns Purpose
IX_Miners_UserId UserId Fast lookup by user
IX_Miners_ReferralCode ReferralCode Referral code lookup
IX_MiningSessions_MinerId_Status MinerId, Status Active session check
IX_Referrals_ReferrerId ReferrerId Referral list

API Flow

Start Mining Session

%%{init: {'theme':'dark'}}%%
sequenceDiagram
    participant Client as 📱 Client
    participant API as 🌐 Mining API
    participant Handler as ⚙️ CommandHandler
    participant Miner as 👤 MinerAggregate
    participant Calc as 🧮 RateCalculator
    participant DB as 💾 PostgreSQL
    participant Cache as ⚡ Redis
    
    Client->>API: POST /api/v1/mining/start
    API->>Handler: StartMiningCommand
    Handler->>Miner: ValidateNoActiveSession()
    Handler->>Calc: CalculateMiningRate()
    Calc-->>Handler: MiningRate
    Handler->>Miner: CreateSession(24h)
    Miner->>DB: SaveSession()
    Miner->>Cache: CacheSessionInfo(ttl: 24h)
    API-->>Client: 200 OK { session_id, rate, end_time }

Claim Mining Reward

%%{init: {'theme':'dark'}}%%
sequenceDiagram
    participant Client as 📱 Client
    participant API as 🌐 Mining API
    participant Handler as ⚙️ CommandHandler
    participant Miner as 👤 MinerAggregate
    participant DB as 💾 PostgreSQL
    participant MQ as 📨 RabbitMQ
    
    Client->>API: POST /api/v1/mining/claim
    API->>Handler: ClaimRewardCommand
    Handler->>Miner: GetActiveSession()
    
    alt Session Completed
        Miner->>Miner: CalculateEarnedPoints()
        Miner->>Miner: AddToTotalPoints()
        Miner->>DB: SaveMiningHistory()
        Miner->>MQ: Publish PointsMinedEvent
        API-->>Client: 200 OK { earned_points, total_points }
    else Session Not Ready
        API-->>Client: 400 Bad Request
    end

Inter-Service Communication

Service Dependencies

%%{init: {'theme':'dark'}}%%
graph TD
    subgraph External["🔐 Authentication"]
        IAM[IAM Service]
    end
    
    subgraph Core["⛏️ Mining Service"]
        Mining[Mining Service]
    end
    
    subgraph Integration["🔗 Integrations"]
        Social[Social Service]
        Wallet[Wallet Service]
    end
    
    IAM -->|JWT Validation| Mining
    Social <-->|Friend Data| Mining
    Mining -->|Point Conversion| Wallet
    
    style External fill:#C0392B,color:#ECF0F1,stroke:#A93226,stroke-width:2px
    style Core fill:#8E44AD,color:#ECF0F1,stroke:#7D3C98,stroke-width:3px
    style Integration fill:#3498DB,color:#ECF0F1,stroke:#2980B9,stroke-width:2px

Integration Events (RabbitMQ)

%%{init: {'theme':'dark'}}%%
flowchart LR
    subgraph Publishers["📤 Publishers"]
        IAM[IAM Service]
        Social[Social Service]
        Mining1[Mining Service]
    end
    
    subgraph Events["📨 Events"]
        E1[UserRegisteredEvent]
        E2[FriendAddedEvent]
        E3[PointsMinedEvent]
    end
    
    subgraph Consumers["📥 Consumers"]
        Mining2[Mining Service]
        Wallet[Wallet Service]
    end
    
    IAM --> E1 --> Mining2
    Social --> E2 --> Mining2
    Mining1 --> E3 --> Wallet
    
    style Events fill:#E67E22,color:#ECF0F1,stroke:#D35400,stroke-width:2px

Deployment

Docker Compose

mining-service:
  build:
    context: ../..
    dockerfile: services/mining-service-net/Dockerfile
  environment:
    - DATABASE_URL=${MINING_DATABASE_URL}
    - REDIS_URL=${REDIS_URL}
    - RABBITMQ_URL=${RABBITMQ_URL}
    - JWT_AUTHORITY=${IAM_SERVICE_URL}
  labels:
    - traefik.http.routers.mining.rule=PathPrefix(`/api/v1/mining`)

Health Checks

Endpoint Check
/health/live Service running
/health/ready DB + Redis connected
/health Full status

Security

Rate Limiting

  • 1 mining session start per 24 hours
  • 10 circle invites per day

Anti-Fraud

%%{init: {'theme':'dark'}}%%
flowchart TD
    Request([🚀 Request]) --> Device{🔍 Device Check}
    Device -->|New Device| Flag[⚠️ Flag for Review]
    Device -->|Known| IP{🌐 IP Check}
    IP -->|Suspicious| Block[❌ Block]
    IP -->|Normal| KYC{🔐 KYC Verified?}
    KYC -->|No| Limited[⚠️ Limited Features]
    KYC -->|Yes| Full[✅ Full Access]
    
    style Request fill:#2C3E50,color:#ECF0F1,stroke:#34495E,stroke-width:3px
    style Block fill:#C0392B,color:#ECF0F1,stroke:#A93226,stroke-width:2px
    style Full fill:#27AE60,color:#ECF0F1,stroke:#229954,stroke-width:2px
    style Device fill:#E67E22,color:#ECF0F1,stroke:#D35400,stroke-width:2px
    style IP fill:#E67E22,color:#ECF0F1,stroke:#D35400,stroke-width:2px
    style KYC fill:#E67E22,color:#ECF0F1,stroke:#D35400,stroke-width:2px

Caching Strategy (Redis)

Key Pattern TTL Purpose
session:{minerId} 24h Active session cache
rate:{minerId} 1h Mining rate cache
leaderboard:daily 5m Leaderboard cache

Monitoring

Metrics

  • Mining sessions started/day
  • Points mined total
  • Active miners count
  • Referral conversion rate

Logging

  • Serilog structured logging
  • Correlation IDs for tracing
  • Prometheus + Grafana integration