How to Build Large, Maintainable Python Systems with Clean Architecture: Concepts & Real-World Examples
Why Clean Architecture?
As software grows, so does its complexity—leading to framework lock-in, untestable logic, and tangled code.
Clean Architecture keeps business rules independent of frameworks, databases, and UI.
“Your business logic should be testable and changeable without touching your framework or database.”
The Key Principle
Your business logic should be testable without binding to any framework.
If you can run your core logic—entities and use cases—in pure Python, you’re on the right track.
The Layers in Practice
Layered separation makes your codebase maintainable and testable:
- Entities: Plain business objects (e.g.
Car,Rental) - Use Cases: Application logic (e.g. "rent a car", "list all cars")
- Repositories/Interfaces: Abstractions for data access or services
- Frameworks/Drivers: Django, Flask, databases, UI code
UML Class Diagram
classDiagram
class Car {
+int car_id
+str make
+str model
+bool is_available
}
class Rental {
+int rental_id
+int car_id
+int user_id
+str start_date
+str end_date
}
class CarRepository {
+save(car)
+get_by_id(car_id)
+get_all()
+delete(car_id)
}
class RentalRepository {
+save(rental)
+get_by_id(rental_id)
+get_all()
+delete(rental_id)
}
class CarCRUDUseCase {
-car_repository
+create_car(car_id, make, model)
+get_car(car_id)
+list_cars()
+update_car(car_id, make, model, is_available)
+delete_car(car_id)
}
class RentCarUseCase {
-car_repository
-rental_repository
+execute(car_id, user_id, start_date, end_date)
}
class DjangoCarRepository {
+save(car)
+get_by_id(car_id)
+get_all()
+delete(car_id)
}
class MongoCarRepository {
+save(car)
+get_by_id(car_id)
+get_all()
+delete(car_id)
}
class DjangoRentalRepository
class MongoRentalRepository
CarCRUDUseCase --> CarRepository
RentCarUseCase --> CarRepository
RentCarUseCase --> RentalRepository
DjangoCarRepository --|> CarRepository
MongoCarRepository --|> CarRepository
DjangoRentalRepository --|> RentalRepository
MongoRentalRepository --|> RentalRepository
Flow Diagram: Class Interaction at Runtime
How does a user action (“rent a car”) flow through the layers?
sequenceDiagram
participant User
participant UI as UI/View (Django/Flask)
participant UC as RentCarUseCase
participant CarRepo as CarRepository
participant RentalRepo as RentalRepository
participant DB as DB/Framework
User->>UI: Submit rent car form (car_id, user_id, dates)
UI->>UC: execute(car_id, user_id, start_date, end_date)
UC->>CarRepo: get_by_id(car_id)
CarRepo->>DB: Query car by id
CarRepo-->>UC: Car entity
UC->>CarRepo: save(car) (mark as unavailable)
CarRepo->>DB: Update car
UC->>RentalRepo: save(rental)
RentalRepo->>DB: Insert rental record
RentalRepo-->>UC: Rental entity
UC-->>UI: Return Rental entity
UI-->>User: Render result (success page/HTML)
Sample Code: Each Layer in Python
1. Entities (Business Objects)
# entities/car.py
class Car:
def __init__(self, car_id, make, model, is_available=True):
self.car_id = car_id
self.make = make
self.model = model
self.is_available = is_available
# entities/rental.py
class Rental:
def __init__(self, rental_id, car_id, user_id, start_date, end_date):
self.rental_id = rental_id
self.car_id = car_id
self.user_id = user_id
self.start_date = start_date
self.end_date = end_date
2. Use Cases (Application Logic)
# use_cases/car_crud.py
class CarCRUDUseCase:
def __init__(self, car_repository):
self.car_repository = car_repository
def create_car(self, car_id, make, model):
car = Car(car_id=car_id, make=make, model=model, is_available=True)
self.car_repository.save(car)
return car
def get_car(self, car_id):
return self.car_repository.get_by_id(car_id)
def list_cars(self):
return self.car_repository.get_all()
def update_car(self, car_id, make, model, is_available):
car = Car(car_id=car_id, make=make, model=model, is_available=is_available)
self.car_repository.save(car)
return car
def delete_car(self, car_id):
self.car_repository.delete(car_id)
# use_cases/rent_car.py
class RentCarUseCase:
def __init__(self, car_repository, rental_repository):
self.car_repository = car_repository
self.rental_repository = rental_repository
def execute(self, car_id, user_id, start_date, end_date):
car = self.car_repository.get_by_id(car_id)
if not car or not car.is_available:
raise Exception("Car not available")
car.is_available = False
self.car_repository.save(car)
rental = Rental(
rental_id=None, car_id=car_id, user_id=user_id,
start_date=start_date, end_date=end_date
)
self.rental_repository.save(rental)
return rental
3. Repository Interfaces and Implementations
# interface_adapters/car_repository.py
class CarRepository:
def save(self, car): ...
def get_by_id(self, car_id): ...
def get_all(self): ...
def delete(self, car_id): ...
Django ORM Example:
# frameworks/django_repositories.py
from entities.car import Car
from myapp.models import CarModel
class DjangoCarRepository(CarRepository):
def save(self, car):
obj, _ = CarModel.objects.update_or_create(
pk=car.car_id,
defaults={'make': car.make, 'model': car.model, 'is_available': car.is_available}
)
return car
def get_by_id(self, car_id):
try:
obj = CarModel.objects.get(pk=car_id)
return Car(car_id=obj.pk, make=obj.make, model=obj.model, is_available=obj.is_available)
except CarModel.DoesNotExist:
return None
def get_all(self):
return [
Car(
car_id=obj.pk,
make=obj.make,
model=obj.model,
is_available=obj.is_available
)
for obj in CarModel.objects.all()
]
def delete(self, car_id):
CarModel.objects.filter(pk=car_id).delete()
MongoDB Example:
# frameworks/mongo_car_repository.py
from entities.car import Car
from interface_adapters.car_repository import CarRepository
class MongoCarRepository(CarRepository):
def __init__(self, db):
self.collection = db['cars']
def save(self, car):
self.collection.update_one(
{'car_id': car.car_id},
{'$set': {'make': car.make, 'model': car.model, 'is_available': car.is_available}},
upsert=True
)
return car
def get_by_id(self, car_id):
doc = self.collection.find_one({'car_id': car_id})
if not doc:
return None
return Car(
car_id=doc['car_id'],
make=doc['make'],
model=doc['model'],
is_available=doc.get('is_available', True)
)
def get_all(self):
return [
Car(
car_id=doc['car_id'],
make=doc['make'],
model=doc['model'],
is_available=doc.get('is_available', True)
)
for doc in self.collection.find()
]
def delete(self, car_id):
self.collection.delete_one({'car_id': car_id})
4. UI Layer Example: Django View and Template
# myapp/views.py
from django.shortcuts import render
from use_cases.car_crud import CarCRUDUseCase
from frameworks.django_repositories import DjangoCarRepository
def car_list_view(request):
use_case = CarCRUDUseCase(DjangoCarRepository())
cars = use_case.list_cars()
return render(request, "car_list.html", {"cars": cars})
Django Template (car_list.html):
<!DOCTYPE html>
<html>
<head>
<title>Car List</title>
</head>
<body>
<h1>Available Cars</h1>
<table border="1">
<tr>
<th>ID</th>
<th>Make</th>
<th>Model</th>
<th>Available</th>
</tr>
{% for car in cars %}
<tr>
<td>{{ car.car_id }}</td>
<td>{{ car.make }}</td>
<td>{{ car.model }}</td>
<td>{{ car.is_available|yesno:"Yes,No" }}</td>
</tr>
{% endfor %}
</table>
</body>
</html>
5. Flask API Example
from flask import Flask, request, jsonify
from pymongo import MongoClient
from use_cases.car_crud import CarCRUDUseCase
from frameworks.mongo_car_repository import MongoCarRepository
app = Flask(__name__)
client = MongoClient("mongodb://localhost:27017")
db = client["car_rental"]
car_repo = MongoCarRepository(db)
car_crud = CarCRUDUseCase(car_repo)
@app.route('/cars', methods=['POST'])
def create_car():
data = request.json
car = car_crud.create_car(data['car_id'], data['make'], data['model'])
return jsonify({"car_id": car.car_id, "make": car.make, "model": car.model, "is_available": car.is_available}), 201
@app.route('/cars', methods=['GET'])
def list_cars():
cars = car_crud.list_cars()
return jsonify([
{"car_id": c.car_id, "make": c.make, "model": c.model, "is_available": c.is_available}
for c in cars
])
6. Unit Testing Use Cases
import unittest
from entities.car import Car
from use_cases.car_crud import CarCRUDUseCase
class InMemoryCarRepository:
def __init__(self):
self._cars = {}
def save(self, car): self._cars[car.car_id] = car; return car
def get_by_id(self, car_id): return self._cars.get(car_id)
def get_all(self): return list(self._cars.values())
def delete(self, car_id): self._cars.pop(car_id, None)
class TestCarCRUDUseCase(unittest.TestCase):
def setUp(self):
self.repo = InMemoryCarRepository()
self.usecase = CarCRUDUseCase(self.repo)
def test_create_car(self):
car = self.usecase.create_car(1, 'Honda', 'Civic')
self.assertEqual(car.car_id, 1)
self.assertEqual(car.make, 'Honda')
def test_list_cars(self):
self.usecase.create_car(2, 'Toyota', 'Yaris')
cars = self.usecase.list_cars()
self.assertEqual(len(cars), 1)
self.assertEqual(cars[0].model, 'Yaris')
if __name__ == '__main__':
unittest.main()
Key Takeaways
- UI (Flask/Django/HTML): Just collects input, calls use cases, and presents results.
- Business logic is fully testable, reusable, and not coupled to frameworks.
- Repositories abstract data storage—you can swap MongoDB, SQL, or even in-memory for tests.
- Templates only display data—never mix business rules into your HTML.
If you can run all your core logic and tests without any web server or database, you’re building for the long run.
Want a deeper dive, form handling, or even an end-to-end repo? Let me know!
Get in Touch with us
Related Posts
- 为什么企业应该开发自己的电商系统(而不是依赖租用型平台)
- Why Your Business Should Build Its Own E-Commerce System (Instead of Renting One)
- Upstream、Downstream 和 Fork:Android 与 Linux 开发者必须理解的核心概念
- Upstream, Downstream, and Fork: A Clear Guide for Android & Linux Developers
- NVIDIA、Microsoft、OpenAI、Google、Oracle 以及 AMD:正在共同推动 AI 泡沫如何形成?
- The Real AI Bubble: How NVIDIA, Microsoft, OpenAI, Google, Oracle — and Now AMD — Shape the Future of Compute
- 深度学习在房地产开发中的应用
- Deep Learning in Property Development
- 代码修复与遗留系统维护服务 —— Simplico 助力企业保持系统稳定、安全、高效
- Code Fixing & Legacy System Maintenance — Keep Your Business Running Smoothly with Simplico
- Python 深度学习在工厂自动化中的应用:2025 全面指南
- Python Deep Learning in Factory Automation: A Complete Guide (2025)
- 工厂 / 制造业专用 Python 开发与培训服务
- Python Development & Industrial Automation Training Services
- 为什么 Python + Django 是现代电商系统的最佳技术栈(完整指南 + 定价方案)
- Why Python + Django Is the Best Tech Stack for Building Modern eCommerce Platforms (Complete Guide + Pricing Plans)
- 三十六计现代商业版:理解中国企业竞争、谈判与战略思维的终极指南
- The 36 Chinese Business Stratagems: A Modern Guide to Understanding How Chinese Companies Compete and Win
- 理解机器学习中的 Training、Validation、Testing
- Understanding Training, Validation, and Testing in Machine Learning













