1. 框架分层模型:层职责、层间接口、层依赖原则、层边界示例

本章是《Framework层设计模式应用》的基石。你将理解一个可维护、可测试的框架为何必须采用分层架构,以及每一层承担什么职责、层与层之间如何通信、依赖关系如何被严格管控。分层不是教条,而是应对框架复杂度最有效的结构化手段。错误的边界会放大变更成本,而清晰的分层则让框架在长期演进中保持弹性。

核心原则:每一层只做“本层该做的事”,从不越俎代庖。依赖必须是单向的——外层依赖内层,内层绝不反向依赖外层。这是框架抵御腐化的最后一道防线。

1.1 分层结构总览

一个通过实践验证的成熟框架,通常划分为四个逻辑层:表现层(Presentation Layer)、应用层(Application Layer)、领域层(Domain Layer)、基础层(Infrastructure Layer)。每一层都封装了一组紧密相关的职责,并通过定义良好的接口向上层提供能力。

框架四层分层模型 表现层 UI 组件、路由、视图模型、适配器 应用层 用例、数据转换、工作流、事务协调 领域层 实体、值对象、聚合、领域服务、领域事件 基础层 持久化、消息中间件、文件系统、网络/IO 层间 接口 ▼ 关键约束:领域层不导入任何框架或数据库驱动,应用层不接触 UI 组件。 ▼ 层间通信通过接口(抽象)进行,而非直接耦合到具体实现。

1.2 层职责详解

层名 核心职责 不应该做 典型组件
表现层 渲染 UI、捕获用户输入、将用户意图转化为应用命令 不应包含业务逻辑、数据校验之外的验证、直接访问数据库 Controller、Presenter、View、ViewModel
应用层 编排用例流程、进行事务管理、组织数据转换 不应包含领域规则、持久化逻辑、UI 渲染 ApplicationService、UseCase、DTO Assembler
领域层 封装业务规则、维护实体状态变化、定义领域事件 不应依赖框架特性、不应该直接操作数据库、不导入基础设施包 Entity、ValueObject、Aggregate、DomainService、Repository 接口
基础层 提供技术细节实现:持久化、消息、网络、缓存 不应包含业务规则、不应直接暴露实现细节给上层、不触发领域逻辑 RepositoryImpl、MessageBus、FileWriter、HttpClient

1.3 层间接口与依赖方向

分层架构中最重要的原则是依赖倒置:高层模块不依赖低层模块的具体实现,二者都依赖抽象。换句话说,领域层定义仓库(Repository)接口,基础层实现该接口。层间接口应遵循以下规范:

  • 接口归属:接口定义在“被调用方”所在层。
  • 参数与返回值:使用本层或更内层的类型,不引入外层类型。
  • 粒度:接口方法应当完整表达一个业务意图,而非暴露一条 SQL。
  • 异常:只抛出本层定义的异常,不抛技术栈异常。
设计提示:一个常见的错误是将表现层直接调用基础层的 DAO 接口。这种“捷径”会瞬间打破分层约束,导致业务逻辑散落在 UI 代码中。正确的做法是:表现层 → 应用层(用例)→ 领域层(接口)→ 基础层(实现)。

1.4 层边界示例

下面通过一个“用户注册”流程展示层边界如何工作。注意各层如何通过抽象协作,且不越界调用。

// 领域层:定义实体与接口
class User {
    private UserId userId;
    private Email email;
    // 仅持有业务数据与领域行为
}

interface UserRepository {
    User save(User user);
    Optional<User> findByEmail(Email email);
}

// 应用层:编排用例
class RegisterUserUseCase {
    private UserRepository userRepo;
    private NotificationService notifyService;

    public User execute(RegisterUserCommand cmd) {
        // 调用领域层接口检查重复
        if (userRepo.findByEmail(cmd.getEmail()).isPresent()) {
            throw new DuplicateEmailException();
        }
        User user = new User(UserId.generate(), new Email(cmd.getEmail()));
        userRepo.save(user);
        notifyService.sendWelcome(user);
        return user;
    }
}

// 基础层:实现领域接口
class JpaUserRepository implements UserRepository {
    @Override
    public User save(User user) {
        // 调用 JPA API 持久化
        return entityManager.merge(user);
    }
}

1.5 数据流、控制流与信任链路

流类型 方向 信任假设 安全边界
数据流 表现层 → 应用层 → 领域层 → 基础层 → DB/外部系统 数据在进入领域层前已完成格式校验和转义 领域层从不信任外部输入,实体构造函数中执行不变式检查
控制流 表现层发起命令 → 应用层协调 → 领域层运算状态 → 基础层持久化 每一层只调用下一层的公开接口,不绕过 应用层只依赖接口,不依赖具体实现,方便在测试中替换为 Mock
信任链路 基础层是“不信任层”,领域层是“信任决策层” 领域层不信任基础层的返回格式,必须在聚合内完成反序列化验证 领域事件在领域层发布,应用层订阅,基础层负责基础设施投递

1.6 选型权衡与风险点

  • 权衡:严格分层 vs 宽松分层。严格分层增加了跳转链路,但保证了长期可维护性。对于简单 CRUD,可以允许应用层直接调用基础层,但必须设定边界:一旦引入领域逻辑,立即回归严格分层。
  • 风险点:层间逻辑泄露。最常见的是“万能层”——表现层直接塞 SQL 片段、领域层依赖 ORM Session、应用层内嵌 IO 操作。每次代码审查应重点检查 import 语句是否越界。
  • 风险点:环形依赖。当基础层开始依赖领域层(比如领域层定义了一个接口,基础层实现后又被领域层反向 import),就会形成环。解决方法:将接口提取到独立模块(SharedKernel),双方都依赖它。
  • 风险点:过度抽象。每一层都定义接口虽然符合依赖倒置,但过多的接口增加阅读成本。建议:只对将来可能替换或需要测试 Mock 的组件定义接口,否则直接使用类。
重要警示:切勿让“通用”或“灵活”成为破坏分层的理由。任何跨层直接调用最终都会导致架构腐烂。如果发现某一处必须“破戒”,请自觉承担三层评审义务,并在异常位置留下明确注释。

1.7 落地步骤

  1. 识别核心层:从领域层开始,定义实体、值对象和聚合。这一阶段完全不涉及数据库和 UI。
  2. 定义层间接口:为每个领域实体定义仓库接口,放在领域层内部。
  3. 实现应用层:用用例类包装领域操作,暴露给表现层。
  4. 实现基础层:创建 JPA/MyBatis 仓库实现,或消息队列实现。
  5. 建立模块边界:用 IDE 的模块依赖检查(如 IntelliJ 模块依赖图)或构建工具(如 Gradle 的 module-info)强制禁止跨层依赖。例如,针对 Gradle 项目设置:
// presentation/build.gradle.kts
dependencies {
    implementation(project(":application"))
    // 禁止直接依赖 infrastructure 或 domain
}

// application/build.gradle.kts
dependencies {
    implementation(project(":domain"))
    // 不依赖 presentation 或 infrastructure
}

// infrastructure/build.gradle.kts
dependencies {
    implementation(project(":domain"))
}
  1. 自动化边界检查:使用 ArchUnit(Java)或类似的架构测试框架,在 CI 中自动验证层依赖规则不被违反。
// 使用 ArchUnit 强制层依赖规则(Java 示例)
@Test
void enforceLayerArchitecture() {
    layeredArchitecture()
        .layer("Presentation").definedBy("..presentation..")
        .layer("Application").definedBy("..application..")
        .layer("Domain").definedBy("..domain..")
        .layer("Infrastructure").definedBy("..infrastructure..")
        .whereLayer("Presentation").mayOnlyBeAccessedByLayers("Application")
        .whereLayer("Application").mayOnlyBeAccessedByLayers("Presentation")
        .whereLayer("Domain").mayOnlyBeAccessedByLayers("Application", "Infrastructure")
        .whereLayer("Infrastructure").mayNotBeAccessedByLayers("Domain")
        .check(classes);
}

1.8 验收标准

编号 验收项 验收方法
AC-1 领域层不包含任何依赖框架或数据库驱动的 import 语句 代码审查 + 构建依赖检查
AC-2 表现层不直接引用基础层类,所有数据通过应用层 DTO 传递 静态检测 + 架构测试
AC-3 应用层不包含领域规则逻辑,领域规则全部封装在领域实体或领域服务中 代码审查
AC-4 基础层只实现领域层定义的接口,不定义自己的业务接口 架构测试(检查基础设施模块的 public 接口是否被领域层接口覆盖)
AC-5 层间接口参数使用领域层类型,不暴露基础层数据模型 代码审查 + 接口契约文档
AC-6 CI 中架构测试通过率 100%,无任何分层违规 CI 构建日志

到此,你已经掌握了框架分层模型的本质。每当你设计新的模块或重构既有代码时,请以本章的层职责表为标尺,用依赖倒置和单向依赖原则来判断每一行代码的归属。下一步,我们将深入表现层的设计模式,学习如何用 MVP、MVVM 和 MVI 等模式将 UI 与业务彻底分离。