将业务密钥建模为属性与嵌入式对象

问题描述

我正在遵循有关域引导设计的Spring Boot本书,作者在其中建模具有大量属性的Cargo实体。有问题的两个属性是数字键和业务键(bookingId)。货运实体如下所示:

**Cargo.java**


package whatever;

import lombok.Getter;
import lombok.Setter;

import javax.persistence.*;

@Entity
@Getter @Setter
public class Cargo {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Embedded
    private BookingId bookingId; // Business Identifier
}

**BookingId.java**

package whatever;

import lombok.Getter;
import lombok.Setter;

import javax.persistence.Column;
import javax.persistence.Embeddable;

@Embeddable
@Getter @Setter
public class BookingId {
    @Column(name="booking_id")
    private String bookingId;
}

如您所见,作者将业务密钥建模为嵌入式对象。将属性嵌入另一个对象中有什么好处?为什么我们在Cargo类中不只拥有一个名为bookingId的字符串变量,并对其施加唯一性约束呢?

解决方法

为什么我们在Cargo类中不只是拥有一个名为bookingId的字符串变量,并对其施加唯一性约束?

两个主要原因:

使用特定类型而不是像String这样的通用原语,可使编译器将预订标识符与其他类型的字符串区分开,从而减少了您无意中传递了预订标识符的更改,其中,位置标识符是

此外,定义一个类(如BookingId)可以使“需要知道底层数据结构的代码”与“不需要的代码”之间进行逻辑分离。换句话说,您将受益于拥有预订标识符的凝聚力数据结构可以一处查询。

(对于模型中的“标识符”而言,其中的第二个往往不如其他值类型重要。在许多情况下,标识符在语义上是不透明的-关于标识符,您唯一能做的就是将它们与一个另一个。)

使用显式类型对信息进行建模还可以使您减少操作空间,消除没有任何意义的方法。例如,采用预订ID的子字符串可能无法实现任何有用的功能,因此您可以通过将api简化为域模型中有意义的语义来简化api。

您会在集合中看到类似的问题;列表和地图倾向于具有广泛的接口,因此它们支持许多可能的应用程序,但是运输领域与列表和地图无关,而与列表和地图有关。标准库恰好具有方便使用的数据结构这一事实是实现的偶然事件,而不是您要建模的业务的一部分。

有关系吗?最终,机器真的不在乎代码设计-但是,下一个进行更改的人员将受到设计的很大影响。常见DDD模式的部分动机是,对代码进行了设计,以便程序员(您可以专注于手头的任务)(另请参见将“域代码”与“持久性代码”分开,以及生命周期模式,请参阅Evans 2003年的第6章。