Java中的领域驱动设计:不仅仅是理论,更是活代码

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

为什么DDD不是流行术语,而是拯救项目的工具



每位Java开发者都曾遇到过这样的情况:代码能运行,业务逻辑分散在服务层中,半年后只有作者(而且也很吃力)才能理解项目。熟悉吗?这就是经典的贫血领域模型——实体只是带有getter和setter的POJO,所有逻辑都存在于“臃肿”的服务层中。



这时,领域驱动设计(DDD)登场了——这是一种将业务逻辑而非技术细节置于开发核心的方法。不,这不是又一个“理论性”方法论。Java项目中的DDD提供了具体的模式,能将混乱转变为清晰、可维护的架构。在本文中,我们将通过代码和示例,结合实际来剖析DDD,绝不空谈。



战略性DDD vs 战术性DDD:Java项目该如何选择?



DDD分为两个层次:战略性和战术性。前者帮助将大型系统分解为限界上下文并建立它们之间的通信。后者则为代码提供具体的构建块:实体、值对象、聚合、仓库、领域事件等。



对于Java开发者来说,战术性DDD是入门的最佳选择。它提供了今天就能使用的工具。但如果没有对上下文的战略性理解,你可能会创建一个“通用”模型,最终不适合任何人。因此,让我们从小处着手:通过一个简单但真实的领域——订单管理系统——来剖析关键模式。



Java中DDD的构建块:从实体到聚合



1. 实体——每个实体都有其身份



在DDD中,实体是一个具有唯一标识符和连续生命线的对象。你可以更改其所有字段,但它仍然是同一个实体。在Java中,这不仅仅是POJO——这是一个具有行为的对象。



示例:电商订单。即使订单状态发生变化,它仍然是具有相同ID的同一个订单。



public class Order {    private final OrderId id; // 值对象,非原始类型    private OrderStatus status;    private List<OrderLineItem> items;    private Money totalAmount;

public Order(OrderId id) { this.id = id; this.status = OrderStatus.PENDING; this.items = new ArrayList<>(); this.totalAmount = Money.ZERO; }

public void addItem(Product product, int quantity) { // 业务规则:不能向已确认的订单添加商品 if (this.status != OrderStatus.PENDING) { throw new IllegalStateException("Cannot add items to a non-pending order"); } OrderLineItem item = new OrderLineItem(product, quantity); items.add(item); recalculateTotal(); }

public void confirm() { if (items.isEmpty()) { throw new IllegalStateException("Cannot confirm an empty order"); } this.status = OrderStatus.CONFIRMED; // 此处可触发领域事件 // registerEvent(new OrderConfirmedEvent(this.id)); }

private void recalculateTotal() { this.totalAmount = items.stream() .map(OrderLineItem::getSubtotal) .reduce(Money.ZERO, Money::add); }

// Getter——仅用于读取,没有setter! public OrderId getId() { return id; } public OrderStatus getStatus() { return status; } public List<OrderLineItem> getItems() { return Collections.unmodifiableList(items); } public Money getTotalAmount() { return totalAmount; }}


请注意:OrderId不是Long,而是值对象。为什么?因为原始类型不携带语义。OrderId可以包含前缀、创建日期或

博客

书籍推荐