This repository demonstrates a scalable and maintainable Python backend following the Clean Architecture principles. It is designed to be adaptable across multiple infrastructures, including SQL, NoSQL, message queues, and caches. The project emphasizes:
- Separation of concerns to keep business logic independent of frameworks and databases.
- Dependency Injection to manage dependencies efficiently.
- Unit of Work Pattern to handle transactional consistency.
- Modular design to support extensibility and testability.
By following Clean Architecture, this project ensures that the core business logic remains decoupled from external dependencies, making it easier to scale, modify, and test.
Clean Architecture, proposed by Robert C. Martin (Uncle Bob), is a software design philosophy that ensures:
- Independence from frameworks β The business logic does not depend on external libraries.
- Testability β Core logic can be tested without external dependencies.
- Separation of concerns β Divides the system into layers with clear responsibilities.
- Scalability & Maintainability β Easy to extend and modify without breaking existing features.
ββββββββββββββββββββββββββββββββ
β Controllers β β Handles requests & responses (HTTP, gRPC, etc.)
ββββββββββββββββββββββββββββββββ€
β Services β β Coordinates business logic & interacts with use cases
ββββββββββββββββββββββββββββββββ€
β Use Cases β β Encapsulates core business logic
ββββββββββββββββββββββββββββββββ€
β Infrastructures (DB, Cache) β β Handles external dependencies like databases & queues
ββββββββββββββββββββββββββββββββ
Each layer interacts only with the layer directly below it, ensuring minimal coupling.
Dependency Injection is used to decouple components by injecting dependencies instead of creating them inside a class. It enables:
- Easier testing (mocking dependencies)
- Improved modularity
- Greater flexibility
πΉ Refer to the dependency-injector library for implementation details.
The Unit of Work pattern ensures that multiple repository operations are executed as a single transaction.
- Prevents partial updates in case of failure.
- Ensures database consistency.
internal/
βββ app/ # Defines servers and middlewares (protocols)
βββ controllers/ # Handles requests and responses (endpoints)
βββ domains/ # Core business logic (services, use cases, entities)
βββ infrastructures/ # External dependencies (databases, caches, queues)
βββ patterns/ # Dependency injection
βββ main.py # Application entry point
- App: Define servers (protocols) and middlewares.
- Controllers: Define the API endpoints and route requests to services.
- Domains: Contains core business logic, including services and use cases.
- Infrastructures: Houses repositories and database interactions.
- Patterns: Implements design patterns like Dependency Injection and Unit of Work.
- Main.py: Initializes the application, including dependency injection and routing setup.
This modular structure ensures scalability, testability, and maintainability.
Ensure you have the following installed:
-
Python 3.13+
-
PostgreSQL 15+ (Use your own local PostgreSQL or you can use my docker-compose.dev.yaml)
-
Keycloak 26.x (Optional) (Use your own local Keycloak with webhook extension https://github.com/p2-inc/keycloak-events or you can use my docker-compose.dev.yaml)
-
OpenFGA 1.8.9 (Optional) (Use your own local OpenFGA or you can use my docker-compose.dev.yaml)
-
Consul 1.21 (Optional) (Use your own local Consul or you can use my docker-compose.dev.yaml)
-
Docker & Docker Compose (Optional)
-
make (Optional)
-
Clone the Repository
git clone https://github.com/EdwardPham1615/python-clean-architecture-project.git cd python-clean-architecture-project
-
Set Up a UV Virtual Environment
uv lock && uv sync
-
Manage Relational Database Migrations
# using Alembic (https://alembic.sqlalchemy.org/en/latest/) # For manually generate migration files base on your need alembic -x sqlalchemy.url=postgresql+asyncpg://<username>:<password>@<ip>:<port>/<db> revision -m <name_version> # For auto generate migrations files base on your models alembic -x sqlalchemy.url=postgresql+asyncpg://<username>:<password>@<ip>:<port>/<db> revision --autogenerate -m <name_version> # Example: alembic -x sqlalchemy.url=postgresql+asyncpg://cleanarc:cleanarc!123@localhost:5432/cleanarc revision --autogenerate -m "update comments"
-
Manage Keycloak and Webhook Config
This section is optional and our repo still working without Keycloak, but you need to implement another authentication service by yourself !!! In this repo, i use Phasetwo as an example of authentication service integration (https://phasetwo.io/docs/introduction/), is basically Keycloak with extensions. But you can just use an original Keycloak and then install webhook extension with it (optional). Step 1: Setup your Keycloak using the original documents https://www.keycloak.org/documentation. Step 2: Setup webhooks (optional) Follow this document https://phasetwo.io/docs/audit-logs/webhooks/ And checkout my folder "examples/external_authentication_service_webhook_crud".
-
Setup OpenFGA
This section is optional and our repo still working without OpenFGA, but you need to implement another authorization service by yourself !!! In this repo, we need a token, a store_id and a authorization_model_id. Follow the official document of OpenFGA for details (https://openfga.dev/docs/getting-started/setup-openfga/overview). Step 1: Generate a new token for OpenFGA then config it for docker and also our app, we use a simple authen method (https://openfga.dev/docs/getting-started/setup-openfga/configure-openfga#pre-shared-key-authentication). Step 2: Create a new store, use playground to make it simple or you can follow this (https://openfga.dev/docs/getting-started/create-store). Step 3: Create a new authorization_model_id model, use playground to make it simple or you can follow this (https://openfga.dev/docs/getting-started/configure-model). I have already create a sample of authorization model here "internal/infrastructures/external_rebac_authorization_service/openfga_client/authorization_model_versions/00001_autho_model.dsl". Step 4: Config token, store_id and authorization_model_id to our app.
-
Setup Consul
This section is optional and our repo still working without Consul, just use local .env instead !!! Follow the official document of Consul for details (https://developer.hashicorp.com/consul/docs). For basic setup, just follow some simple steps. Step 1: Create a folder in "Key/Value" of Consul and set it to CFG_MANAGER_SERVICE__ENV Step 2: Create every env in .env.example inside the upper folder Step 3: Set CFG_MANAGER_SERVICE__ENABLE=true Step 4: Set CFG_MANAGER_SERVICE__URL and CFG_MANAGER_SERVICE__TOKEN Step 5: Start app Note: Our internal/infrastructures/config_manager including the auto-reload configs function.
-
Manage proto files
This section is optional if you want to develop service with gRPC. Every proto files are stored at "internal/controllers/grpc/protos" Use the following command if you want to generate new proto: # gen-proto make gen-proto
-
Start the Application
python main.py # or ./run.sh
-
Alternative run with docker
# if you do not want to start from scratch, just run with docker # using commands from Makefile # build an image make build # start make start # stop make stop # restart make restart
-
Access the API
- Use
http://127.0.0.1:8082/docs
for Swagger UI. - Use
http://127.0.0.1:8082/redoc
for Redoc documentation. - Use
http://127.0.0.1:5000/healh-check
for health check port