在Spring Boot项目中,采用不同的架构模式会直接体现在项目的目录结构上。下面我将结合一个常见的订单系统案例,为你详细对比领域驱动设计(DDD)和传统三层架构的目录结构、设计思想与代码实现。
🏗️ 架构核心区别一览
在深入目录细节前,了解两者的核心差异至关重要。传统三层架构是技术导向 的,以数据流为中心进行技术分层;而DDD是业务导向的,以领域模型为核心进行设计。
| 对比维度 | 传统三层架构 (Technology-Driven) | DDD四层架构 (Business-Driven) |
|---|---|---|
| 设计出发点 | 技术分层,关注"如何实现"数据流 | 领域建模,关注"业务是什么" |
| 核心模型 | 贫血模型:实体主要是数据载体,行为分散在Service中 | 充血模型:实体包含数据和行为,业务逻辑内聚 |
| 代码组织核心 | 技术职责 (Controller, Service, Repository) | 业务领域 (如Order, Product子域) |
| 适合场景 | 业务简单、以CRUD为主的管理系统 | 业务复杂、需要长期演进的核心系统(如电商、金融) |
📁 项目目录结构详解
以下是一个基于Spring Boot的订单管理系统,分别以两种架构模式组织的目录结构。
传统三层架构目录结构
这种结构按技术职责横向切割,简单直接,适合CRUD应用。
bash
src/main/java/com/example/orderapp/
├── controller/ # 表现层:处理HTTP请求和响应
│ ├── OrderController.java # 订单相关的REST接口
│ └── ProductController.java # 商品相关的REST接口
├── service/ # 业务逻辑层:承载核心业务逻辑
│ ├── OrderService.java # 订单业务服务接口
│ ├── OrderServiceImpl.java # 订单业务服务实现
│ ├── ProductService.java # 商品业务服务接口
│ └── ProductServiceImpl.java # 商品业务服务实现
├── dao/ # 数据访问层(或称持久层)
│ ├── OrderRepository.java # 订单数据访问接口(继承JpaRepository)
│ └── ProductRepository.java # 商品数据访问接口
├── entity/ # 实体类(贫血模型,通常与数据库表对应)
│ ├── Order.java
│ └── Product.java
└── dto/ # 数据传输对象,用于前后端数据交互
├── OrderDTO.java
└── ProductDTO.java
关键代码示例(贫血模型):
在传统三层架构中,实体类通常是只有数据和getter/setter的"贫血模型",业务逻辑集中在Service中。
less
// entity/Order.java (贫血模型)
@Entity
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private BigDecimal amount;
private String status;
// ... 只有getter和setter方法,没有业务行为
}
// service/OrderServiceImpl.java (业务逻辑分散在Service)
@Service
public class OrderServiceImpl implements OrderService {
@Autowired
private OrderRepository orderRepository;
@Override
@Transactional
public void createOrder(OrderDTO orderDTO) {
// 1. 参数校验逻辑在Service
if (orderDTO.getAmount() == null || orderDTO.getAmount().compareTo(BigDecimal.ZERO) <= 0) {
throw new IllegalArgumentException("金额必须大于0");
}
// 2. 创建订单实体(只是数据载体)
Order order = new Order();
order.setAmount(orderDTO.getAmount());
order.setStatus("CREATED");
// 3. 保存到数据库
orderRepository.save(order);
}
}
DDD四层架构目录结构
DDD按业务域纵向划分,强调领域模型的核心地位,结构更复杂但业务内聚性更高。
bash
src/main/java/com/example/orderapp/
├── interfaces/ # 用户接口层(或称表现层)
│ ├── assembler/ # 装配器:负责DTO与领域模型的互相转换
│ │ └── OrderAssembler.java
│ ├── dto/ # 数据传输对象(DTO),接收请求和返回响应
│ │ ├── request/
│ │ │ └── OrderCreateRequest.java
│ │ └── response/
│ │ └── OrderResponse.java
│ └── controller/
│ └── OrderController.java # 更薄,主要负责协议处理
├── application/ # 应用层:薄层,负责用例编排、事务控制等
│ ├── service/
│ │ └── OrderApplicationService.java # 应用服务,协调多个领域对象
│ └── command/ # (可选)命令对象,如CreateOrderCommand
│ └── CreateOrderCommand.java
├── domain/ # 领域层(核心):包含丰富的业务逻辑
│ ├── model/ # 领域模型
│ │ ├── aggregate/ # 聚合
│ │ │ └── Order.java # 聚合根(充血模型,包含数据和行为)
│ │ ├── valueobject/ # 值对象
│ │ │ └── Money.java # 例如金额值对象(含货币单位和金额)
│ │ └── entity/ # 实体(当不是聚合根时)
│ │ └── OrderItem.java # 订单项
│ ├── service/ # 领域服务:处理不适合放在实体内的业务逻辑
│ │ └── OrderDomainService.java # 例如处理跨聚合的逻辑
│ ├── repository/ # 仓储接口:定义持久化操作,面向领域
│ │ └── OrderRepository.java
│ └── event/ # 领域事件
│ └── OrderCreatedEvent.java
└── infrastructure/ # 基础设施层:为上层提供技术实现
├── persistence/ # 持久化实现
│ ├── dao/ # 数据访问对象(如JPA Entity)
│ │ └── OrderPO.java # 持久化对象(与数据库表结构映射)
│ ├── repository/
│ │ └── OrderRepositoryImpl.java # 仓储接口的实现
│ └── converter/
│ └── OrderConverter.java # 负责领域对象与持久化对象的转换
├── client/ # 外部服务适配器(如调用其他微服务)
├── config/ # 配置类
└── common/ # 通用技术组件(如工具类)
关键代码示例(充血模型与分层协作):
DDD的领域层使用"充血模型",业务逻辑封装在领域对象内部。
java
// domain/model/aggregate/Order.java (聚合根,充血模型)
public class Order {
private OrderId id; // 使用值对象ID,而非基本类型
private Money totalAmount; // 使用值对象
private OrderStatus status;
private List<OrderItem> items;
// 核心业务行为封装在实体内部
public static Order create(CreateOrderCommand command, Product product) {
// 业务规则校验
if (!product.hasEnoughStock(command.getQuantity())) {
throw new DomainException("库存不足");
}
Order order = new Order();
order.id = OrderId.nextId();
order.status = OrderStatus.CREATED;
order.items = Arrays.asList(new OrderItem(product.getId(), command.getQuantity()));
order.calculateTotal(); // 计算总价
// 发布领域事件
order.registerEvent(new OrderCreatedEvent(order.getId()));
return order;
}
private void calculateTotal() {
this.totalAmount = items.stream()
.map(OrderItem::getSubtotal)
.reduce(Money.ZERO, Money::add);
}
// 其他业务方法,如 order.confirm()、order.cancel() 等
}
// application/service/OrderApplicationService.java (应用服务,薄层)
@Service
public class OrderApplicationService {
// 依赖的是领域层的接口
private final OrderRepository orderRepository;
private final ProductService productService; // 假设是领域服务
@Transactional
public OrderResponse createOrder(CreateOrderCommand command) {
// 1. 获取依赖的领域对象(如商品)
Product product = productService.findProduct(command.getProductId());
// 2. 调用领域模型执行核心业务逻辑
Order newOrder = Order.create(command, product);
// 3. 持久化(基础设施层实现)
orderRepository.save(newOrder);
// 4. 返回结果(通过装配器转换为DTO)
return OrderAssembler.toResponse(newOrder);
}
}
// interfaces/controller/OrderController.java (薄薄的控制器)
@RestController
@RequestMapping("/orders")
public class OrderController {
private final OrderApplicationService orderAppService;
public OrderController(OrderApplicationService orderAppService) {
this.orderAppService = orderAppService;
}
@PostMapping
public ResponseEntity<OrderResponse> createOrder(@RequestBody @Valid OrderCreateRequest request) {
// 1. 将请求DTO转换为应用层需要的Command/Query
CreateOrderCommand command = OrderAssembler.toCommand(request);
// 2. 调用应用服务
OrderResponse response = orderAppService.createOrder(command);
// 3. 返回响应
return ResponseEntity.ok(response);
}
}
💡 如何选择适合的架构
- 选择传统三层架构 :如果您的项目业务相对简单,主要是增删改查(CRUD)操作,且后期不会变得非常复杂。例如,很多后台管理系统、数据报表系统或简单的REST API服务。这种架构上手快,开发效率高。
- 选择DDD架构 :如果您的系统业务逻辑复杂,包含大量核心规则和状态流转,且需要长期演进的系统。例如,电商交易核心、风控系统、结算系统等。虽然前期设计和建模成本较高,但能有效应对业务复杂性,提升代码的可维护性和扩展性。