Skip to content

suiseriff/pure-architecture-fastapi

Repository files navigation

Implementation of Clean Architecture (Uncle Bob) on FastAPI.


Test Passing License Documentation


Project Overview

  • how to organize a project and prevent it from turning into spaghetti code
  • where to store business logic so that it remains independent, clean, and extensible
  • how not to lose control when a microservice grows

Using the principles of Robert Martin (aka Uncle Bob).

Documentation

The project documentation is built with MkDocs and available at GitHub Pages.

To build and serve locally:

just docs-serve

Access at http://localhost:8000

Quick start

Local development

git clone https://github.com/Maclovi/pure-architecture-fastapi
cd pure-architecture-fastapi

python -m venv .venv
source .venv/bin/activate

just bootstrap

Integration tests via coverage (can be run in CI)

just infra
just cov

Full docker stack

 just up

Configuration Management: configs.py

Following the twelve-factor app methodology, configuration is managed via environment variables (commonly referred to as env vars). These variables offer a flexible way to adjust settings across deployments without altering the codebase. Unlike traditional config files, they minimize the risk of accidental inclusion in version control and provide a universal, language- and OS-independent approach to configuration.

  • Configuration file: src/cats/infrastructure/configs.py
  • Sample environment file: .env.dist

The file leverages these env vars to define service settings.

Documentation: docs

API documentation is available via Swagger, automatically generated by FastAPI. No manual updates are required—just use it as is.

Application Entry Point: src/cats/web.py

The web.py file contains a single create_app function, serving as the central hub for initializing key application components. Dependency injection is handled here using the "dishka" framework, enabling a modular architecture where business logic remains decoupled from other layers.

Database migrations are managed with the file alembic.ini. To apply migrations, run:

python -m alembic upgrade head

Presentation Layer: src/cats/presentation/

This layer handles server-side logic (akin to MVC controllers). The project currently supports two server types:

  • RESTful HTTP server (built with FastAPI)
  • RPC server (planned with NATS transport, implementation pending)
  • Routers follow a consistent design:
  • Handlers are organized by functional domain.
  • Each domain gets its own router, processing specific routes.
  • Business logic is injected into these routers for seamless execution.

HTTP Presentation: src/cats/presentation/http

REST API versioning is straightforward. To introduce a v2 version, create an http/v2 directory with the updated content and modify src/cats/bootstrap/routes.py as follows:

def setup_routes(app: "FastAPI", /) -> None:
    router_v1 = APIRouter(prefix="/v1")
    router_v1.include_router(cats.router)

    router_v2 = APIRouter(prefix="/v2")
    router_v2.include_router(v2cats.router)

    app.include_router(router_v1)
    app.include_router(router_v2)

While FastAPI is the default choice, you’re free to swap it for another HTTP framework if needed.

Business Models: src/cats/entities

This directory holds the business logic entities (models), reusable across all layers. These can include methods, such as validation logic, where applicable.

Core Logic: src/cats/application

The heart of the application—business logic—is housed here:

  • Organized into domain-specific groups.
  • Each group is encapsulated in its own structure.
  • One structure per file for clarity.

Persistence (Infrastructure Layer): src/cats/infrastructure/persistence

Repositories act as abstract data stores (e.g., databases) that the business logic interacts with, keeping storage concerns separate.

Dependency Injection

To ensure business logic remains independent of external dependencies, we use dependency injection via the dishka framework. Dependencies are injected into the business logic structures, making them portable and allowing interface implementations to be swapped without touching the application package.

Architectural Approach: Clean Architecture

Core Concept

The best architecture often emerges late in development, once the codebase takes shape.

A solid architecture postpones decisions until the last responsible moment.

Guiding Principle

Dependency Inversion (from SOLID) drives our use of dependency injection. Dependencies flow inward, from outer tools to inner business logic, ensuring the latter remains isolated. This splits the app into two primary layers:

  1. Inner Layer: Business logic (use cases).
  2. External Layer Tools (databases, servers, brokers, external libraries).

Inner Layer Rules with business logic should be clean. It should:

  • No imports from the outer layer.
  • Relies solely on Python’s standard library.
  • Communicates with the outer layer via interfaces.

The business logic is agnostic to specifics like PostgreSQL or REST APIs—it interacts with abstract interfaces instead.

External Layer Constraints has other limitations:

  • Components (e.g., HTTP servers, databases) don’t directly interact with each other.
  • Communication with the core layer happens through interfaces.
  • Data is formatted to suit the business logic (see src/cats/entities).

For instance, fetching data from a database via an HTTP request:

  HTTP → Use Case
        Use Case → Repository (e.g., Postgres)
        Use Case ← Repository
  HTTP ← Use Case

A more intricate flow might look like:

  HTTP → Use Case
        Use Case → Repository
        Use Case ← Repository
        Use Case → External API
        Use Case ← External API
        Use Case → RPC
        Use Case ← RPC
        Use Case → Repository
        Use Case ← Repository
  HTTP ← Use Case

Layer Interactions

The > and < symbols denote interface-based boundary crossings between layers.

Terminology

  • Entities: Business logic data structures in src/cats/entities (akin to MVC models).
  • Use Cases: Business rules in src/cats/application.

The infrastructure layer (e.g., src/cats/infrastructure/persistence) bridges business logic to tools like databases or external services. Entry points to the app can be named flexibly—options include controller (our choice), gateway, transport, or input.

Expanding Layers

The traditional Clean Architecture model, suited for large monoliths, uses four layers. It further splits the outer layer (with inward dependencies) and, for complex logic, divides the inner layer too. Add layers only when complexity demands it.

Related Concepts

Clean Architecture aligns with Onion Architecture and Hexagonal Architecture (Ports and Adapters), all rooted in Dependency Inversion. The latter is nearly identical, differing mainly in naming conventions.

Author

Sergey - GitHub Profile

About

Implementation of Clean Architecture(Uncle Bob) on FastAPI.

Resources

License

Contributing

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 3

  •  
  •  
  •  

Languages