整洁架构:实体、用例与接口适配

FreeGuideOnline 最新 2026-06-18

好的,这是为您生成的“软件架构整洁之道:实体、用例与接口适配”教程页面内容,可直接展示。

软件架构整洁之道:实体、用例与接口适配

欢迎来到整洁架构的核心区域。本教程将带你深入理解 Robert C. Martin 提出的整洁架构(Clean Architecture)中最关键的三个层次:实体、用例与接口适配。我们将摒弃空洞的理论,用最通俗的语言和实例,帮你建立起抵御框架变更、工具演进和界面替换的稳固代码核心。

什么是整洁架构?

在开始之前,让我们先对齐一个核心理念:好的架构,其中心是业务逻辑,而非技术实现。 整洁架构是一种以业务价值为中心的软件设计方法,它通过严格的分层和依赖规则,让代码变得可测试、可维护且独立于外部细节。

核心原则:依赖规则——源代码的依赖方向,必须始终指向内部,从外部细节指向内部核心。

整洁架构中的同心圆,从内到外依次是:实体 > 用例 > 接口适配 > 框架与驱动。今天我们聚焦于前三层,它们是系统核心业务的承载者。

第一层:实体(Enterprise Business Rules)

实体是系统最核心、最稳定的一层。它封装了企业级的关键业务规则和数据。这一层的变化,往往意味着企业本身业务逻辑的根本性改变。

实体到底是什么?

实体既可以是拥有方法的对象,也可以是纯粹的数据结构加函数。关键在于,它不依赖任何外部事物。

  • 它封装最关键的商业规则:如“一笔订单金额不能为负”、“一个用户必须拥有唯一的邮箱”。
  • 它定义核心业务对象:如 OrderUserProduct 等。
  • 它可以是其他层的数据载体:但它本身不知道如何被持久化,如何被展示。

实体的设计准则

  • 零依赖原则:实体不应该导入任何框架、数据库、网络相关的库。它只依赖语言自身的基础类型和工具库。
  • 高内聚:与某个核心概念相关的规则和数据应放在一起。例如,Order 实体自己应该拥有 addItem() 方法,而不是让外部服务去操作一堆散落的数据。
  • 用例无关性:实体不知道谁在调用它,也不知道调用者的目的是什么。它只是纯粹的、自解释的业务规则容器。

示例:一个简洁的 User 实体

# Python示例,同样适用于其他语言
from dataclasses import dataclass
from typing import Optional

@dataclass
class User:
    id: Optional[int]  # ID可能由持久化层生成,实体本身不关心
    email: str
    name: str

    def change_email(self, new_email: str) -> None:
        """核心业务规则:邮箱必须包含‘@’"""
        if "@" not in new_email:
            raise ValueError("无效的邮箱地址")
        self.email = new_email

这个实体不知道它将被如何储存(数据库、文件),也不知道哪些界面会用到它。它只负责保护“邮箱必须有@”这条企业级规则。

第二层:用例(Application Business Rules)

用例层位于实体层的外围,它包含应用特定的业务规则。如果说实体回答“什么是正确的”,那么用例回答“在特定场景下,我们需要做什么”。

用例是什么?

用例是软件系统能够完成的一个具体的、面向用户的任务。它编排实体,实现一个完整的业务目标。例如:“用户注册”、“提交订单”、“导出报表”。

  • 它依赖于实体:用例会获取、创建和修改实体。
  • 它定义输入和输出:通过输入端口(Input Port)接收请求数据,通过输出端口(Output Port)传递结果或外部依赖。
  • 它不关心外界如何触发它:可以是HTTP请求、命令行、或者测试用例。

用例中的端口与适配器概念

用例层最重要的设计是依赖倒置,它通过定义接口(端口)来断开与外部层的直接耦合。

  • 输入端口 (Input Port):用例类本身实现的接口,供外层调用。
  • 输出端口 (Output Port):用例需要的、由外层实现的接口。例如,用例需要保存实体,它会定义一个 UserRepository 接口,而不关心具体的数据库实现。

设计准则

  • 单一职责:每个用例只做一件事。避免庞大的“上帝服务”。
  • 依赖倒置:用例绝不直接实例化外部类。它声明所需的抽象接口,由外层在运行时注入具体实现。
  • 数据隔离:用例的输入输出应该使用与特定界面、传输协议无关的简单数据结构(DTO),而不是 HttpRequest 对象。

示例:“注册新用户”用例

# 定义输出端口(抽象的接口)
class UserRepository:
    def save(self, user: User) -> User:
        raise NotImplementedError

    def exists_by_email(self, email: str) -> bool:
        raise NotImplementedError

# 用例的请求和响应模型(简单的数据类)
class RegisterUserRequest:
    email: str
    name: str

class RegisterUserResponse:
    success: bool
    user_id: Optional[int]
    message: str

# 用例实现(输入端口本身)
class RegisterUserUseCase:
    def __init__(self, user_repo: UserRepository):
        self._user_repo = user_repo

    def execute(self, request: RegisterUserRequest) -> RegisterUserResponse:
        # 1. 应用特定规则:检查邮箱是否已注册
        if self._user_repo.exists_by_email(request.email):
            return RegisterUserResponse(False, None, "邮箱已被注册")

        # 2. 创建领域实体,实体自身会进行核心验证
        try:
            user = User(id=None, email=request.email, name=request.name)
        except ValueError as e:
            return RegisterUserResponse(False, None, str(e))

        # 3. 通过输出端口保存实体
        saved_user = self._user_repo.save(user)

        # 4. 返回成功响应
        return RegisterUserResponse(True, saved_user.id, "注册成功")

注意,这个用例完全不知道 HTTP、数据库操作。它可以被纯单元测试覆盖,而无需启动任何框架。

第三层:接口适配(Interface Adapters)

这一层是核心与外部世界的“翻译官”。它负责将外层传入的数据转换为用例可以理解的格式,并将用例的输出结果转换为外层可用的格式。

接口适配层的职责

  • 控制反转容器:将用例所需的实现(如 UserRepository 的具体数据库实现)注入到用例中。
  • 数据格式转换:将 http.Request 中的 JSON 字段提取出来,构造成 RegisterUserRequest;将 RegisterUserResponse 转换成 JSON 响应、HTML 视图或命令行打印。
  • 跨边界通信:管理 UI、存储、外部服务等具体技术。

三种常见的适配器

1. 控制器/表现层适配器(MVC、REST)

它将 Web 框架(如 Flask、Express、Spring MVC)的路由处理程序转换为用例调用。

# Flask 示例:一个适配器
from flask import Flask, request, jsonify

app = Flask(__name__)

# 依赖组装(通常在工厂或启动脚本中)
sql_repo = SQLUserRepository(session)  # 实现UserRepository
register_usecase = RegisterUserUseCase(sql_repo)

@app.route('/users', methods=['POST'])
def register():
    data = request.get_json()
    # 将外部HTTP数据转换为用例请求结构
    req = RegisterUserRequest(email=data['email'], name=data['name'])
    # 调用用例
    response = register_usecase.execute(req)
    # 将用例响应转换为HTTP JSON
    return jsonify({
        "id": response.user_id,
        "message": response.message
    }), 201 if response.success else 400

控制器极其纤薄,它不含业务逻辑,只负责格式转换和委托。

2. 持久层适配器(Repository 实现)

它实现用例层定义的 UserRepository 接口,使用具体 ORM、SQL 语句或文件系统。

class SQLUserRepository(UserRepository):
    def __init__(self, db_session):
        self.session = db_session

    def save(self, user: User) -> User:
        # 将领域实体转换为数据库模型(假如使用ORM)
        db_user = UserModel(email=user.email, name=user.name)
        self.session.add(db_user)
        self.session.commit()
        # 返回带ID的实体
        return User(id=db_user.id, email=db_user.email, name=db_user.name)

    def exists_by_email(self, email: str) -> bool:
        return self.session.query(UserModel).filter_by(email=email).first() is not None

3. 外部服务适配器(邮件、短信等)

适配邮件发送接口,将其转化为用例可以调用的抽象端口。

适配层的设计守则

  • 绝对不能包含领域逻辑:任何与业务决策相关的代码都不能出现在控制器或视图里。
  • 可替换的即插即用设备:适配器应该像电源插头一样,可以随时更换而不影响核心电器运转。从 REST 换成 GraphQL 或 gRPC,只需更换适配器。
  • 透明度:适配器层代码应该直观地显示“它在做什么转换”,让任何一个开发者都能快速理解数据的边界。

它们如何协同工作:一个完整的请求流程

当一个“用户注册”请求到达时,数据流动路径如下:

  1. Web 框架接收 HTTP 请求,将其委托给控制器适配器
  2. 控制器提取 JSON 数据,创建 RegisterUserRequest,并调用用例 RegisterUserUseCase.execute()
  3. 用例通过依赖注入握有 UserRepository 实例(实际为 SQLUserRepository 适配器)。
  4. 用例检查 exists_by_email(),然后创建 User 实体,执行 change_email 等操作(如果存在)。
  5. 用例调用 repository.save(user),将实体传递给持久层适配器。
  6. 持久层适配器将实体转换为数据库记录,保存,并返回带有 id 的新实体。
  7. 用例将结果封装为 RegisterUserResponse,返回给控制器。
  8. 控制器RegisterUserResponse 转换为 HTTP JSON 响应,返回给客户端。

整个过程中,实体和用例完全不涉及任何框架、协议或特定数据库。它们可以脱离运行环境被单独测试和演化。

总结:整洁架构带来的价值

通过将系统划分为实体、用例、接口适配,你获得了:

  • 可测试性:业务规则可以在毫秒级内完成单元测试,无需启动服务器。
  • 可维护性:需求变更往往只影响某一层。修改邮件服务不会触及核心业务逻辑。
  • 可扩展性:想要支持新的前端、新的数据库?只需编写一个新的适配器。
  • 延迟决策:你可以先专注于核心业务建模,再决定使用哪种框架或数据库。

整洁架构不是繁文缛节,而是一种让你在软件开发这场“战争”中,始终掌握主动权、不被外部工具绑架的战略。从今天起,尝试在你的项目中应用这三层划分,哪怕只是一个模块,你都会立刻感受到代码重获的自由与清晰。


实践是理解整洁架构的唯一道路。 一旦你亲手将实体、用例与适配器剥离,你便再也无法忍受业务逻辑与框架代码交织的混乱状态。祝你在整洁架构的道路上越走越远!