
Clean Architecture là gì?
Clean Architecture là mô hình kiến trúc phần mềm do Robert C. Martin (Uncle Bob) tổng hợp từ các trường phái trước: Hexagonal Architecture của Alistair Cockburn, Onion Architecture của Jeffrey Palermo, Screaming Architecture của chính Uncle Bob, DCI và BCE. Mục tiêu chung của tất cả các trường phái này là tách biệt rõ ràng các tầng sao cho business logic không phụ thuộc vào framework, database hay UI cụ thể — thứ thay đổi nhanh nhất trong vòng đời phần mềm.
Clean Architecture đưa ra 5 tính chất hệ thống mong muốn: độc lập framework (dùng frameworks như công cụ, không bị chúng ràng buộc), khả năng test (business rules test được không cần UI/DB/web server), độc lập UI (swap web console mà không đổi business rules), độc lập database (swap Oracle sang MongoDB tự do), và độc lập external agencies (business rules không biết gì về outside world).
Bốn tầng chính
Clean Architecture sử dụng mô hình các vòng tròn đồng tâm (concentric circles). Mỗi tầng bên trong không biết gì về tầng bên ngoài. Số vòng có thể nhiều hơn 4 — đây là schematic, không phải quy định cứng.
- Entities (Layer 1): enterprise-wide business rules — Product, Order, User. Lớp này ít thay đổi nhất, không import framework nào. Đây là các object có methods hoặc data structures kết hợp functions.
- Use Cases (Layer 2): application-specific business rules — CreateOrder, CancelOrder, Checkout. Orchestrate flow giữa entities, chỉ thay đổi khi application operation thay đổi. Không phụ thuộc DB hay web framework.
- Interface Adapters (Layer 3): converters giữa format use case/entity và format bên ngoài. Chứa controllers, presenters, views, repositories. SQL được giới hạn ở đây — domain không biết gì về database.
- Frameworks & Drivers (Layer 4): database, Flask/Django/FastAPI, UI framework, third-party services. Phần lớn là glue code. “The web is a detail. The database is a detail.”

Dependency Rule — quy tắc cốt lõi
Mọi source code dependencies chỉ được phép hướng vào trong (point inwards). Tầng trong không import tầng ngoài. Khi control flow cần hướng ra ngoài — use case gọi presenter — sử dụng Dependency Inversion Principle (DIP): inner circle định nghĩa interface (port), outer circle implements nó. Dù control flow hướng ra ngoài, source dependency vẫn hướng vào trong.
Data vượt qua boundary phải là simple data structures: dicts, tuples, dataclasses. Không bao giờ truyền Entity hay ORM row qua interface — nếu làm vậy, inner circle sẽ phụ thuộc vào outer framework.
Cấu trúc project Python thực tế
Dựa trên Cosmic Python và kinh nghiệm thực tế từ sản phẩm production, cấu trúc khuyến nghị:
my_project/
├── domain/ # Layer 1: pure Python
│ ├── __init__.py
│ ├── models.py # dataclasses: Order, Product
│ └── rules.py # enterprise business logic
├── use_cases/ # Layer 2: application rules
│ ├── __init__.py
│ ├── interfaces.py # ABC/Protocol: OrderRepository
│ └── order_service.py # CreateOrder, CancelOrder
├── adapters/ # Layer 3: glue
│ ├── __init__.py
│ ├── repositories.py # SQLAlchemyOrderRepo
│ ├── api_controllers.py # Flask/FastAPI endpoints
│ └── mappers.py # ORM ↔ domain mapping
└── frameworks/ # Layer 4: tech details
├── __init__.py
├── flask_app.py # Flask setup
├── database.py # SQLAlchemy engine
└── config.py # settings, env vars

Code thực tế — Repository Pattern
Dưới đây là ví dụ Python triển khai tối giản nhưng đúng Dependency Rule:
# domain/models.py
from dataclasses import dataclass
@dataclass
class Order:
id: int
items: list[str]
total: float
# use_cases/interfaces.py
from abc import ABC, abstractmethod
class OrderRepository(ABC):
@abstractmethod
def get_order(self, order_id: int) -> Order: ...
class OrderService(ABC):
@abstractmethod
def create(self, items: list[str]) -> Order: ...
# use_cases/order_service.py
from domain.models import Order
class CreateOrder:
def __init__(self, repo: OrderRepository):
self.repo = repo
def execute(self, items: list[str]) -> Order:
order = Order(id=None, items=items,
total=sum(10.0 for _ in items))
self.repo.save(order)
return order
# adapters/repositories.py
import sqlalchemy as sa
from sqlalchemy.orm import Session
from use_cases.interfaces import OrderRepository
from domain.models import Order
class SqlAlchemyOrderRepo(OrderRepository):
def __init__(self, session: Session):
self.session = session
def save(self, order: Order) -> None:
row = sa.OrderRow(items=order.items,
total=order.total)
self.session.add(row)
self.session.commit()
# frameworks/flask_app.py (composition root)
from flask import Flask, request, jsonify
from use_cases.order_service import CreateOrder
from adapters.repositories import SqlAlchemyOrderRepo
from sqlalchemy.orm import Session
from sqlalchemy import create_engine
app = Flask(__name__)
engine = create_engine("postgresql:///myapp")
session = Session(engine)
create_order = CreateOrder(repo=SqlAlchemyOrderRepo(session))
@app.route("/orders", methods=["POST"])
def create():
items = request.json["items"]
order = create_order.execute(items)
return jsonify({"id": order.id, "total": order.total})
So sánh với các pattern khác
| Pattern | Độ phức tạp | Testability | Framework coupling | Phù hợp |
|---|---|---|---|---|
| MVC thuần (Django) | Thấp | Trung bình | Chặt | CRUD đơn giản |
| Fat Models | Thấp | Trung bình | Chặt (Django) | Django-only |
| Clean Architecture | Cao | Rất cao | Lỏng | Dài hạn, phức tạp |
| Hexagonal | Cao | Rất cao | Lỏng | Tương tự CA |
Khi nào dùng, khi nào dùng lại
Clean Architecture phù hợp khi app sống nhiều năm, business logic phức tạp, cần swap framework (Django → FastAPI), nhiều delivery mechanism (CLI + API + web) hoặc team lớn cần rõ ràng trách nhiệm. Tuy nhiên, nó overkill cho CRUD app đơn giản, script nhỏ hay prototype cần tốc độ — trong những trường hợp đó, Django views hay FastAPI router trực tiếp đủ tốt.
Ưu và nhược điểm
| Ưu điểm | Nhược điểm |
|---|---|
| Business logic testable không cần framework | Nhiều boilerplate (interfaces, DTOs, mappers) |
| Framework-agnostic domain | Steep learning curve cho junior |
| Domain sống sót qua thay đổi UI/DB | Có thể dẫn đến anemic domain nếu làm sai |
| Tách biệt rõ trách nhiệm | Thiếu magic từ framework (DI thủ công) |
| Tuân thủ SOLID | Nhiều file cần navigate |
Tổng kết
Clean Architecture không phải công thức bắt buộc — mà là guideline giúp bạn đặt business logic ở vị trí trung tâm, framework ở ngoài. Đối với Python project dài hạn, tách biệt domain khỏi adapter thực sự đáng giá. Xem thêm blog gốc của Uncle Bob và Cosmic Python book.
