FastAPI+SQLModel开发:该端点设计是否符合整洁架构?
你的FastAPI+SQLModel写法是否符合整洁架构?
首先直接给出结论:这种直接在REST端点中编写数据库逻辑的写法,并不完全符合整洁架构的核心原则,但它是FastAPI+SQLModel为快速开发设计的便捷方案——是否适用,取决于你的项目规模和长期维护需求。
为什么不符合整洁架构?
整洁架构的核心是依赖规则:内层(业务逻辑、实体)不依赖外层(Web框架、ORM、数据库),所有依赖都向内指向核心业务;同时要求关注点分离,让数据访问、业务逻辑、接口层各司其职。
而你的代码中:
- 数据库查询(仓储层职责)直接写在FastAPI端点(Web层)中,导致Web框架层直接依赖数据访问细节,违反了依赖规则
- 业务逻辑(如果后续需要添加,比如权限校验、数据过滤)只能嵌入端点代码,会让Web层变得臃肿,逻辑分散难以维护
FastAPI+SQLModel的设计初衷
FastAPI和SQLModel的结合确实是为了降低样板代码、加速原型开发——它们通过Pydantic实现了数据库模型和API模型的无缝转换,让开发者快速写出可运行的接口。这种写法非常适合小型项目、原型验证,但当应用规模增长后,会逐渐暴露出问题:
- 代码重复:多个端点会出现类似的查询逻辑,修改时要同步改多处
- 业务逻辑分散:所有规则都堆在端点里,时间久了端点会变成“大泥球”
- 测试成本高:要验证数据查询或业务逻辑,必须启动FastAPI服务,无法单独测试核心逻辑
- 框架锁定:如果后续更换Web框架或ORM,所有端点的数据库逻辑都要重写
如何适配整洁架构(同时保留FastAPI的优势)
结合你之前Flask+SQLAlchemy的分层经验,完全可以把这套架构迁移到FastAPI中,核心是重新划分三层职责,同时利用FastAPI的依赖注入来解耦:
1. 仓储层:封装数据访问逻辑
把所有SQLModel的Session操作集中到仓储类中,让Web层和用例层都不直接接触数据库细节:
# repository/song_repository.py from sqlmodel import Session, select from models.song import Song class SongRepository: def __init__(self, session: Session): self.session = session def get_all_songs(self) -> list[Song]: result = self.session.execute(select(Song)) return result.scalars().all() # 后续可以扩展其他方法:get_song_by_id、create_song等
2. 用例层:封装核心业务逻辑
用例层调用仓储层,实现业务规则(比如数据过滤、权限校验),这层是应用的核心,不依赖任何外部框架:
# use_cases/get_songs_use_case.py from repository.song_repository import SongRepository from models.song import Song class GetSongsUseCase: def __init__(self, song_repo: SongRepository): self.song_repo = song_repo def execute(self) -> list[Song]: # 示例:添加业务逻辑,只返回已发布的歌曲 all_songs = self.song_repo.get_all_songs() return [song for song in all_songs if song.is_published]
3. REST层:只做接口适配
FastAPI端点只负责接收请求、调用用例、返回响应,不包含任何业务或数据逻辑:
# api/songs.py from fastapi import APIRouter, Depends from sqlmodel import Session from database import get_session from repository.song_repository import SongRepository from use_cases.get_songs_use_case import GetSongsUseCase from models.schemas import SongRead # 单独的Pydantic响应模型,与数据库模型解耦 router = APIRouter(prefix="/songs") # 依赖注入:提供仓储实例 def get_song_repo(session: Session = Depends(get_session)) -> SongRepository: return SongRepository(session) # 依赖注入:提供用例实例 def get_get_songs_use_case(repo: SongRepository = Depends(get_song_repo)) -> GetSongsUseCase: return GetSongsUseCase(repo) @router.get("", response_model=list[SongRead]) def get_songs(use_case: GetSongsUseCase = Depends(get_get_songs_use_case)): return use_case.execute()
额外优化:分离数据库模型与API模型
建议把SQLModel的数据库模型(Song)和Pydantic的API模型(SongRead、SongCreate)分开,这样数据库结构的变化不会直接影响API响应格式,进一步解耦各层。
总结
- 小型项目/原型:直接用FastAPI+SQLModel的快速写法完全没问题,开发效率优先
- 中大型/长期维护项目:必须遵循整洁架构分层,虽然初期多写一些代码,但能让核心业务逻辑独立于框架,降低后续维护成本,也更容易扩展和测试
内容的提问来源于stack exchange,提问作者André Carvalho
相关产品推荐
相关产品推荐

