Appearance
三个服务和领域对象的职责
要理解DDD中领域对象、领域服务、应用服务、基础设施服务的边界,核心是抓住「业务规则的归属」和「技术细节的隔离」。以下从定义-职责-边界-例子四维度展开,并结合「用户下单」流程具体说明。
一、核心概念与职责边界
先通过一句话总结和对比表格明确每个组件的定位:
| 组件 | 所在层 | 核心职责 | 关键特征 | 禁止做的事 |
|---|---|---|---|---|
| 领域对象 | 领域层 | 封装领域状态(数据)和自身相关的业务行为(方法),是业务规则的“原子载体”。 | 有状态、富行为(非贫血模型) | 依赖技术细节(如数据库、API) |
| 领域服务 | 领域层 | 处理跨领域对象的业务规则或无法归属单个对象的逻辑,是业务规则的“组合器”。 | 无状态、纯业务逻辑 | 处理技术细节(如保存数据) |
| 应用服务 | 应用层 | 作为用例入口,协调领域对象/服务完成流程,不包含业务规则。 | 无状态、流程协调 | 包含任何业务规则 |
| 基础设施服务 | 基础设施层 | 提供技术实现能力(数据库、第三方API、消息队列等),是领域层的“工具库”。 | 实现抽象接口(依赖倒置) | 包含任何业务规则 |
1. 领域对象(Domain Object):业务规则的“原子载体”
定义:领域的核心构件,包括实体(Entity)(有唯一标识,如Order、Product)和值对象(Value Object)(无唯一标识,如UserAddress、Money)。
职责:
- 封装自身状态(如订单的商品列表、用户地址);
- 实现自身相关的业务行为(如订单计算总价、商品扣减库存);
- 保证自身状态的一致性(如扣库存时检查是否足够)。
例子:
Order实体:包含orderId、lines(商品行)、totalPrice(总价),有calculateTotalPrice()方法(自己计算总价);Product实体:包含productId、stock(库存),有reduceStock(int quantity)方法(自己扣减库存,且检查库存是否足够);UserAddress值对象:包含province、city、street,有isValid()方法(自己验证地址格式是否合法)。
2. 领域服务(Domain Service):跨对象业务规则的“组合器”
定义:当业务规则无法归属单个领域对象(需跨多个对象协作)时,用领域服务封装。
职责:
- 处理跨领域对象的业务规则(如验证订单库存需查所有商品的库存);
- 处理不属于单个对象的逻辑(如复杂定价规则:满减+优惠券);
- 无状态(不保存数据,仅做逻辑计算)。
关键判断:如果一个逻辑需要询问多个领域对象(而非让单个对象“自己做”),则用领域服务。
例子:
OrderValidationService:验证订单的库存(需遍历Order的商品行,查询对应Product的库存)、验证地址(需结合User的地区和UserAddress的有效性);PricingService:计算订单最终价格(需结合Order的总价、Coupon的折扣、User的会员等级)。
3. 应用服务(Application Service):用例流程的“协调者”
定义:应用层的入口,对应用户/系统的用例(如“下单”“退款”),负责协调领域层和基础设施层完成流程。
职责:
- 接收外部请求(如REST API的
PlaceOrderRequest),转换为领域对象(如OrderLine、UserAddress); - 协调流程:调用领域服务验证规则→调用领域对象执行行为→调用基础设施服务保存数据;
- 返回结果(如订单ID),不包含任何业务规则。
关键特征:应用服务是“无业务规则的流程管家”,它只关心“先做什么、后做什么”,不关心“怎么做”(怎么做由领域层负责)。
4. 基础设施服务(Infrastructure Service):技术细节的“实现者”
定义:负责提供技术能力,实现领域层定义的抽象接口(依赖倒置原则),隔离业务逻辑与技术细节。
职责:
- 实现持久化(如
OrderRepository保存订单到数据库); - 调用第三方服务(如
NotificationService发送短信、PaymentGateway调用微信支付); - 处理中间件(如消息队列发送异步消息)。
关键原则:领域层定义抽象接口(如OrderRepository),基础设施层实现接口(如JpaOrderRepository),确保领域层不依赖具体技术。
二、“用户下单”流程:四者的分工实战
以下用代码片段+流程步骤说明“用户下单”中各组件的协作(伪代码,聚焦核心逻辑)。
1. 前提:定义领域层抽象接口
领域层定义需要的抽象(依赖倒置):
java
// 领域层:订单仓库接口(抽象)
public interface OrderRepository {
void save(Order order);
}
// 领域层:商品仓库接口(抽象)
public interface ProductRepository {
Optional<Product> findById(ProductId id);
void save(Product product);
}
// 领域层:通知服务接口(抽象)
public interface NotificationService {
void sendOrderPlacedNotification(User user, Order order);
}2. 领域对象实现(核心业务规则)
java
// 领域对象:订单实体(Entity)
public class Order {
private OrderId id;
private UserId userId;
private List<OrderLine> lines; // 商品行(值对象)
private UserAddress address; // 地址(值对象)
private Money totalPrice; // 总价(值对象)
// 工厂方法:创建订单(保证状态一致性)
public static Order create(UserId userId, List<OrderLine> lines, UserAddress address) {
Order order = new Order();
order.id = OrderId.generate();
order.userId = userId;
order.lines = lines;
order.address = address;
order.calculateTotalPrice(); // 自己计算总价(领域行为)
return order;
}
// 领域行为:计算总价(自身业务规则)
private void calculateTotalPrice() {
this.totalPrice = lines.stream()
.map(line -> line.getPrice().multiply(line.getQuantity()))
.reduce(Money.ZERO, Money::add);
}
// 省略getter...
}
// 领域对象:商品实体(Entity)
public class Product {
private ProductId id;
private String name;
private Money price;
private int stock;
// 领域行为:扣减库存(自身业务规则,保证状态一致)
public void reduceStock(int quantity) {
if (stock < quantity) {
throw new InsufficientStockException("商品库存不足:" + name);
}
this.stock -= quantity;
}
// 省略getter...
}
// 领域对象:地址值对象(Value Object)
public class UserAddress {
private String province;
private String city;
private String street;
// 工厂方法:创建地址(验证格式)
public static UserAddress create(String province, String city, String street) {
validateAddress(province, city, street);
return new UserAddress(province, city, street);
}
// 领域行为:验证地址有效性(自身业务规则)
private static void validateAddress(String province, String city, String street) {
if (StringUtils.isBlank(province) || StringUtils.isBlank(city) || StringUtils.isBlank(street)) {
throw new InvalidAddressException("地址不能为空");
}
// 可扩展更多规则(如省份是否存在)
}
// 省略getter...
}3. 领域服务实现(跨对象业务规则)
java
// 领域服务:订单验证服务(跨Order和Product)
@Service
public class OrderValidationService {
private final ProductRepository productRepository;
// 注入领域层抽象接口(依赖倒置)
public OrderValidationService(ProductRepository productRepository) {
this.productRepository = productRepository;
}
// 业务规则:验证订单库存是否充足(跨Order和Product)
public void validateStock(Order order) {
for (OrderLine line : order.getLines()) {
Product product = productRepository.findById(line.getProductId())
.orElseThrow(() -> new ProductNotFoundException("商品不存在:" + line.getProductId()));
if (product.getStock() < line.getQuantity()) {
throw new InsufficientStockException("商品库存不足:" + product.getName());
}
}
}
// 业务规则:验证地址是否属于用户所在地区(跨User和UserAddress)
public void validateAddress(User user, UserAddress address) {
if (!user.getRegion().equals(address.getProvince())) {
throw new InvalidAddressException("地址不在用户所在地区:" + user.getRegion());
}
}
}4. 应用服务实现(流程协调)
java
// 应用服务:订单应用服务(对应“下单”用例)
@Service
public class OrderApplicationService {
private final OrderRepository orderRepository;
private final ProductRepository productRepository;
private final UserRepository userRepository;
private final OrderValidationService orderValidationService;
private final NotificationService notificationService;
// 注入所有依赖(领域服务+基础设施服务的抽象接口)
public OrderApplicationService(OrderRepository orderRepository,
ProductRepository productRepository,
UserRepository userRepository,
OrderValidationService orderValidationService,
NotificationService notificationService) {
this.orderRepository = orderRepository;
this.productRepository = productRepository;
this.userRepository = userRepository;
this.orderValidationService = orderValidationService;
this.notificationService = notificationService;
}
// 下单用例入口:接收命令对象(封装请求参数)
public OrderId placeOrder(PlaceOrderCommand command) {
// 1. 解析参数,获取领域对象
User user = userRepository.findById(command.getUserId())
.orElseThrow(() -> new UserNotFoundException("用户不存在:" + command.getUserId()));
List<OrderLine> lines = buildOrderLines(command.getProductQuantities());
UserAddress address = UserAddress.create(command.getProvince(), command.getCity(), command.getStreet());
// 2. 调用领域服务验证业务规则
orderValidationService.validateAddress(user, address); // 验证地址归属
Order order = Order.create(user.getId(), lines, address); // 创建订单(自动算总价)
orderValidationService.validateStock(order); // 验证库存
// 3. 执行领域行为(扣减库存)
deductStock(order);
// 4. 调用基础设施服务:保存订单
orderRepository.save(order);
// 5. 调用基础设施服务:发送通知
notificationService.sendOrderPlacedNotification(user, order);
// 6. 返回结果(订单ID)
return order.getId();
}
// 辅助方法:构建商品行(转换参数为领域对象)
private List<OrderLine> buildOrderLines(List<ProductQuantityCommand> productQuantities) {
return productQuantities.stream()
.map(cmd -> {
Product product = productRepository.findById(cmd.getProductId())
.orElseThrow(() -> new ProductNotFoundException("商品不存在:" + cmd.getProductId()));
return OrderLine.create(product.getId(), product.getPrice(), cmd.getQuantity());
})
.collect(Collectors.toList());
}
// 辅助方法:扣减库存(调用Product的领域行为)
private void deductStock(Order order) {
for (OrderLine line : order.getLines()) {
Product product = productRepository.findById(line.getProductId())
.orElseThrow(() -> new ProductNotFoundException("商品不存在:" + line.getProductId()));
product.reduceStock(line.getQuantity()); // 调用商品自身的扣库存行为
productRepository.save(product); // 保存更新后的商品(基础设施服务)
}
}
}5. 基础设施服务实现(技术细节)
java
// 基础设施服务:订单仓库的JPA实现(实现领域层抽象接口)
@Repository
public class JpaOrderRepository implements OrderRepository {
private final OrderJpaEntityRepository jpaRepository; // Spring Data JPA接口
@Autowired
public JpaOrderRepository(OrderJpaEntityRepository jpaRepository) {
this.jpaRepository = jpaRepository;
}
@Override
public void save(Order order) {
// 转换领域对象为JPA实体(ORM映射)
OrderJpaEntity jpaEntity = OrderJpaEntity.fromDomain(order);
jpaRepository.save(jpaEntity);
}
}
// 基础设施服务:通知服务的实现(调用第三方推送API)
@Service
public class PushNotificationService implements NotificationService {
private final PushApiClient pushApiClient; // 第三方推送SDK
@Autowired
public PushNotificationService(PushApiClient pushApiClient) {
this.pushApiClient = pushApiClient;
}
@Override
public void sendOrderPlacedNotification(User user, Order order) {
String message = String.format("您的订单%s已创建,总价%s元", order.getId(), order.getTotalPrice());
pushApiClient.send(user.getPhone(), message); // 调用第三方API(技术细节)
}
}三、关键总结:避免常见误区
- 禁止领域对象依赖技术:领域对象不能调用数据库、API等,否则会污染业务逻辑(如
Order不能有save()方法)。 - 禁止应用服务包含业务规则:应用服务只做流程协调,不能计算总价、验证库存(如
placeOrder方法不能写if (product.stock < quantity))。 - 禁止领域服务处理技术细节:领域服务不能保存数据、调用API(如
OrderValidationService不能调用productRepository.save())。 - 必须依赖倒置:领域层定义抽象接口,基础设施层实现,确保领域层独立于技术(如
OrderRepository是接口,JpaOrderRepository是实现)。
通过以上例子,你可以清晰看到:所有业务规则都在领域层(领域对象/服务),应用层负责流程,基础设施层负责技术实现。这种分层方式让业务逻辑更纯粹、更易维护,也更能应对需求变化(比如换数据库只需要改基础设施层,不影响领域层)。
