作者:killian| 更新日期:2025年6月 | 字数:约3200字
在企业级开发中,架构往往决定了项目的可维护性、可测试性和可演化性。今天我们将以“创建订单”功能为例,深入对比两种常见的后端架构:传统三层架构与 Clean Architecture,并结合 MyBatis-Plus 持久化实现 + Mockito 单元测试场景,揭示两者的实用差异。
长期以来,Java后端开发主要基于“控制层Controller → 业务层Service → 数据层Repository”的三层架构。它逻辑清晰、便于分工,在早期系统中十分高效。
然而,随着业务复杂度提升,三层架构面临“Service臃肿化”“业务逻辑难以复用”“测试不友好”等痛点。为了解决这些问题,Clean Architecture(整洁架构)开始在微服务时代快速传播。
Clean Architecture 强调“依赖倒置”,鼓励用例驱动、接口编程、解耦耦合,同时提供了高度可测试性的天然设计。
以“用户提交购物车商品 → 创建订单记录”这一过程为例,包含:
我们将通过代码对比这两种架构如何实现这一流程。
com.example.layered
├── controller
├── service / impl
├── mapper
├── entity
├── dto
@RestController
@RequestMapping("/orders")
public class OrderController {
@PostMapping
public ResponseEntity<Void> create(@RequestBody CreateOrderRequestDTO dto) {
orderService.createOrder(dto);
return ResponseEntity.ok().build();
}
}
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);
}
com.example.clean
├── adapter.in.web # Controller
├── application.port.in # 用例接口
├── application.service # 用例实现
├── domain.model # 领域模型
├── domain.port.out # Repository接口
├── adapter.out.persistence # MyBatis实现
@RestController
@RequestMapping("/orders")
public class OrderController {
@PostMapping
public ResponseEntity<Void> create(@RequestBody CreateOrderRequestDTO dto) {
createOrderUseCase.create(dto);
return ResponseEntity.ok().build();
}
}
public void create(CreateOrderRequestDTO dto) {
Order order = new Order(dto.getUserId(), dto.getItems());
order.validate();
orderRepository.save(order);
}
public interface OrderRepository {
void save(Order order);
}
public void save(Order order) {
OrderEntity entity = new OrderEntity();
entity.setUserId(order.getUserId());
entity.setItems(order.getItems());
orderMapper.insert(entity);
}
OrderRepository
,测试 CreateOrderService
@Mock OrderMapper orderMapper;
@InjectMocks OrderServiceImpl orderService;
@Test
void should_save_order() {
CreateOrderRequestDTO dto = new CreateOrderRequestDTO(...);
orderService.createOrder(dto);
verify(orderMapper).insert(any());
}
缺点:你只能测试“是否执行”,无法验证“业务是否合理”。
@Mock OrderRepository orderRepository;
@InjectMocks CreateOrderService service;
@Test
void 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 |
---|---|---|
上手速度 | ⭐⭐⭐⭐ | ⭐⭐ |
架构清晰度 | ⭐⭐ | ⭐⭐⭐⭐ |
单元测试友好度 | ⭐⭐ | ⭐⭐⭐⭐⭐ |
对外依赖隔离 | ⭐ | ⭐⭐⭐⭐ |
可维护性 | ⭐⭐ | ⭐⭐⭐⭐ |
团队协作支持 | ⭐⭐ | ⭐⭐⭐⭐ |
如果你项目是三层架构,也不必立刻大改。不妨:
最终,自然演进为 Clean 架构。
架构的本质不是“炫技”,而是“让系统更清晰、更可控、更能测试”。Clean Architecture 提供了一种思维方式,它引导我们把业务当作核心,而非框架和数据库。
如果你还在三层架构中挣扎,不妨试一次 Clean Architecture。就像我们在“创建订单”功能中看到的,你会发现:逻辑分得清、测试写得动、代码能长久。
未来,我们还将探讨 Event Sourcing、CQRS 与 Clean 的融合路径,欢迎关注并交流。
本文由 @killian 原创,转载请注明出处。
☕ 请作者喝杯咖啡,持续更新更深入的干货
彩蛋时间:如果你看到了这里,说明你是那种喜欢动手实战的人。那我悄悄分享一个开发圈流传的工具试用入口,貌似跟高效调试很有关系,地址也挺特别的:
入口
据说注册还能解锁一些隐藏功能,懂的都懂(别外传 )