关于 system-architecture
面向进行模块、服务或重大功能设计的开发者,提供 Python/React 分层架构、数据库与状态管理决策指引,并通过 architecture.md 和 ADR 记录决策。
主要功能
- 01分层架构与依赖方向规则
- 02架构方案评估流程
- 03数据库设计与迁移建议
- 04React 状态管理指引
- 05输出架构文档与 ADR
适用场景
- 01规划新模块或子系统
- 02评估数据库结构变更
- 03选择前端状态管理方案
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ hieutrtr/ ai1-skills --skill system-architecture安装后在 Claude Code 中输入/system-architecture确认已加载。
附带文件references/architecture-decision-record-template.mdreferences/layer-responsibilities.md
详细介绍
系统架构
适用场景
在以下情况下启用此技能:
- 设计需要做出结构性决策的新模块、服务或重大功能
- 在不同架构方案之间做出选择(例如,逻辑应放在哪里、数据流应如何组织)
- 规划数据库架构变更或重构现有架构
- 决定前端状态管理方案(服务端状态与客户端状态、Context 与 Store)
- 评估新能力的技术取舍
- 创建或审查架构决策记录(ADR)
- 从头搭建新项目或主要子系统
输入: 如果存在 plan.md(由 project-planner 生成),请阅读以了解功能范围和受影响模块的背景。否则,直接根据用户请求开展工作。
输出: 将架构决策写入 architecture.md,并在 docs/adr/ADR-NNN-<title>.md 中创建 ADR。告知用户:“架构已写入 architecture.md。运行 /api-design-patterns 编写 API 合约,或运行 /task-decomposition 制定实现任务。”
请勿将此技能用于:
- 编写实现代码(使用
python-backend-expert或react-frontend-expert) - 设计 API 合约或端点规范(使用
api-design-patterns) - 制定测试模式或策略(使用
pytest-patterns或react-testing-patterns) - 做出部署或基础设施决策(使用
docker-best-practices或deployment-pipeline)
说明
项目分层架构
标准的 Python/React 全栈架构遵循分层模式,并严格遵守依赖方向。
后端分层(FastAPI)
HTTP Request
↓
┌─────────────────────┐
│ Routers (routes/) │ ← HTTP concerns: request parsing, response formatting, status codes
│ │ Uses: Depends() for injection, Pydantic schemas for validation
├─────────────────────┤
│ Services │ ← Business logic: orchestration, validation rules, domain operations
│ (services/) │ No HTTP awareness. Raises domain exceptions, not HTTPException.
├─────────────────────┤
│ Repositories │ ← Data access: queries, CRUD operations, database interactions
│ (repositories/) │ No business logic. Returns model instances or None.
├─────────────────────┤
│ Models (models/) │ ← SQLAlchemy ORM models: table definitions, relationships, indexes
│ Schemas (schemas/) │ ← Pydantic v2 models: request/response contracts, validation
└─────────────────────┘
↓
Database
依赖方向规则:
- 路由器依赖服务(绝不直接依赖仓储)
- 服务依赖仓储(绝不依赖路由器)
- 仓储依赖模型(绝不依赖服务)
- 架构各层共享 Schema,但 Schema 本身不定义任何依赖
- 绝不跳过层级:路由中不得直接访问数据库
依赖注入模式:
# Router depends on Service via Depends()
@router.post("/users", response_model=UserResponse)
async def create_user(
data: UserCreate,
service: UserService = Depends(get_user_service),
) -> UserResponse:
return await service.create_user(data)
# Service depends on Repository via constructor injection
class UserService:
def __init__(self, repo: UserRepository) -> None:
self.repo = repo
# Repository depends on AsyncSession via Depends()
class UserRepository:
def __init__(self, session: AsyncSession) -> None:
self.session = session
前端分层(React/TypeScript)
┌─────────────────────┐
│ Pages (pages/) │ ← Route-level components: data fetching, layout composition
├─────────────────────┤
│ Layouts │ ← Page structure: navigation, sidebars, content areas
│ (layouts/) │
├─────────────────────┤
│ Features │ ← Domain-specific: UserProfile, OrderList, ChatPanel
│ (features/) │ Composed from shared components + hooks
├─────────────────────┤
│ Shared Components │ ← Reusable UI: Button, Modal, Table, Form, Input
│ (components/) │ No business logic. Configurable via props.
├─────────────────────┤
│ Hooks (hooks/) │ ← Custom hooks: useAuth, usePagination, useDebounce
│ API (api/) │ ← API client functions, TanStack Query configurations
├─────────────────────┤
│ Types (types/) │ ← Shared TypeScript interfaces and type definitions
└─────────────────────┘
组件依赖方向:
- 页面导入功能模块和布局
- 功能模块导入共享组件和 Hooks
- 共享组件只能导入其他共享组件和类型
- Hooks 导入 API 函数和类型
- API 函数只能导入类型
决策框架
遇到架构决策时,请遵循以下结构化流程:
第 1 步:明确问题
- 需要什么能力?
- 有哪些非功能性需求?(性能、可扩展性、可维护性)
- 存在哪些限制?(团队规模、时间安排、现有基础设施)
第 2 步:确定选项
- 列出 2–3 个可行的架构方案
- 对于每个方案,记录:
- 工作方式(简要的技术说明)
- 优势
- 劣势
- 风险
第 3 步:根据标准评估
| 标准 | 权重 | 说明 |
|---|---|---|
| 可维护性 | 高 | 团队能否轻松理解、修改和调试? |
| 可测试性 | 高 | 能否单独测试每个组件? |
| 性能 | 中 | 是否满足延迟和吞吐量要求? |
| 团队熟悉度 | 中 | 团队是否有使用此方案的经验? |
| 运维成本 | 低 | 基础设施和维护成本是多少? |
| 未来灵活性 | 低 | 随着需求变化,此方案能否轻松演进? |
第 4 步:做出决定并记录
- 选择最符合加权标准的方案
- 在 ADR 中记录决策(参见
references/architecture-decision-record-template.md) - 记录未选择的方案及原因——这些背景信息对未来决策很有价值
第 5 步:沟通
- 与团队分享 ADR
- 确定所需的迁移或发布步骤
- 标注可逆性:这是单向门还是双向门?
数据库架构设计
设计原则
- 从规范化开始(3NF)——只有确认存在性能瓶颈时才进行反规范化,切勿凭空猜测
- 每项逻辑变更对应一个迁移——每个 Alembic 迁移都应代表一项独立、完整的架构修改
- 始终包含降级操作——每个迁移都必须有可正常运行的
downgrade()函数 - 有策略地创建索引:
- 主键(自动创建)
- 外键(始终创建)
- 常用查询的 WHERE 子句中涉及的列
- 多列查询使用复合索引
- 筛选查询使用部分索引(例如
WHERE is_active = true)
SQLAlchemy 2.0 异步模式
# Model definition with Mapped types (SQLAlchemy 2.0 style)
class User(Base):
__tablename__ = "users"
id: Mapped[int] = mapped_column(primary_key=True)
email: Mapped[str] = mapped_column(String(255), unique=True, index=True)
is_active: Mapped[bool] = mapped_column(default=True)
created_at: Mapped[datetime] = mapped_column(server_default=func.now())
# Relationships: ALWAYS use eager loading with async
posts: Mapped[list["Post"]] = relationship(
back_populates="author",
lazy="selectin", # or "joined" — NEVER "lazy" with async
)
异步会话规则:
- 每个请求使用一个
AsyncSession——绝不在并发任务之间共享 - 使用
async with上下文管理器自动清理 - 让会话边界与事务边界保持一致
- 使用
selectin或joined加载——延迟加载与 asyncio 不兼容 - 仅在处理遗留代码时,才将
run_sync()作为最后手段
迁移规划
- 架构变更 → 生成迁移:
alembic revision --autogenerate -m "description" - 检查生成的迁移——验证列类型、索引和约束
- 测试升级:
alembic upgrade head - 测试降级:
alembic downgrade -1 - 测试数据保留:确保现有数据在往返操作后仍然保留
前端架构
状态管理决策树
Is the data from the server?
├── YES → Use TanStack Query (useQuery, useMutation)
│ Configure staleTime, gcTime, query keys
│
└── NO → Is it needed across multiple components?
├── YES → Is it complex with actions/reducers?
│ ├── YES → Use Zustand store
│ └── NO → Use React Context
│
└── NO → Use useState / useReducer locally
TanStack Query 约定:
- 查询键:
[resource, ...identifiers](例如["users", userId]、["posts", { page, limit }]) - 使用
queryOptions()工厂集中定义键和函数,避免复制粘贴导致的键错误 - 根据数据新鲜度需求设置
staleTime(默认值 0 对大多数情况来说过于激进) - 变更后使用
invalidateQueries()使缓存失效——不要手动调用refetch() - 处理所有状态:
isPending、isError、data
组件设计规则:
- 使用 props 配置组件,使用 hooks 获取数据
- 仅在必要时向上提升状态——不要过早创建 context
- 组件保持在 200 行以内——超出时提取子组件或自定义 hooks
- 优先使用
children和组合,而非深层 props 传递
路由结构
组织路由,使其与 URL 结构保持一致:
src/
├── pages/
│ ├── HomePage.tsx → /
│ ├── LoginPage.tsx → /login
│ ├── users/
│ │ ├── UserListPage.tsx → /users
│ │ └── UserDetailPage.tsx → /users/:id
│ └── settings/
│ └── SettingsPage.tsx → /settings
横切关注点
身份验证流程
Login Request
↓
Backend: Validate credentials → Generate JWT (access + refresh tokens)
↓
Frontend: Store access token in memory, refresh token in httpOnly cookie
↓
API Calls: Attach access token via Authorization header
↓
Token Expired: Use refresh token to obtain new access token
↓
Refresh Failed: Redirect to login
身份验证的架构决策:
- 访问令牌:有效期较短(15-30 分钟),存储在内存中(而非 localStorage)
- 刷新令牌:有效期较长(7-30 天),存储在 httpOnly cookie 中
- 后端:使用 FastAPI
Depends()链进行令牌验证 → 提取用户信息 → 检查权限 - 前端:Auth context 提供
user、login()、logout()、isAuthenticated
错误处理策略
应在适当的层级处理错误:
| 层级 | 错误类型 | 操作 |
|---|---|---|
| 路由器 | HTTPException | 返回带状态码的 HTTP 错误响应 |
| 服务 | 领域异常 | 抛出自定义异常(例如 UserNotFoundError) |
| 存储库 | 数据库异常 | 捕获并重新抛出为领域异常,或直接向上传播 |
| 前端 | API 错误 | 显示用户易于理解的消息,并在适当时重试 |
后端异常层级结构:
class AppError(Exception):
"""Base application error."""
class NotFoundError(AppError):
"""Resource not found."""
class ConflictError(AppError):
"""Resource conflict (duplicate, version mismatch)."""
class ValidationError(AppError):
"""Business rule violation."""
路由器级异常处理程序会将领域异常映射为 HTTP 响应:
@app.exception_handler(NotFoundError)
async def not_found_handler(request: Request, exc: NotFoundError):
return JSONResponse(status_code=404, content={"detail": str(exc)})
日志架构
后端(structlog):
- 在生产环境中输出结构化 JSON 日志
- 在开发环境中输出便于阅读的控制台日志
- 在中间件层绑定请求上下文(request_id、user_id)
- 在服务层记录日志(业务事件),而非在存储库层记录(过于嘈杂)
- 使用日志级别:DEBUG(仅开发环境)、INFO(业务事件)、WARNING(可恢复的问题)、ERROR(需要关注的故障)
前端:
- 在开发环境中使用
console.* - 在生产环境中向后端或 Sentry 发送结构化错误报告
- 记录用户操作以便调试,而非用于分析
配置管理
后端(pydantic-settings):
class Settings(BaseSettings):
model_config = SettingsConfigDict(env_file=".env")
database_url: str
redis_url: str = "redis://localhost:6379"
jwt_secret: str
debug: bool = False
前端(环境变量):
- 使用
VITE_API_URL设置 API 基础 URL - 通过 Vite 的
import.meta.env在构建时注入 - 前端环境变量中不存放密钥
输出文件
architecture.md
在项目根目录下将架构文档写入 architecture.md:
# Architecture: [Feature/System Name]
## Overview
[1-2 sentence summary of the architectural approach]
## Layer Structure
[Backend and frontend layer descriptions from this skill's patterns]
## Key Decisions
[Summary of decisions made, with links to ADRs]
## Database Schema
[Entity descriptions, relationships, key indexes]
## Cross-Cutting Concerns
[Auth, error handling, logging approach]
## Next Steps
- Run `/api-design-patterns` to define API contracts
- Run `/task-decomposition` to create implementation tasks
ADR
对于每个重要决策,在 docs/adr/ 中创建一份 ADR:
# ADR-NNN: [Decision Title]
## Status
Accepted | Proposed | Superseded
## Context
[Why this decision is needed]
## Decision
[What we decided]
## Consequences
[Positive and negative outcomes]
按顺序为 ADR 编号(ADR-001、ADR-002 等)。
示例
架构决策:实时通知
问题: 应用需要为用户提供实时通知(新消息、状态更新)。
评估的选项:
| 选项 | 优点 | 缺点 |
|---|---|---|
| WebSocket | 真正的双向通信、延迟低 | 连接管理复杂、扩展更困难 |
| Server-Sent Events (SSE) | 简单、基于 HTTP、自动重连 | 单向通信(仅服务器→客户端)、浏览器连接数有限 |
| Polling | 实现最简单、到处都能运行 | 延迟较高、造成不必要的服务器负载 |
决策: 在此场景中使用 WebSocket。
理由: 通知需要低延迟,而且系统最终需要双向通信(输入状态指示、在线状态)。仅针对通知而言,SSE 可以胜任,但未来需要双向通信时还得采用另一套方案。轮询会导致实时用户体验无法接受的延迟。
架构:
- 后端:FastAPI WebSocket 端点,使用
ConnectionManager类 - 前端:自定义
useWebSockethook,支持自动重连 - 扩展:使用 Redis pub/sub 分发多实例消息
- 持久化:将通知存储在数据库中,以供离线用户查看
- 回退方案:使用 REST 端点提供通知历史和初始加载
完整的 ADR 格式请参阅 references/architecture-decision-record-template.md。
边缘情况
单体架构与微服务
对于开发人员少于 10 人的团队,默认采用模块化单体架构。模块化单体架构具备以下优点:
- 模块边界清晰,且没有网络开销
- 共用数据库,同时使用模块专属的架构
- 易于重构和浏览代码
- 部署和调试简单
仅在以下情况下考虑微服务:
- 特定组件需要独立扩展
- 不同模块需要使用不同的技术栈
- 团队规模超过 10 人,且所有权边界清晰
- 业务要求部署独立
迁移路径: 在单体架构中设计模块边界时,应将模块视作服务(不直接跨模块访问数据库,而是通过服务接口通信)。这样在有需要时,就能轻松拆分为微服务。
何时打破分层模式
对于标准 CRUD 操作,应遵循严格的 Router → Service → Repository 模式。可接受的例外包括:
- 后台任务: 可以直接调用服务,无需经过路由器
- 事件处理器: 领域事件监听器可以在任何上下文中调用服务
- CLI 命令: 管理脚本可以直接访问服务或仓库
- 迁移: 数据迁移可以直接访问模型(无需服务层或仓库层)
- 健康检查: 可以直接访问数据库,进行简单的连通性验证
在所有情况下,业务逻辑仍应放在服务层中——这些例外涉及的是入口点,而不是绕过业务规则。
演进架构
需要更改架构时:
- 编写 ADR,记录更改的动机和提议
- 确定所有受影响的模块及其依赖项
- 规划渐进式迁移——绝不进行大爆炸式重写
- 在过渡期间保持向后兼容(绞杀者无花果模式)
- 设定完成迁移并移除旧代码的期限