When I develop with Flask and SQLAlchemy, I commonly use the following layers:
- A repository layer, that deals with database connections and data persistence;
- A use cases layers, a bunch of functions that use the repository and do exactly what the application is intented to do;
- A rest layer where my REST endpoints where located.
I get this architectural pattern from the book "Clean Architectures in Python", by Leonardo Giordani. You can get the book here.
Recently I started to learn FastAPI and SQLModel ORM. The whole idea of using Pydantic in SQLModel and FastAPI create the possibility to do an endpoint like this:
@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]
In this endpoint the very database logic is dealt with, as the endpoint receives a session as a dependency and executes a query to get the results and, using Pydantic's magic, receives parsed and validated data and responds with a Pydantic model.
My question is: something like this is considered an clean architecture? Based on the principles I've learn, we have the web framework doing almost everything. And I think the very idea of using Pydantic both in SQLModel and FastAPI is to allow us to create endpoints like that.
But this is clean? When my app grows, couldn't I be limited by this kind of design?
Your question is worth.
3 layer architecture is not only in your mentioned book, it is very common over web application.
Your code seems simple and looks like no need to be layered. It does not mean FastAPI are not for 3 layer arch. It means your sample code is too simple, it is just CRUD: read model from db, covert it to set of pydantic instance and return it. If your api only need CRUD data on single Table then your code is perfect for purpose.
On most backend, as business grow, api logic get bigger and more complexed. Api will deal with one or more Table, and need complicated logic. If you keep this kind of code, your code will lose efficient.
Let me show simple example,