实体
引言
在领域驱动设计里,实体的设计可以说是通用语言的核心,也是最开始在模型划分中需要考虑的。怎么样设计实体和怎么样划分限界上下文同样重要。实体的概念就是要保证通用语言的完整性。领域驱动让设计实体的关注点从数据的属性和表的关联转化到了富有行为的领域概念上。
实体是具有可变性的,这是一个和值对象比较明显的区分,也即实体是可以持续得变化,持续得修改,并且具有唯一的标识。在设计实体的时候需要跳出CRUD的设计思维。把关注重点从数据模型设计转移到实体模型上。实体是能够表达什么概念,具有哪些行为,领域范围是哪些。实体的唯一标识是用来区分实体的,在实体的整个生命周期中这个唯一标识都是不变的。
设计实体
实体设计中,需要先确定实体的唯一标识。在Java的实体设计中,可以借助框架来实现唯一标识。这里先不讨论具体实现细节。设计唯一标识其实可以有多种方式。
- 用户输入唯一标识,程序再根据输入生成可识别的数值和符号,这种方式不方便修改生成规则,而且也会存在输入冲突。
- 应用程序生成,比如java自带的UUID生成器。apache的Commons的id生成组件。
- 持久化机制,DB的序列值Sequence或者自增主键。
- 其他上下文提供唯一标识,比如本地上下文也有一个本地的User,然后用全局的比如登录系统的用户id作为本地User实体的主键,这种需要谨慎使用,尽量保证系统的自治性。
- 委派标识。很多情况下,唯一标识都是和领域概念无关的,客户端也无需关注这个标识。这时候就可以用一些ORM工具来处理,写一个抽象父类,来专门做id生成。其他子类的实体只需要关注自己领域的模型和行为即刻。
具体实现可以用Hibernate或者Jpa这些工具。如下是一个用jpa生成自增主键的父类。
package com.lijingyao.bookrent.entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;
/**
* Created by lijingyao on 15/12/20 13:14.
*/
@MappedSuperclass
public abstract class LayerSuperType {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
protected Long getId() {
return id;
}
protected void setId(Long id) {
this.id = id;
}
}
实体就可以不用关注id生成,如下,是一个代表用户的实体。
package com.lijingyao.bookrent.entity;
import com.sun.istack.internal.NotNull;
import java.util.Calendar;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Index;
import javax.persistence.Table;
/**
* Created by lijingyao on 15/12/20 12:48.
*/
@Entity
@Table(
name = "br_user",
indexes = {
@Index(name = "IDX_USER_NAME", columnList = "name", unique = false)})
public class User extends LayerSuperType