LLMOP

LLMOP FastAPI 项目编程规范

本文档定义了基于 LLMOP (Large-language-model-Oriented Programming) 原则的 FastAPI 项目的编程规范。 LLMOP 的核心思想是接口极小、命名唯一易理解、契约与实现硬绑定, 旨在让代码对大型语言模型 (LLM) 更易于理解、维护和扩展。

FastAPI LLMOP Python SQLAlchemy

1. LLMOP 核心原则

接口极小 (Minimal Interface)

  • R1.1.1采用五层架构,每层职责明确且接口最小化。
  • R1.1.2每个模块只暴露必要的接口,隐藏内部实现细节。
  • R1.1.3模块间交互只能通过 *_api.py 层进行。

命名唯一 (Unique Naming)

  • R1.2.1所有命名不言自明,清晰反映业务含义和技术角色。
  • R1.2.2避免使用模糊或过于简短的名称。
  • R1.2.3禁止无业务逻辑的命名,如 get_info

契约与实现硬绑定

  • R1.3.1*_api.py 中使用 typing.Protocol 定义服务契约。
  • R1.3.2API 层是模块唯一对外接口。
  • R1.3.3修改 service 公有方法,需同步修改 api 接口。

2. 项目结构规范

2.1 根目录结构

R2.1.1

核心应用代码位于 app/ 目录下。

R2.1.2

根目录必须包含:main.py, requirements.txt, env.example, .gitignore

2.2 应用目录结构 (app/)

应用目录按照功能分离,职责清晰:

  • module/: 业务模块目录,包含所有业务领域模块。
  • util/: 全局工具函数目录,存放完全不依赖特定业务模块、具有普适性的工具函数。
  • core/: 存放应用核心组件,如配置、数据库、认证、异常、中间件等。

2.3 业务模块结构 (app/module/*/)

R2.3.1

每个业务模块必须包含以下文件,构成五层架构:

  • *_model.py
  • *_util.py
  • *_service.py
  • *_api.py
  • *_router.py
  • test/

3. 文件和命名规范

3.1 文件命名

R3.1.1

使用小写蛇形命名法 (snake_case) 并用全称,例如 user_model.py

R3.1.2

统一使用单数形式:所有文件名、目录名、类名、变量名等均使用单数形式。

R3.1.3

每个文件开始的第一行注释必须是文件名的相对路径,如:# app/module/dummy/dummy_router.py

3.2 类命名

R3.2.2

模型类命名必须包含 Model 后缀,如 UserModel

R3.2.3

Pydantic schema 命名规范:以 <资源><动作?>In/Out 命名,如 UserCreateIn, UserOut

3.3 函数和变量命名

R3.3.3

函数/方法命名要含义清楚,哪怕很长。不要用 get_user,应该用 get_user_by_id

4. 五层架构规范

4.1 架构依赖关系

依赖关系流向图

Router
API
Service
Utils / Model

R4.1.2

跨模块交互:任何层只能依赖对方的 API 层 (*_api.py),不得直接依赖对方的 Service 或 Model。

R4.1.3

严禁反向依赖和循环依赖。

5. 代码风格规范

5.2 类型提示

R5.2.1

所有 Python 文件都必须在文件开头添加 from __future__ import annotations

R5.2.2

所有函数、方法参数和返回值都必须有明确的类型提示 (Type Hints)。

R5.2.3

函数局部变量不需要添加类型提示,保持代码简洁性。

# 正确:函数参数和返回值有类型提示,局部变量无类型提示
async def create_user(self, *, user_data: UserCreateIn) -> UserOut:
    # 局部变量不需要类型提示
    user_dict = user_data.model_dump()
    new_user = UserModel(**user_dict)
    self.db.add(new_user)
    await self.db.flush()
    return UserOut.model_validate(new_user)

# 错误:局部变量添加了类型提示,导致代码冗余
async def create_user_wrong(self, *, user_data: UserCreateIn) -> UserOut:
    user_dict: dict = user_data.model_dump()  # 错误:不需要
    new_user: UserModel = UserModel(**user_dict)  # 错误:不需要
    # ...

6. 异常处理规范

R6.1.1

app/core/exceptions.py 中定义基础异常类,数量控制在 6 个以内,全部继承自 fastapi.HTTPException

R6.2.3

Router 层不处理异常。异常统一在 main.py 中通过全局异常处理器捕获并返回标准化的 JSON 响应。

# app/core/exceptions.py
from fastapi import HTTPException

class AuthenticationRequiredException(HTTPException):
    def __init__(self, detail: str = "Authentication required"):
        super().__init__(status_code=401, detail=detail)

class PermissionDeniedException(HTTPException):
    def __init__(self, detail: str = "Permission denied"):
        super().__init__(status_code=403, detail=detail)

class ResourceNotFoundException(HTTPException):
    def __init__(self, detail: str = "Resource not found"):
        super().__init__(status_code=404, detail=detail)
# ... etc.

7. FastAPI 特定规范

7.1 依赖注入

R7.1.1

广泛使用 FastAPI 的依赖注入系统 (Depends) 来获取数据库会话、认证用户和服务实例。

R7.1.3

依赖项使用 Protocol 接口类型提示,而非具体实现类,以实现解耦。

7.3 认证与授权

R7.3.2

Actor 定义:在所有上下文中,actor 都是指当前操作的用户数据,类型为 UserOut | None

R7.3.5

授权主要在 Service 层进行。服务通过构造函数接收 actor,并在内部方法中进行细粒度的权限控制。

8. 数据库规范

8.2 Session 管理

R8.2.1

统一的 Session 生命周期通过 Depends(get_db) 管理,实现每个请求一个事务。

# app/core/db.py
async def get_db() -> AsyncGenerator[AsyncSession, None]:
    async with AsyncSessionLocal() as session:
        try:
            yield session
            await session.commit()
        except Exception:
            await session.rollback()
            raise

9. 配置管理规范

R9.2

使用 Pydantic Settings 类集中管理所有应用配置。

R9.6

敏感信息(如数据库密码)必须通过环境变量或 env 文件管理,绝不能硬编码。

10. 日志规范

R10.2

推荐使用结构化日志格式(JSON),便于日志分析和监控。

R10.4

日志中应包含 request_id 以串联调用链,通过 ContextVar 和中间件实现。

11. 测试规范

11.6 测试环境隔离 (核心原则)

重要

测试代码中严禁直接从 main.py 导入全局的 app 对象。必须在测试 fixture 中动态创建一个全新的、隔离的 FastAPI 实例,并使用 dependency_overrides 覆盖数据库依赖。

11.7 全局 Fixture 配置 (conftest.py)

项目根目录的 conftest.py 必须提供核心 fixtures,以确保测试环境的隔离性和一致性。

# conftest.py (位于项目根目录)
import pytest
from httpx import AsyncClient
from fastapi import FastAPI
from app.core.db import get_db, Base

@pytest.fixture(scope="function")
async def test_db():
    # ... setup in-memory db ...
    yield session
    # ... teardown db ...

@pytest.fixture(scope="function")
async def client(test_db: AsyncSession):
    test_app = FastAPI()
    
    # [核心] 覆盖数据库依赖
    test_app.dependency_overrides[get_db] = lambda: test_db

    async with AsyncClient(app=test_app, base_url="http://test") as ac:
        yield ac, test_app

    test_app.dependency_overrides.clear()

11.8 认证客户端 Fixture (最佳实践)

R11.8.3

Fixture 内部应执行完整的业务流程来获取认证状态(如调用注册和登录服务),而不是模拟(Mock)认证依赖,这样更能有效地测试完整的集成链路。

12. 静态类型检查规范

R12.1

在 CI 流水线中使用 mypy --strict app/ 对整个应用进行静态类型检查。

13. 模块扩展规范

13.2 模块间交互

R13.2.2

Service 层可以调用其他模块的 API 层服务。通过服务工厂函数 create_*_service(db, actor) 直接创建所需的服务实例。

14. 服务工厂模式规范

R14.1

为了实现类型安全并明确职责,服务创建分为两个独立的函数:

  • get_*_service_with_depends(...): 专用于 FastAPI 依赖注入。
  • create_*_service(db, actor): 专用于直接的、程序化的服务创建。

15. 数据契约规范

R15.2

API 模型字段类型必须与数据库模型保持一致,避免类型不匹配导致的验证错误。

R15.3

时间字段统一使用 datetime 类型,由 Pydantic 自动处理序列化,不要使用 str 类型。

16. 循环依赖处理规范

R16.2.1

延迟导入法(推荐):在需要调用的函数内部进行导入,而不是在文件顶部,这是解决 Python 循环导入最直接有效的方法。

# app/module/user/user_service.py
class UserService(UserServiceProtocol):
    async def get_user_with_orders(self, *, user_id: int) -> UserWithOrdersOut:
        # 延迟导入,避免在文件顶层产生循环依赖
        from app.module.order import order_api
        
        user = await self._get_user_by_id(user_id=user_id)
        order_service = order_api.create_order_service(db=self.db, actor=self.actor)
        orders = await order_service.get_orders_by_user_id(user_id=user_id)
        
        return UserWithOrdersOut(user=user, orders=orders)

17. 完整项目目录结构

project_root/
├── app/                       # 核心应用代码
│   ├── __init__.py
│   ├── core/                  # 核心组件 (配置, DB, 认证, 异常)
│   ├── module/                # 业务模块
│   │   └── user/              # 用户模块示例
│   │       ├── __init__.py
│   │       ├── user_api.py
│   │       ├── user_model.py
│   │       ├── user_router.py
│   │       ├── user_service.py
│   │       ├── user_util.py
│   │       └── test/          # 模块测试
│   └── util/                  # 全局工具函数
├── conftest.py                # 全局测试 Fixture 配置
├── data/                      # 数据目录 (e.g., SQLite DB)
├── env                        # 环境变量文件 (git ignored)
├── env.example                # 环境变量模板
├── log/                       # 日志文件目录
├── main.py                    # FastAPI 应用入口点
├── migration/                 # 数据库迁移文件目录
├── pytest.ini                 # Pytest 配置文件
├── requirements.txt           # 项目依赖
├── script/                    # 辅助脚本
└── .gitignore                 # Git 忽略规则