共计 1956 个字符,预计需要花费 5 分钟才能阅读完成。
实体是什么
在领域驱动设计(Domain Driven Design)中,实体(Entity)是指具有唯一标识的领域对象,其生命周期可能会在系统中发生变化。实体通常是领域模型中最重要的元素之一。
实体具有以下特点:
-
唯一标识:实体通过唯一标识来区分不同的对象。这个标识可以是一个唯一的ID或者是几个属性值的组合来确定对象的唯一性。
-
状态和数据:实体包含了一组属性和数据,代表了对象的状态。这些属性可以是可变的,可以根据业务规则和领域逻辑进行更新和修改。
-
方法和行为:实体可以定义一些方法和行为,这些方法一般用于操作实体的状态、验证状态的变化或者处理领域逻辑。
实体具有业务属性、业务逻辑和业务行为,是是实实在在的业务对象。
应用服务都是围绕着数据库来实现的,但是在领域驱动设计里,开发者不应该仅仅只关心数据,而要关注模型。
数据+行为=模型。实体就是领域服务里的模型。
它跟数据库模型的区别在于,它不仅仅有数据,还有业务行为。跟值对象的区别在于,每个实体对象都是唯一的,有唯一键。一个实体可以对应一个带有唯一键的数据库表数据,还有一些场景也可以对应多个表,或者是一堆键值对数据组成的。
实体是可变的,每个实体都有自己的唯一性,我们用id来进行区分。值对象是不变的,是共性,实体都有相同的值对象,例如上述的用户实体都有国家等信息。我们以此区分好实体与值对象。
为什么要使用实体
开发者在设计系统时,最简单的就是不考虑建模只设计数据库模型和CURD操作,这其实也是一种建模方式,但是这种方式仅能应对简单的模型。但是随着业务的发展,这样的操作当更复杂的业务和更复杂的模型出现后是驾驭不了的。
-
唯一标识与身份:实体通过一个唯一的标识符来识别自己,这个标识符可以是一个字符串、数字、UUID等。这个标识符通常在实体创建时被赋予,并且在实体的整个生命周期中保持不变。比如用户实体的标识可以id,也可以是name(如果name是唯一的话)
-
保持状态和数据:实体可以通过它们的属性来记录和保持自己的状态和数据。这些数据可以是实体的核心属性,也可以是与实体相关的其他属性。
-
封装行为和领域逻辑:实体不仅仅是数据的容器,它们还应该具备行为和领域逻辑。通过在实体中定义方法,可以集中实现与实体自身相关的业务逻辑和行为。比如用户实体的修改密码,修改用户名等行为。
-
领域对象的标识性角色:实体可以作为领域模型中的重要对象,起到标识性的角色。在DDD中,实体在领域模型中承担着核心的业务概念,并与其他实体、值对象等一起构建领域模型。
通过使用实体,可以将业务规则和逻辑聚集到一个具体的对象中,并且将数据和行为封装在该对象中。实体能够提供更好的可维护性、可测试性和可扩展性,使领域模型更加贴合实际业务需求。
基于DT-Go的实践
-
所有的
entity
都必须继承dt.Entity
接口, 这里为实体注入了领域事件和运行时的Worker
对象。 -
所有的
entity
都必须重写Identity() string
方法。 -
实体可以选择的继承
PO
或者DTO
// dt.Entity
type Entity interface {
//唯一ID
Identity() string
//获取请求运行时对象
GetWorker() Worker
//发布/获取领域事件
AddPubEvent()
GetPubEvent()
RemovePubEvent()
AddSubEvent()
GetSubEvent()
Marshal() []byte
}
用户实体
package entity
import (
...
)
//User 用户实体
type User struct {
dt.Entity
po.User
}
// Identity 唯一
func (u *User) Identity() string {
return strconv.Itoa(u.ID)
}
// ChangePassword 修改密码
func (u *User) ChangePassword(newPassword, oldPassword string) error {
// 更新密码
u.SetPassword(newPassword)
//用户实体加入修改密码事件
u.AddPubEvent(&event.ChangePassword{
UserID: u.User.ID,
NewPassword: u.Password,
OldPassword: oldPassword,
})
return nil
}
// ChangeName 修改用户名
func (u *User) ChangeName(name string) error {
// 更新密码
u.SetName(name)
//用户实体加入重命名事件
u.AddPubEvent(&event.ChangeUserName{
UserID: u.User.ID,
UserName: name,
})
return nil
}