DDD模型初探

简介: DDD模型初探

背景

面试官: DDD模型知道吗?

了不起: 知道,DDD叫领域驱动设计。

面试官: 在实际项目中使用过吗?

了不起: 没有使用过

面试官: 如果要求你用DDD来设计一个订单系统, 你会这么设计?

相关概念

聚合根: 它是一个实体对象,代表了一个业务上的整体,它可以包含多个实体对象和值对象。聚合根负责维护整个聚合内部的一致性,所有对聚合内部的操作都必须通过聚合根进行。在实现订单管理功能时,我们可以使用聚合根来维护订单和订单项之间的关系。

实体对象: 实体对象是具有唯一标识符的对象,它们具有生命周期和状态,并且可以与其他实体对象进行交互。在订单管理系统中,我们可以定义一个Order实体对象,它包含订单号、订单状态、订单金额等属性。

值对象: 值对象是没有唯一标识符的对象,它们通常用于描述实体对象的属性或特征。在订单管理系统中,我们可以定义一个Address值对象,它包含收货人姓名、收货地址、联系电话等属性。

限界上下文:在订单管理系统中,限界上下文是一个非常重要的概念。它定义了一个业务领域的边界,包括一组相关的实体对象和值对象,以及它们之间的关系。限界上下文通常是一个独立的模块,可以被独立地开发、测试和部署。

构建项目的步骤

确定限界上下文

在订单管理系统中,订单模块是一个独立的业务领域,它包含订单的创建、确认、发货、收货、取消等业务流程。因此,我们可以将订单模块定义为一个限界上下文。

  • 订单管理:这个上下文包括Order实体对象,以及相关的值对象,如Address和OrderItem。它还包括与订单管理相关的领域服务,如计算订单总额或更新订单状态。
  • 客户管理:这个上下文包括Customer实体对象,以及相关的值对象,如ContactInfo和PaymentMethod。它还包括与客户管理相关的领域服务,如创建新客户或更新客户信息。
  • 库存管理:这个上下文包括Product实体对象,以及相关的值对象,如ProductVariant和StockLevel。它还包括与库存管理相关的领域服务,如更新库存水平或为订单保留库存。

确定实体对象和值对象

在订单管理系统中,我们可以定义一个Order实体对象,它包含订单号、订单状态、订单金额等属性。我们还可以定义一个Address值对象,它包含收货人姓名、收货地址、联系电话等属性。另外,我们还可以定义一个OrderItem值对象,它包含商品名称、商品数量、商品单价等属性。

Order实体对象

public class Order {
    private String orderId;
    private OrderStatus orderStatus;
    private BigDecimal orderAmount;
    private List<OrderItem> orderItems;
    private Address shippingAddress;
    private Date orderDate;
    // 构造函数
    public Order(String orderId, OrderStatus orderStatus, BigDecimal orderAmount, List<OrderItem> orderItems, Address shippingAddress, Date orderDate) {
        this.orderId = orderId;
        this.orderStatus = orderStatus;
        this.orderAmount = orderAmount;
        this.orderItems = orderItems;
        this.shippingAddress = shippingAddress;
        this.orderDate = orderDate;
    }
    // getter和setter方法
    public String getOrderId() {
        return orderId;
    }
    public void setOrderId(String orderId) {
        this.orderId = orderId;
    }
    .....
    // 计算订单总金额
    public BigDecimal calculateOrderAmount() {
        BigDecimal totalAmount = BigDecimal.ZERO;
        for (OrderItem item : orderItems) {
            totalAmount = totalAmount.add(item.getItemAmount());
        }
        return totalAmount;
    }
    // 更新订单状态
    public void updateOrderStatus(OrderStatus newStatus) {
        this.orderStatus = newStatus;
    }
}

Address和OrderItem值对象

public class Address {
    private String name;
    private String addressLine1;
    private String addressLine2;
    private String city;
    private String state;
    private String zipCode;
    private String phoneNumber;
    // 构造函数
    public Address(String name, String addressLine1, String addressLine2, String city, String state, String zipCode, String phoneNumber) {
        this.name = name;
        this.addressLine1 = addressLine1;
        this.addressLine2 = addressLine2;
        this.city = city;
        this.state = state;
        this.zipCode = zipCode;
        this.phoneNumber = phoneNumber;
    }
    // getter和setter方法
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    .....
}
public class OrderItem {
    private String itemName;
    private int quantity;
    private BigDecimal itemPrice;
    // 构造函数
    public OrderItem(String itemName, int quantity, BigDecimal itemPrice) {
        this.itemName = itemName;
        this.quantity = quantity;
        this.itemPrice = itemPrice;
    }
    // getter和setter方法
    public String getItemName() {
        return itemName;
    }
    public void setItemName(String itemName) {
        this.itemName = itemName;
    }
    //省略其他set/get方法
    ......
    // 计算订单项金额
    public BigDecimal getItemAmount() {
        return itemPrice.multiply(BigDecimal.valueOf(quantity));
    }
}

确定领域服务

在订单管理系统中,我们可以定义一个OrderService领域服务,它包含订单的创建、确认、发货、收货、取消等业务逻辑。这些业务逻辑通常涉及多个实体对象和值对象,因此我们可以将它们封装在一个领域服务中。

public interface OrderService {
    void placeOrder(Order order);
}
public class OrderServiceImpl implements OrderService {
    private OrderRepository orderRepository;
    public OrderServiceImpl(OrderRepository orderRepository) {
        this.orderRepository = orderRepository;
    }
    @Override
    public void placeOrder(Order order) {
        order.placeOrder();
        orderRepository.save(order);
    }
}

确定聚合根

在订单管理系统中,我们可以将OrderAggregate实体对象作为聚合根,它是一个有唯一标识符的实体对象,它包含多个实体对象和值对象。我们可以通过聚合根来管理整个订单模块的状态和行为。

public class OrderAggregate {
    private Order order;
    private List<OrderItem> orderItems;
    public OrderAggregate(Order order, List<OrderItem> orderItems) {
        this.order = order;
        this.orderItems = orderItems;
    }
    public void addOrderItem(OrderItem orderItem) {
        orderItems.add(orderItem);
    }
    public void removeOrderItem(OrderItem orderItem) {
        orderItems.remove(orderItem);
    }
    public double calculateTotalPrice() {
        double totalPrice = 0;
        for (OrderItem orderItem : orderItems) {
            totalPrice += orderItem.getProduct().getPrice() * orderItem.getQuantity();
        }
        return totalPrice;
    }
    public void placeOrder() {
        if (order.getCustomer().canPlaceOrder()) {
            // do something
        } else {
            // do something else
        }
    }
}

确定仓储接口

在订单管理系统中,我们可以定义一个OrderRepository仓储接口,它包含对订单的增删改查等操作。我们可以通过仓储接口来实现对订单的持久化和查询。

public interface OrderRepository {
    void save(Order order);
}
public class OrderRepositoryImpl implements OrderRepository {
    @Override
    public void save(Order order) {
        // do something
    }
}

确定应用服务

在订单管理系统中,我们可以定义一个OrderApplicationService应用服务,它是一个面向用户的服务,它包含对订单的查询、创建、确认、发货、收货、取消等操作。我们可以通过应用服务来实现用户与订单模块的交互。

通过以上步骤,我们可以使用DDD来设计和实现订单模块,从而提高软件开发的效率和质量。最后项目整体结构如下:

充血模型和贫血模型

DDD中的充血模型是指将业务逻辑封装在实体对象中,实体对象不仅仅是数据的容器,还包含了业务逻辑的处理(比如Order)。这样可以避免业务逻辑散落在各个层次中,使得代码更加清晰和易于维护。充血模型和贫血模型是两种不同的设计模式,它们在处理业务逻辑时有着不同的优缺点。

  • 充血模型是指将业务逻辑封装在实体对象和领域服务中,使得实体对象具有更多的行为和状态。在充血模型中,实体对象不仅仅是数据的容器,它们还具有一些行为和状态,可以对外提供更加丰富的接口。充血模型的优点是可以将业务逻辑封装在实体对象和领域服务中,使得代码更加清晰和易于维护。缺点是实体对象的行为和状态可能会变得过于复杂,导致代码难以理解和维护。
  • 贫血模型是指将业务逻辑封装在服务层中,使得实体对象只是数据的容器,不具有任何行为和状态。在贫血模型中,实体对象只是一个简单的POJO对象,它们的行为和状态由服务层来管理。贫血模型的优点是实体对象的代码比较简单,易于理解和维护。缺点是服务层的代码可能会变得过于复杂,导致代码难以理解和维护。

充血模型和贫血模型都有各自的优缺点,具体使用哪种模型取决于具体的业务需求和开发团队的技术水平。在DDD中,通常使用充血模型来处理业务逻辑,因为充血模型可以更好地封装业务逻辑,使得代码更加清晰和易于维护。

总结

总结一下DDD方式的优缺点以及我们什么场景下采用DDD

优点:

  1. 更好地理解业务领域:DDD可以帮助我们更好地理解业务领域,将业务逻辑和技术实现分离,从而提高软件开发的效率和质量。
  2. 更好地管理复杂性:DDD可以帮助我们更好地管理复杂性,将复杂的业务逻辑分解成小的领域模型,从而降低系统的复杂度。
  3. 更好地支持变化:DDD可以帮助我们更好地支持变化,将变化隔离在领域模型内部,从而降低变化对系统的影响。

缺点:

  1. 学习成本高:DDD需要掌握一定的领域知识和技术实现,因此学习成本较高。
  2. 设计复杂度高:DDD需要进行领域建模和设计,因此设计复杂度较高。
  3. 实现难度大:DDD需要进行领域驱动的实现,因此实现难度较大。

适用场景:

  • 需要处理复杂业务逻辑的系统。
  • 需要支持变化的系统。
  • 需要更好地理解业务领域的系统。
  • 需要更好地管理复杂性的系统。
  • 需要更好地支持业务创新的系统。


相关文章
|
2月前
|
消息中间件 测试技术 领域建模
DDD - 一文读懂DDD领域驱动设计
DDD - 一文读懂DDD领域驱动设计
464 0
|
9月前
|
消息中间件 架构师 搜索推荐
DDD领域驱动设计的概念解析
DDD领域驱动设计的概念解析
161 0
|
11月前
|
消息中间件 JavaScript 小程序
领域驱动设计(DDD)的几种典型架构介绍
领域驱动设计(DDD)的几种典型架构介绍
|
设计模式 缓存 Java
DDD分层
为什么分层 引用《领域驱动设计模式、原理与实践》 为了避免将代码库变成大泥球(BBoM)并因此减弱领域模型的完整性且最终减弱可用性,系统架构要支持技术复杂性与领域复杂性的分离。引起技术实现发生变化的原因与引起领域逻辑发生变化的原因显然不同,这就导致基础设施和领域逻辑问题会以不同速率发生变化 每一层都有各自的职责,显然这也是符合SRP的
479 0
DDD分层
|
设计模式 缓存 Java
DDD之代码架构
这是一篇迟到的文章。这其实是我写DDD的第四篇文章。去年11月份左右我在个人网站上写了三篇关于DDD的文章,都是比较偏战略部分的。那个时候我还在一个正在使用DDD的项目上,也是我第一次真正开始深入使用DDD。
552 1
|
存储 设计模式 前端开发
浅析 DDD 领域驱动设计(1)
浅析 DDD 领域驱动设计
267 0
浅析 DDD 领域驱动设计(1)
|
缓存 数据可视化 Java
浅析 DDD 领域驱动设计(2)
浅析 DDD 领域驱动设计
227 0
浅析 DDD 领域驱动设计(2)
|
设计模式 SQL 测试技术
一文理解 DDD 领域驱动设计!
以一种领域专家、设计人员、开发人员都能理解的通用语言作为相互交流的工具,在交流的过程中发现领域概念,然
一文理解 DDD 领域驱动设计!
|
存储 缓存 监控
浅谈我对DDD领域驱动设计的理解
DDD的全称为Domain-driven Design,即领域驱动设计。下面我从领域、问题域、领域模型、设计、驱动这几个词语的含义和联系的角度去阐述DDD是如何融入到我们平时的软件开发初期阶段的。要理解什么是领域驱动设计,首先要理解什么是领域,什么是设计,还有驱动是什么意思,什么驱动什么。
|
前端开发 Java 数据库连接
DDD领域驱动设计实战(03)-深入理解实体
DDD领域驱动设计实战(03)-深入理解实体
349 0
DDD领域驱动设计实战(03)-深入理解实体