feat: scaffold modular monolith backend architecture
This commit is contained in:
+112
@@ -0,0 +1,112 @@
|
|||||||
|
# WTFX Backend Architecture Rules (后端架构铁律)
|
||||||
|
|
||||||
|
> **核心定位**:WTFX 采用 **Python + FastAPI + PostgreSQL + Redis** 的**模块化单体 (Modular Monolith)** 架构。
|
||||||
|
> **适用对象**:所有开发者与 AI Coding Agent。在编写或修改任何后端代码之前,**必须严格遵守本规范**。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. 架构核心铁律 (Non-Negotiable Rules)
|
||||||
|
|
||||||
|
1. **【分层单向依赖】**:
|
||||||
|
- 依赖调用链严格为:`API Route -> Domain Service -> Repository -> Database/Infrastructure`。
|
||||||
|
- **严禁反向或跨层调用**(如 Route 直连 DB,或 Repo 调用 Service)。
|
||||||
|
2. **【禁止 Route 直连数据库】**:
|
||||||
|
- API 路由层(`app/api/`)只允许做:入参校验 (Pydantic Schema)、鉴权依赖注入、调用 Domain Service、返回标准响应。
|
||||||
|
- **严禁在 Route 中执行 SQL 或 ORM 查询**。
|
||||||
|
3. **【Domain 纯净性】**:
|
||||||
|
- 业务领域层(`app/domains/`)代表纯粹的业务大脑,**严禁导入 FastAPI 或任何 HTTP/WebSocket 相关的 Web 框架依赖**。
|
||||||
|
4. **【资金与状态变更强制走 Ledger】**:
|
||||||
|
- 严禁直接执行 `user.balance += amount` 等无据操作。
|
||||||
|
- 所有账户资金变动(充值、下注扣款、保证金冻结/解冻、手续费、获胜兑付)**必须通过 LedgerService 生成不可变的复式记账流水 (`LedgerEntry`)**。
|
||||||
|
5. **【外部第三方服务强制使用 Adapter 隔离】**:
|
||||||
|
- 链上 RPC、Hyperliquid、价格预言机等外部系统交互必须统一封装在 `app/infrastructure/` 适配器中。
|
||||||
|
- Domain 业务层只依赖抽象接口(如 `TradingProvider` / `ChainClient`),不感知底层具体 API 细节。
|
||||||
|
6. **【事件总线驱动,解耦 WebSocket】**:
|
||||||
|
- 业务状态变更(如订单成交、市场毕业)只向 `EventBus` 发送领域事件。
|
||||||
|
- WebSocket 模块(`app/ws/`)独立订阅事件并广播给在线客户端,业务逻辑层严禁直接依赖 WS 连接对象。
|
||||||
|
7. **【Model 与 Schema 严格分离】**:
|
||||||
|
- `models/` 映射数据库实体,`schemas/` 定义 API 输入输出结构体。
|
||||||
|
- **严禁将数据库 Model 直接作为 API Response 返回**,防止敏感字段(密码哈希、内部风控分等)意外泄露。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. 目录职责分工
|
||||||
|
|
||||||
|
```
|
||||||
|
wtf-backend/
|
||||||
|
├── app/
|
||||||
|
│ ├── main.py # 应用入口与生命周期管理 (Lifespan)
|
||||||
|
│ ├── config.py # Pydantic Settings 环境变量配置
|
||||||
|
│ ├── logging.py # 结构化日志配置
|
||||||
|
│ │
|
||||||
|
│ ├── api/ # HTTP 接口接入层 (只做请求解析与转发)
|
||||||
|
│ │ ├── dependencies.py # 共享依赖注入 (DB Session, 当前用户, Auth)
|
||||||
|
│ │ └── routes/ # 各业务模块的路由入口
|
||||||
|
│ │ ├── auth.py # Web3 SIWE 钱包登录与 JWT
|
||||||
|
│ │ ├── markets.py # 预测市场查询与建盘
|
||||||
|
│ │ ├── orders.py # 交易下单与撤单
|
||||||
|
│ │ ├── positions.py # 用户持仓查询
|
||||||
|
│ │ └── wallet.py # 充值、提现与流水
|
||||||
|
│ │
|
||||||
|
│ ├── domains/ # 业务领域大脑 (纯业务逻辑,无 Web 依赖)
|
||||||
|
│ │ ├── market/ # 市场状态机与联合曲线规则
|
||||||
|
│ │ ├── trading/ # 交易撮合与风控检查
|
||||||
|
│ │ ├── ledger/ # 【核心】不可变流水账本
|
||||||
|
│ │ ├── wallet/ # 钱包余额状态管理
|
||||||
|
│ │ ├── settlement/ # 预测到期结算与兑付
|
||||||
|
│ │ └── user/ # 用户信息与邀请返佣
|
||||||
|
│ │
|
||||||
|
│ ├── infrastructure/ # 外部世界适配层 (数据库、缓存、链上、三方 API)
|
||||||
|
│ │ ├── database/ # PostgreSQL 连接池与异步 Session 工厂
|
||||||
|
│ │ ├── redis/ # Redis 缓存与 Streams 事件总线
|
||||||
|
│ │ ├── blockchain/ # EVM RPC 监听与合约调用客户端
|
||||||
|
│ │ └── external/ # Hyperliquid / CLOB 等外部交易适配器
|
||||||
|
│ │
|
||||||
|
│ ├── models/ # SQLAlchemy 2.0 异步数据库实体
|
||||||
|
│ │ ├── market.py
|
||||||
|
│ │ ├── order.py
|
||||||
|
│ │ ├── position.py
|
||||||
|
│ │ ├── ledger.py
|
||||||
|
│ │ └── user.py
|
||||||
|
│ │
|
||||||
|
│ ├── schemas/ # Pydantic v2 请求与响应数据传输对象 (DTO)
|
||||||
|
│ │ ├── market.py
|
||||||
|
│ │ ├── order.py
|
||||||
|
│ │ ├── ledger.py
|
||||||
|
│ │ └── common.py
|
||||||
|
│ │
|
||||||
|
│ └── ws/ # WebSocket 实时网关 (行情推流与用户私有信道)
|
||||||
|
│ ├── manager.py # 连接池与订阅频道管理
|
||||||
|
│ └── handlers.py # WS 消息解析与心跳
|
||||||
|
│
|
||||||
|
├── migrations/ # Alembic 数据库迁移脚本
|
||||||
|
├── tests/ # 单元测试与集成测试
|
||||||
|
├── Dockerfile # 生产镜像构建文件
|
||||||
|
├── docker-compose.yml # 本地开发与服务器运行编排
|
||||||
|
└── requirements.txt # 锁定依赖项
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. 核心业务流程标准示例 (How to Write Code)
|
||||||
|
|
||||||
|
### 正确的下单流程示例 (Trading Flow):
|
||||||
|
```
|
||||||
|
[Client] ---> POST /api/v1/orders
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
[orders.py (Route)]
|
||||||
|
│ (1. 解析 CreateOrderRequest Schema)
|
||||||
|
│ (2. 依赖注入 TradingService)
|
||||||
|
▼
|
||||||
|
[TradingService (Domain)]
|
||||||
|
│ (1. 风险检查 RiskCheck)
|
||||||
|
│ (2. 冻结资金: 调用 LedgerService.freeze_balance)
|
||||||
|
│ (3. 创建订单: 调用 OrderRepository.create)
|
||||||
|
│ (4. 抛出事件: EventBus.publish("OrderCreated"))
|
||||||
|
▼
|
||||||
|
[PostgreSQL] (Commit Transaction)
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
[WS Gateway] (异步监听到 OrderCreated -> 广播行情)
|
||||||
|
```
|
||||||
+21
@@ -0,0 +1,21 @@
|
|||||||
|
FROM python:3.11-slim
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# 安装基础编译依赖
|
||||||
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
|
build-essential \
|
||||||
|
libpq-dev \
|
||||||
|
curl \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# 安装 Python 依赖
|
||||||
|
COPY requirements.txt .
|
||||||
|
RUN pip install --no-cache-dir -r requirements.txt
|
||||||
|
|
||||||
|
# 复制代码
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
EXPOSE 8080
|
||||||
|
|
||||||
|
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8080", "--workers", "4"]
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
from pydantic_settings import BaseSettings
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
class Settings(BaseSettings):
|
||||||
|
PROJECT_NAME: str = "WTFX Backend API"
|
||||||
|
VERSION: str = "2.0.0"
|
||||||
|
DEBUG: bool = False
|
||||||
|
|
||||||
|
# 环境:development, staging, production
|
||||||
|
ENVIRONMENT: str = "development"
|
||||||
|
|
||||||
|
# 数据库配置 (PostgreSQL)
|
||||||
|
DATABASE_URL: str = "postgresql+asyncpg://postgres:postgres@localhost:5432/wtfx"
|
||||||
|
|
||||||
|
# Redis 配置
|
||||||
|
REDIS_URL: str = "redis://localhost:6379/0"
|
||||||
|
|
||||||
|
# JWT 鉴权
|
||||||
|
JWT_SECRET: str = "wtfx_secret_jwt_key_change_in_production"
|
||||||
|
JWT_ALGORITHM: str = "HS256"
|
||||||
|
ACCESS_TOKEN_EXPIRE_MINUTES: int = 60 * 24 * 7 # 7 days
|
||||||
|
|
||||||
|
# 跨域设置
|
||||||
|
CORS_ORIGINS: List[str] = [
|
||||||
|
"http://localhost:3000",
|
||||||
|
"http://localhost:3001",
|
||||||
|
"https://wtfx.app",
|
||||||
|
"https://test.wtfx.app",
|
||||||
|
"https://admin.wtfx.app",
|
||||||
|
"https://admin.test.wtfx.app"
|
||||||
|
]
|
||||||
|
|
||||||
|
# 链上 RPC 配置 (Robinhood Chain)
|
||||||
|
ROBINHOOD_TESTNET_RPC: str = "https://rpc.testnet.chain.robinhood.com"
|
||||||
|
ROBINHOOD_MAINNET_RPC: str = "https://rpc.chain.robinhood.com"
|
||||||
|
|
||||||
|
# 控制器合约地址 (由部署脚本生成)
|
||||||
|
CONTROLLER_ADDRESS: str = "0x0000000000000000000000000000000000000000"
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
env_file = ".env"
|
||||||
|
case_sensitive = True
|
||||||
|
|
||||||
|
settings = Settings()
|
||||||
+39
@@ -0,0 +1,39 @@
|
|||||||
|
from fastapi import FastAPI
|
||||||
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
|
from contextlib import asynccontextmanager
|
||||||
|
from app.config import settings
|
||||||
|
|
||||||
|
@asynccontextmanager
|
||||||
|
async def lifespan(app: FastAPI):
|
||||||
|
# 启动时:初始化 DB 连接池、Redis 事件总线与外部适配器
|
||||||
|
print(f"Starting {settings.PROJECT_NAME} in [{settings.ENVIRONMENT}] mode...")
|
||||||
|
yield
|
||||||
|
# 关闭时:优雅断开连接
|
||||||
|
print(f"Shutting down {settings.PROJECT_NAME}...")
|
||||||
|
|
||||||
|
app = FastAPI(
|
||||||
|
title=settings.PROJECT_NAME,
|
||||||
|
version=settings.VERSION,
|
||||||
|
lifespan=lifespan,
|
||||||
|
docs_url="/docs" if settings.ENVIRONMENT != "production" else None,
|
||||||
|
redoc_url="/redoc" if settings.ENVIRONMENT != "production" else None
|
||||||
|
)
|
||||||
|
|
||||||
|
# CORS 配置
|
||||||
|
app.add_middleware(
|
||||||
|
CORSMiddleware,
|
||||||
|
allow_origins=settings.CORS_ORIGINS,
|
||||||
|
allow_credentials=True,
|
||||||
|
allow_methods=["*"],
|
||||||
|
allow_headers=["*"],
|
||||||
|
)
|
||||||
|
|
||||||
|
@app.get("/health", tags=["System"])
|
||||||
|
async def health_check():
|
||||||
|
"""生产探活与健康检查端点 (Blue-Green Probe)"""
|
||||||
|
return {
|
||||||
|
"status": "ok",
|
||||||
|
"service": "wtfx-backend",
|
||||||
|
"version": settings.VERSION,
|
||||||
|
"environment": settings.ENVIRONMENT
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
version: '3.8'
|
||||||
|
|
||||||
|
services:
|
||||||
|
backend:
|
||||||
|
build: .
|
||||||
|
container_name: wtfx-backend
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "8080:8080"
|
||||||
|
environment:
|
||||||
|
- ENVIRONMENT=production
|
||||||
|
- DATABASE_URL=postgresql+asyncpg://wtfx_user:wtfx_pass@postgres:5432/wtfx_db
|
||||||
|
- REDIS_URL=redis://redis:6379/0
|
||||||
|
depends_on:
|
||||||
|
- postgres
|
||||||
|
- redis
|
||||||
|
|
||||||
|
postgres:
|
||||||
|
image: postgres:16-alpine
|
||||||
|
container_name: wtfx-postgres
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
POSTGRES_USER: wtfx_user
|
||||||
|
POSTGRES_PASSWORD: wtfx_pass
|
||||||
|
POSTGRES_DB: wtfx_db
|
||||||
|
volumes:
|
||||||
|
- pgdata:/var/lib/postgresql/data
|
||||||
|
ports:
|
||||||
|
- "5432:5432"
|
||||||
|
|
||||||
|
redis:
|
||||||
|
image: redis:7-alpine
|
||||||
|
container_name: wtfx-redis
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- redisdata:/data
|
||||||
|
ports:
|
||||||
|
- "6379:6379"
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
pgdata:
|
||||||
|
redisdata:
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
fastapi>=0.111.0
|
||||||
|
uvicorn[standard]>=0.30.0
|
||||||
|
pydantic>=2.7.0
|
||||||
|
pydantic-settings>=2.3.0
|
||||||
|
sqlalchemy[asyncio]>=2.0.30
|
||||||
|
asyncpg>=0.29.0
|
||||||
|
alembic>=1.13.1
|
||||||
|
redis>=5.0.4
|
||||||
|
web3>=6.19.0
|
||||||
|
eth-account>=0.11.2
|
||||||
|
pyjwt>=2.8.0
|
||||||
|
python-multipart>=0.0.9
|
||||||
|
httpx>=0.27.0
|
||||||
|
pytest>=8.2.0
|
||||||
|
pytest-asyncio>=0.23.7
|
||||||
Reference in New Issue
Block a user