12 KiB
12 KiB
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