跳转至

440_Clean Architecture整洁架构


一句话说明

整洁架构是 Robert C. Martin 提出的分层架构思想:业务规则在中心,外部细节(数据库、UI、框架)在外层,依赖方向只能从外向内,核心业务逻辑不受任何技术选型影响。


核心知识点

白话理解

想象洋葱:最里面是最核心的业务规则(纯粹的逻辑),外面套着用例层,再外面是接口适配层,最外面才是数据库/Web框架这些"脏活"。洋葱的特点是:外层可以知道内层,内层永远不知道外层是谁——这就是依赖规则。

四层结构

外层 → 内层(依赖方向)
┌─────────────────────────────┐
│  Frameworks & Drivers       │  ← 数据库、Web框架、外部服务
│  ┌───────────────────────┐  │
│  │  Interface Adapters   │  │  ← Controller、Presenter、Gateway
│  │  ┌─────────────────┐  │  │
│  │  │  Use Cases      │  │  │  ← 业务用例、应用服务
│  │  │  ┌───────────┐  │  │  │
│  │  │  │  Entities │  │  │  │  ← 核心业务实体和规则(最稳定)
│  │  │  └───────────┘  │  │  │
│  │  └─────────────────┘  │  │
│  └───────────────────────┘  │
└─────────────────────────────┘

核心依赖规则

内层代码绝对不能依赖外层代码。数据库换了、框架换了,只要内层没变,业务逻辑完全不受影响。


经典题目与解法

题目1:用整洁架构实现"用户注册"功能

题意:实现用户注册,数据库可以随时替换,业务逻辑不受影响。

# ============ 第1层:实体层(Entity)最内层 ============
class User:
    """纯业务实体,不依赖任何框架或数据库"""
    def __init__(self, user_id: str, username: str, email: str):
        self.user_id = user_id
        self.username = username
        self.email = email

    def is_valid_email(self) -> bool:
        """业务规则:邮件必须含@"""
        return '@' in self.email

    def __repr__(self):
        return f"User({self.username}, {self.email})"


# ============ 第2层:用例层(Use Case)业务逻辑 ============
from abc import ABC, abstractmethod
import uuid

class UserRepository(ABC):
    """用例层定义接口(内层定义规范,外层实现)"""
    @abstractmethod
    def save(self, user: User) -> bool: pass

    @abstractmethod
    def find_by_email(self, email: str) -> User: pass

class RegisterUserUseCase:
    """用户注册用例——只有业务逻辑,不管数据怎么存"""
    def __init__(self, repo: UserRepository):
        self.repo = repo                   # 依赖注入,内层只知道接口

    def execute(self, username: str, email: str) -> dict:
        # 1. 创建实体并校验
        user = User(str(uuid.uuid4()), username, email)
        if not user.is_valid_email():
            return {'success': False, 'error': '邮件格式无效'}

        # 2. 检查邮件是否已注册
        if self.repo.find_by_email(email):
            return {'success': False, 'error': '邮件已被注册'}

        # 3. 保存
        self.repo.save(user)
        return {'success': True, 'user_id': user.user_id}


# ============ 第3层:接口适配层(Interface Adapter)============
class RegisterUserController:
    """接收 HTTP 请求,调用用例,格式化响应"""
    def __init__(self, use_case: RegisterUserUseCase):
        self.use_case = use_case

    def handle(self, request: dict) -> dict:
        """HTTP 请求处理入口"""
        username = request.get('username', '')
        email = request.get('email', '')

        if not username or not email:      # 参数校验
            return {'status': 400, 'message': '参数缺失'}

        result = self.use_case.execute(username, email)  # 调用用例
        if result['success']:
            return {'status': 201, 'user_id': result['user_id']}
        else:
            return {'status': 400, 'message': result['error']}


# ============ 第4层:基础设施层(Framework/DB)最外层 ============
class InMemoryUserRepository(UserRepository):
    """内存实现(开发/测试用)"""
    def __init__(self):
        self._store = {}                   # 内存存储

    def save(self, user: User) -> bool:
        self._store[user.email] = user
        return True

    def find_by_email(self, email: str) -> User:
        return self._store.get(email)      # 返回None表示未找到


# ============ 组装(Composition Root)============
repo = InMemoryUserRepository()            # 外层
use_case = RegisterUserUseCase(repo)       # 中层
controller = RegisterUserController(use_case)  # 接口层

# 模拟 HTTP 请求
print(controller.handle({'username': '张三', 'email': 'zhangsan@qq.com'}))
print(controller.handle({'username': '李四', 'email': 'zhangsan@qq.com'}))  # 邮件已注册
print(controller.handle({'username': '王五', 'email': 'invalid-email'}))    # 格式无效

时间复杂度:注册 O(1)(内存字典)
架构收益:换数据库只需替换 InMemoryUserRepository 实现,业务逻辑不动


题目2:整洁架构测试策略——用 Mock 测试用例层

# 用 Mock 仓储测试用例,不需要真实数据库
class MockUserRepository(UserRepository):
    """测试专用 Mock,可控制返回值"""
    def __init__(self, existing_emails=None):
        self.saved_users = []
        self.existing_emails = existing_emails or []  # 预设已存在邮件

    def save(self, user: User) -> bool:
        self.saved_users.append(user)      # 记录保存的用户
        return True

    def find_by_email(self, email: str) -> User:
        if email in self.existing_emails:
            return User("existing", "existing", email)  # 模拟已存在
        return None                        # 模拟不存在

# 单元测试
mock_repo = MockUserRepository(existing_emails=['taken@qq.com'])
use_case = RegisterUserUseCase(mock_repo)

# 正常注册
result = use_case.execute('张三', 'new@qq.com')
assert result['success'] == True
assert len(mock_repo.saved_users) == 1   # 确认用户被保存

# 重复邮件
result = use_case.execute('李四', 'taken@qq.com')
assert result['success'] == False
assert '已被注册' in result['error']

print("所有测试通过!")

面试技巧

  1. 画出依赖箭头:能在白板画出"外层→内层"箭头的候选人加分明显
  2. 举数据库替换场景:最直观的例子——从 MySQL 换 MongoDB,业务代码不动
  3. 与 MVC 对比:MVC 没有严格分层,View 可能直接访问数据库;整洁架构强制依赖规则
  4. 测试是动力:整洁架构最大优点是业务逻辑可纯粹单元测试,不需要启动数据库
  5. 不要过度设计:小项目不需要全套整洁架构,按需取用

速查表

层次职责依赖方向可替换性
Entities核心业务规则无依赖最稳定
Use Cases业务用例/流程依赖 Entities稳定
Interface Adapters格式转换依赖 Use Cases可替换
Frameworks & Drivers具体技术实现依赖 Adapters最易替换
# 整洁架构的核心口诀
# 1. 内层定义接口(抽象)
# 2. 外层提供实现(具体)
# 3. 通过依赖注入连接
# 4. 业务逻辑零依赖框架