python FastAPI + SQLModel开发中的清洁架构原则

hlswsv35  于 2022-12-25  发布在  Python
关注(0)|答案(2)|浏览(743)

当我使用Flask和SQLAlchemy进行开发时,我通常使用以下层:

  • 存储库层,处理数据库连接和数据持久性;
  • 用例层,一堆使用存储库的功能,它们执行应用程序想要执行的操作;
  • REST端点所在的REST层。

我从Leonardo Giordani的《Python中的干净架构》一书中得到了这个架构模式。你可以得到here这本书。
最近我开始学习FastAPI和SQLModel ORM。在SQLModel和FastAPI中使用Pydantic的整个想法创造了这样做端点的可能性:

@app.get("/songs", response_model=list[Song])
def get_songs(session: Session = Depends(get_session)):
    result = session.execute(select(Song))
    songs = result.scalars().all()
    return [Song(name=song.name, artist=song.artist, id=song.id) for song in songs]

在这个端点中,处理的正是数据库逻辑,因为端点接收会话作为依赖项,并执行查询以获得结果,然后使用Pydantic的魔力接收经过解析和验证的数据,并使用Pydantic模型进行响应。
我的问题是:像这样的东西被认为是一个干净的架构吗?基于我学到的原则,我们有Web框架做几乎所有的事情。我认为在SQLModel和FastAPI中使用Pydantic的想法就是允许我们创建这样的端点。
但这是干净的吗?当我的应用程序增长时,我不能被这种设计所限制吗?

von4xj4u

von4xj4u1#

你的问题值得。
3层架构不仅在你提到的书中,它在Web应用程序上非常常见。
你的代码看起来很简单,看起来不需要分层。这并不意味着FastAPI不适合3层架构。这意味着你的示例代码太简单了,它只是CRUD:从数据库中读取模型,将其转换为pydantic示例集并返回。如果您的API只需要单个表上的CRUD数据,那么您的代码是完美的。
在大多数后端,随着业务的增长,API逻辑变得越来越大和复杂。Api将处理一个或多个表,并需要复杂的逻辑。如果你保持这种代码,你的代码将失去效率。
让我举个简单例子,

### when layered

from ../persistence import song_repository # persistance layer
from ../use_case import recommend_songs    # use case layer

@app.get("/songs", response_model=list[Song])
def get_songs(session: Session = Depends(get_session)):
    # data load goes to song_repository's method.
    songs:list[Song] = song_repository.get_all(session)
    return songs 

@app.get("/recommend/songs", response_model=list[DiscountPrice])
def get_recommend_songs(session: Session = Depends(get_session)):
    # you can reuse song_repository's method
    songs:list[Song] = song_repository.get_all(session)
    recommend_rule: list[Rule]= rule_repository.get_all(session) 
    recommended: list[Song] = recommend_songs(songs, recommend_rule)
    return recommended

### when logic mixed on one path operation

@app.get("/recommend/songs", response_model=list[Song])
def get_songs(session: Session = Depends(get_session)):
    # you load data again!
    result = session.execute(select(Song))
    songs = result.scalars().all()
    data = [Song(name=song.name, artist=song.artist, id=song.id) for song in songs]

    result2 = session.execute(select(Rule))
    rules = result2.scalars().all()
    data2 = [Rule(...) for rule in rules]

    #now you start to describe recommend logic,
    # blah blah but this can not reusable, only exist in this function
    recommended = ...
    
    return recommended
ebdffaop

ebdffaop2#

我质疑的是:如果我使用FastAPI + SQLModel的3层结构,那么使用这2个Pydantic Package 器的所有好处是否都会被抛弃?
简而言之,没有。
Fastapi使用pydantic模型来检查输入和输出,他们不关心路径函数内部的逻辑。这只是休息层所做的。
sql模型(可能是sqlalchemy)使用pydantic将sql模型示例数据作为数据传输对象传输或序列化。您在加载数据和响应模式中误用了相同的类Song

@app.get("/songs", response_model=list[SongSchema]) # this is pydantic class
def get_songs(session: Session = Depends(get_session)):
    result = session.execute(select(Song)) # This is SqlAlchemy class
    songs = result.scalars().all()
    return songs # You don't need to serializing it because pydantic will do. If SongScheam has orm = True on Config.

3层拱比fastapi,sqlmodel和pydantic大,所以不受它们的影响,但影响了它们。
事实上,fastapi,pydantic帮助使3层比flask更好。与flask相比,休息层变得更容易编写和全面。持久层也是如此。这给予了开发人员时间来专注于服务层。

相关问题