Implementing FastAPI Services – Abstraction and Separation of Concerns

FastAPI application and service structure for a more maintainable codebase

Published on August 11, 2020

This article introduces an approach to structure FastAPI applications with multiple services in mind. The proposed structure decomposes the individual services into packages and modules, following principles of abstraction and separation of concerns. The code discussed below can also be studied in its entirety in a dedicated companion GitHub repository.

FastAPI – Building High-performing Python APIs

FastAPI is a fast, highly intuitive and well-documented API framework based on Starlette. Despite being relatively new, it's gaining strong adoption by the developer community – it is even already being adopted in production by corporates like Netflix and Microsoft.

Following the UNIX philosophy of "doing one thing, and doing it well", separating parts of the application according to their task improves code readability and maintainability; ultimately reducing complexity. The main benefits of structuring applications in this way:

Separation of concerns – Decomposing the application into modules performing a single job. This allows accepting requests (top down: controller → service → data access) and returning responses (bottom up: data access → service → controller) with a clear separation of what particular functionality should be implemented in which particular module, reducing cognitive load for development.

Request-Response Flow across Application Layers

Request-Response Flow across Application Layers

Abstraction — Components of the application are designed in a reusable way. For instance, ServiceResult is implemented as a generic outcome of a service operation (which may be successful and return a response, or unsuccessful and raise an exception) able to be used by all services of the app, keeping code DRY.

Directory Structure Overview

Directory Structure Overview

The granular nature of namespacing allow to distinguish parts of the application, e.g., routes versus business logic belonging to a particular service – grouping similar tasks together, while keeping distinct parts separated. Four principal packages are needed. For each service, one module is added to these four packages. For instance, a service called "Foo" requires the following modules (discussed in detail below):

./routers/ # Router instance and routes
./services/ # Business logic (including CRUD helpers)
./schemas/ # Data "schemas" (e.g., Pydantic models)
./models/ # Database models (e.g., SQLAlchemy models)

The four principal packages are complemented by two generic packages which contain application-specific (and not service-specific) functionality, such as configuration or utility functions.

Controller Layer – Routes

Within main, the application is instantiated and all routers are included. Additionally, middlewares and/or exception handlers are implemented. The example application discussed below is based on a service called Foo, which requires a number of routes. To handle custom exceptions occurring at the service layer, as instances of class AppExceptionCase, a respective exception handler is added to the application.

当前网页内容, 由 大妈 ZoomQuiet 使用工具: ScrapBook :: Firefox Extension 人工从互联网中收集并分享;
若有不妥, 欢迎评注提醒:


订阅 substack 体验古早写作:

点击注册~> 获得 100$ 体验券: DigitalOcean Referral Badge

关注公众号, 持续获得相关各种嗯哼:


关于 ~ DebugUself with DAMA ;-)
公安备案号: 44049002000656 ...::