Clean Architecture là gì?

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.”

Trình soạn thảo code với syntax highlighting nhiều màu

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

Lập trình viên gõ code trên laptop tại bàn làm việc

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.

Tôi là một lập trình viên IOS. Code chính là IOS nhưng thỉnnh thoảng vẫn đá sang Android hoặc web. Mặc dù không quá thông thạo nhưng tôi sẽ chia sẻ những kiến thức mà mình đã tìm hiểu, áp dụng qua.

Bài viết liên quan

Ghostty: Terminal GPU-rendered thay thế iTerm2 và Alacritty

Ghostty: Terminal GPU-rendered thay thế iTerm2 và Alacritty Ghostty là terminal emulator mới phát hành phiên bản 1.0 vào cuối 2024, được viết bằng Zig và sử dụng GPU rendering…

Xem thêm

Feather Robotics: Nền tảng robot mô-đun giá 30k USD cho developer

Feather Robotics, khởi nghiệp humanoid do cựu kỹ sư Tesla và founder trước của 1X thành lập, đang định vị mình là “Android of robotics” — cung cấp nền tảng…

Xem thêm

Tauri 2.0: Ứng Dụng Desktop Rust Chạy Trên WebView Hệ Thống

Tauri 2.0: Ứng dụng Desktop Rust chạy trên WebView hệ thống Tauri 2.0 là bản phát hành ổn định tháng 10/2024 của framework ứng dụng desktop đa nền tảng mã…

Xem thêm
0 0 đánh giá
Article Rating
Theo dõi
Thông báo của
guest
0 Comments
Cũ nhất
Mới nhất Được bỏ phiếu nhiều nhất