分层架构中的边界:表现层、应用层、领域层、基础设施层的职责与解耦规则

分层架构是解决方案设计中最成熟、最通用的结构范式之一。但“分层”本身并不带来价值——真正带来价值的是层与层之间清晰的边界定义和严格的解耦纪律。本章聚焦四层经典分层(表现层、应用层、领域层、基础设施层),从边界识别、职责划分、数据流控制流、信任链路到落地验收,给出可操作的边界实践指南。

边界识别 — 层与层之间的割裂带

每一层都代表一个关注点维度,层间边界本质上是职责变化率和依赖方向的分界线。变化原因不同、变化频率不同、依赖方向相反的代码,就应该分属不同层级。

边界位置边界两侧的职责差异典型“越界”信号
表现层 ↔ 应用层用户交互 / 用例编排Controller 中直接调用 Repository
应用层 ↔ 领域层事务与工作流 / 核心业务规则Service 中包含 if-else 业务判断
领域层 ↔ 基础设施层业务抽象 / 技术实现Entity 中引用 JPA 或 Redis 注解
基础设施层 ↔ 外部系统适配转换 / 原生协议基础设施代码中混入业务逻辑

边界识别第一原则:如果一段代码同时因为“UI 变更”和“业务规则变更”而需要修改,说明它跨越了不该跨越的边界。

分层设计 — 横向切分与纵向切分

四层架构是横向切分(按技术关注点分层),而业务模块是纵向切分(按功能域分组)。两者交织形成网格结构。每一层只对直接下层有依赖,且依赖方向必须指向领域层。

分层架构边界与解耦关系图 表现层 (Presentation) 界面 / API 端点 / 视图模型 / DTO 解析 应用层 (Application) 用例编排 / 应用服务 / 事务边界 / 任务调度 领域层 (Domain) 实体 / 值对象 / 聚合 / 领域服务 / 仓储接口 基础设施层 (Infrastructure) 持久化实现 / 消息队列 / 外部服务适配 / 缓存 解耦规则 ① 依赖倒置原则 ② 接口归属领域层 ③ DTO 跨层转换 ④ 无循环依赖 ⑤ 仅通过接口通信 ⑥ 基础设施适配领域 ⑦ 层间数据不可变性 ⑧ 禁止跨层跳过 越界判定标准: • 某层代码引用了 非直接下层的类型 • 基础设施中出现 业务规则判断 • 领域层依赖了 具体技术框架 信任链路:上层依赖下层接口,下层绝不反向依赖上层;依赖倒置确保领域层纯净

上图展示了四层的垂直堆叠关系与核心解耦约束。注意:领域层不引用任何基础设施层的具体实现,它只定义接口(仓储接口、事件接口),由基础设施层实现。依赖方向永远指向领域层,这是分层架构的“宪法”。

模块职责 — 每层该做什么,不该做什么

每层的职责边界一旦模糊,架构就会退化。下表列出每层的“应做清单”和“不应做清单”:

层级应做 (Responsibility)不应做 (Violation)
表现层请求解析、参数校验、视图渲染、Session 管理调用 Repository、编写业务规则、处理事务
应用层用例编排、事务控制、权限检查、DTO 转换包含 if-else 业务逻辑、直接操作数据库
领域层核心业务规则、实体行为、值对象不变性、领域事件引用 ORM 注解、依赖 HTTP 或消息框架
基础设施层数据持久化、消息发送、缓存操作、外部 API 适配包含业务判断、跨层跳过应用层

职责检查技巧:针对任意一个类,问两个问题——(1)如果切换 UI 框架,这个类是否需要修改?(2)如果变更一条业务规则,这个类是否需要修改?如果两个答案都是“是”,说明它跨越了边界。

数据流与控制流 — 请求的完整生命周期

一次典型的请求在四层之间以“请求—响应”模式逐层传递,每层只做自己职责内的事,然后交给下一层。控制流由应用层主导,数据流以 DTO / 领域对象为载体跨层传输。

  1. 表现层接收 HTTP 请求,反序列化为 DTO,调用应用层服务。
  2. 应用层将 DTO 转换为领域对象(或直接传递),启动事务,调用领域层服务。
  3. 领域层执行业务规则,通过仓储接口(定义在领域层)持久化数据,发布领域事件。
  4. 基础设施层实现仓储接口,执行 SQL / NoSQL / 消息操作,返回结果。
  5. 响应沿原路径逆向返回:领域层返回结果 → 应用层组装 DTO → 表现层序列化输出。
// 伪代码:请求在各层的完整流转示意
// 表现层
@PostMapping("/orders")
public OrderResponse createOrder(@RequestBody OrderRequest request) {
    CreateOrderCommand cmd = request.toCommand();
    OrderResult result = orderApplicationService.createOrder(cmd);
    return OrderResponse.from(result);
}

// 应用层
@Transactional
public OrderResult createOrder(CreateOrderCommand cmd) {
    Order order = orderDomainService.placeOrder(cmd.toDomain());
    orderRepository.save(order);       // 调用领域层定义的接口
    return OrderResult.from(order);
}

// 领域层
public Order placeOrder(CreateOrderCommand cmd) {
    Order order = new Order(cmd.customerId, cmd.items);
    order.calculateTotal();            // 核心业务规则
    this.domainEvents.add(new OrderPlacedEvent(order));
    return order;
}

// 基础设施层
public class OrderRepositoryImpl implements OrderRepository {
    public void save(Order order) {
        entityManager.persist(order);  // 具体技术实现
    }
}

常见反模式:应用层直接调用基础设施层的 Repository 实现类,绕过了领域层。这会导致业务规则散落在应用层,领域层退化为“数据容器”。

信任链路 — 依赖方向与接口适配

分层架构的信任模型建立在单向依赖之上:上层信任下层提供的接口契约,下层信任上层不会越过边界直接访问实现细节。领域层是信任链路的“锚点”,所有层都围绕领域层定义接口。

  • 依赖倒置(DIP):领域层定义接口,基础设施层实现接口。领域层不依赖基础设施层,两者都依赖抽象。
  • 接口归属:仓储接口、事件发布器接口等抽象定义在领域层,不在基础设施层定义。
  • 跨层数据不可变:传递到下层的数据,下层不能修改;如需修改,必须返回新对象。
// 接口归属领域层(正确)
// 文件位置: domain/repository/OrderRepository.java
public interface OrderRepository {
    void save(Order order);
    Order findById(OrderId id);
}

// 基础设施层实现(正确)
// 文件位置: infrastructure/persistence/OrderRepositoryImpl.java
public class OrderRepositoryImpl implements OrderRepository {
    // JPA / MyBatis / MongoDB 实现
}

选型权衡 — 分层带来的得与失

维度优点代价
可维护性每层职责单一,修改范围可控新增一个简单功能需要修改 4 层文件
可测试性领域层可脱离基础设施做纯单元测试需要大量 Mock 和接口适配代码
技术替换替换 ORM 或消息队列不影响领域层接口抽象增加初次开发成本
团队协作前后端、业务、基础架构可以并行严格的代码评审和架构守护成本
性能—多层 DTO 拷贝和调用链带来额外开销

选型建议:当业务复杂度超过 10 个实体且有多条跨实体规则时,四层分层的收益显著大于成本。极简单 CRUD 场景可考虑“扁平化”处理,但必须预留向分层演进的接口边界。

风险点 — 分层架构的常见陷阱

  • 层间泄漏:表现层直接使用领域对象(如 Entity)渲染视图,导致 UI 变更波及领域层。应使用 DTO 隔离。
  • 贫血领域模型:领域层只有 getter/setter,所有业务逻辑放在应用层。领域层退化为“数据袋”。
  • 循环依赖:领域层依赖基础设施层的接口(本应相反),导致 Maven/Gradle 无法编译。
  • 过度分层:为每一个简单查询都创建完整的四层结构,反而降低可读性和开发效率。
  • 事务扩散:事务注解出现在应用层和基础设施层多处,难以追踪。事务边界应统一在应用层控制。

避坑口诀:表现层只认 DTO,应用层管事务,领域层守规则,基础设施层做实现;接口定义在领域,依赖方向不回头。

落地步骤 — 从零开始构建分层

  1. 识别核心域:与业务方一起梳理实体、值对象、聚合根,定义领域层边界。
  2. 先写领域层接口:定义仓储接口、事件接口、领域服务接口,不写实现。
  3. 再写应用层用例:每个用例对应一个应用服务方法,编排领域层调用。
  4. 实现基础设施层:按领域层接口编写 JPA / MyBatis / Redis 等具体实现。
  5. 构建表现层:编写 Controller 或 GraphQL Resolver,通过 DTO 与应用层通信。
  6. 配置依赖注入:将基础设施层的实现注入到接口中(如 Spring @Bean 或手动 DI)。
  7. 写跨层契约测试:验证请求从表现层到基础设施层的完整链路正确性。

验收标准 — 如何衡量分层是否合理

验收项检查方式通过标准
依赖方向检查使用 ArchUnit 或 JDepend 分析包依赖领域层不依赖任何基础设施层包
层间引用检查代码审查 + 依赖分析工具表现层不直接引用基础设施层类
DTO 隔离检查搜索 @Entity 是否出现在 Controller 中表现层无领域对象引用
业务规则位置统计 if-else / switch 在层中的分布90% 以上业务规则在领域层
修改影响范围模拟一次 UI 变更和一次业务规则变更变更只涉及对应层,不跨层修改
接口归属验证检查所有仓储接口定义位置仓储接口 100% 定义在领域层

验收红线:如果在验收时发现“领域层直接使用了 MyBatis 的 @Select 注解”或“Controller 中调用了 Repository.save()”,视为架构违规,必须重构后才能通过。

分层架构的边界不是一道墙,而是一组可验证、可守护的设计契约。当每一层的职责清晰、依赖方向正确、数据流转有序时,整个解决方案就具备了应对业务变化和技术替换的核心韧性。