> 技术文档 > Mock到底Mock谁?Clean Architecture 实战+Mock测试最佳实践(含对比代码)

Mock到底Mock谁?Clean Architecture 实战+Mock测试最佳实践(含对比代码)


Clean Architecture 与传统三层架构对比实战:从创建订单功能看架构演进之道

作者:killian| 更新日期:2025年6月 | 字数:约3200字

在企业级开发中,架构往往决定了项目的可维护性、可测试性和可演化性。今天我们将以“创建订单”功能为例,深入对比两种常见的后端架构:传统三层架构与 Clean Architecture,并结合 MyBatis-Plus 持久化实现 + Mockito 单元测试场景,揭示两者的实用差异。


一、引言:三层架构的黄金时代与Clean风潮的兴起

长期以来,Java后端开发主要基于“控制层Controller → 业务层Service → 数据层Repository”的三层架构。它逻辑清晰、便于分工,在早期系统中十分高效。

然而,随着业务复杂度提升,三层架构面临“Service臃肿化”“业务逻辑难以复用”“测试不友好”等痛点。为了解决这些问题,Clean Architecture(整洁架构)开始在微服务时代快速传播。

Clean Architecture 强调“依赖倒置”,鼓励用例驱动、接口编程、解耦耦合,同时提供了高度可测试性的天然设计。


二、业务背景:创建订单的典型流程

以“用户提交购物车商品 → 创建订单记录”这一过程为例,包含:

  • 接收订单请求(userId, 商品列表)
  • 验证数据是否为空
  • 构建订单对象
  • 保存到数据库

我们将通过代码对比这两种架构如何实现这一流程。


三、传统三层架构实现:简单直接,渐臃肿

🎯 架构结构

com.example.layered├── controller├── service / impl├── mapper├── entity├── dto

☑️ 核心代码片段

Controller 层
@RestController@RequestMapping(\"/orders\")public class OrderController { @PostMapping public ResponseEntity<Void> create(@RequestBody CreateOrderRequestDTO dto) { orderService.createOrder(dto); return ResponseEntity.ok().build(); }}
Service 层
public void createOrder(CreateOrderRequestDTO dto) { if (dto.getItems() == null || dto.getItems().isEmpty()) { throw new IllegalArgumentException(\"商品不能为空\"); } OrderEntity entity = new OrderEntity(); entity.setUserId(dto.getUserId()); entity.setItems(dto.getItems()); orderMapper.insert(entity);}

✅ 优点

  • 快速交付
  • 思路清晰

❌ 缺点

  • Service 层容易“变胖”:逻辑、构建、持久化全在一起
  • 没有明确的“业务模型”
  • 依赖 MyBatis 实现,难以 Mock 测试

四、Clean Architecture 实现:解耦清晰,测试友好

🎯 架构结构

com.example.clean├── adapter.in.web # Controller├── application.port.in  # 用例接口├── application.service  # 用例实现├── domain.model  # 领域模型├── domain.port.out  # Repository接口├── adapter.out.persistence # MyBatis实现

☑️ 核心代码片段

Controller 层
@RestController@RequestMapping(\"/orders\")public class OrderController { @PostMapping public ResponseEntity<Void> create(@RequestBody CreateOrderRequestDTO dto) { createOrderUseCase.create(dto); return ResponseEntity.ok().build(); }}
UseCase 实现层
public void create(CreateOrderRequestDTO dto) { Order order = new Order(dto.getUserId(), dto.getItems()); order.validate(); orderRepository.save(order);}
Repository 接口
public interface OrderRepository { void save(Order order);}
Repository 实现
public void save(Order order) { OrderEntity entity = new OrderEntity(); entity.setUserId(order.getUserId()); entity.setItems(order.getItems()); orderMapper.insert(entity);}

✅ 优点

  • 每一层职责单一、清晰
  • 可完全Mock OrderRepository,测试 CreateOrderService
  • Order 对象脱离 ORM,更易维护和复用

❌ 缺点

  • 初期结构复杂(入门门槛略高)

五、Mock 测试的差异:可维护性的真正分水岭

✅ 三层架构测试:只能Mock MyBatis Mapper

@Mock OrderMapper orderMapper;@InjectMocks OrderServiceImpl orderService;@Testvoid should_save_order() { CreateOrderRequestDTO dto = new CreateOrderRequestDTO(...); orderService.createOrder(dto); verify(orderMapper).insert(any());}

缺点:你只能测试“是否执行”,无法验证“业务是否合理”。

✅ Clean 架构测试:可捕获并验证业务模型

@Mock OrderRepository orderRepository;@InjectMocks CreateOrderService service;@Testvoid should_construct_domain_model_correctly() { CreateOrderRequestDTO dto = ...; service.create(dto); ArgumentCaptor<Order> captor = ArgumentCaptor.forClass(Order.class); verify(orderRepository).save(captor.capture()); assertEquals(\"userX\", captor.getValue().getUserId());}

优势:验证构建出的领域对象是否符合预期逻辑,这是真正的单元测试价值。


六、总结对比:实战视角下的取舍与进化

项目属性 三层架构 Clean Architecture 上手速度 ⭐⭐⭐⭐ ⭐⭐ 架构清晰度 ⭐⭐ ⭐⭐⭐⭐ 单元测试友好度 ⭐⭐ ⭐⭐⭐⭐⭐ 对外依赖隔离 ⭐ ⭐⭐⭐⭐ 可维护性 ⭐⭐ ⭐⭐⭐⭐ 团队协作支持 ⭐⭐ ⭐⭐⭐⭐

七、适用建议与进化建议

✅ 适合三层架构的场景:

  • 小型服务 / MVP 快速开发
  • 业务逻辑不复杂的管理后台

✅ 适合 Clean Architecture 的场景:

  • 需要长期演进、可维护性强的核心业务
  • 涉及多个外部依赖(数据库、消息队列、API)
  • 测试覆盖要求高的中大型项目

🧠 进化路线建议

如果你项目是三层架构,也不必立刻大改。不妨:

  • 先从提取用例逻辑开始(UseCase)
  • 将复杂逻辑迁移到 Domain 层
  • 用接口(Port)抽象对外依赖

最终,自然演进为 Clean 架构。


结语:架构不是目的,清晰才是力量

架构的本质不是“炫技”,而是“让系统更清晰、更可控、更能测试”。Clean Architecture 提供了一种思维方式,它引导我们把业务当作核心,而非框架和数据库

如果你还在三层架构中挣扎,不妨试一次 Clean Architecture。就像我们在“创建订单”功能中看到的,你会发现:逻辑分得清、测试写得动、代码能长久

未来,我们还将探讨 Event Sourcing、CQRS 与 Clean 的融合路径,欢迎关注并交流。

本文由 @killian 原创,转载请注明出处。
☕ 请作者喝杯咖啡,持续更新更深入的干货

💡 彩蛋时间:如果你看到了这里,说明你是那种喜欢动手实战的人。那我悄悄分享一个开发圈流传的工具试用入口,貌似跟高效调试很有关系,地址也挺特别的:

🔗 入口

据说注册还能解锁一些隐藏功能,懂的都懂(别外传 😂)