领域模型-实体 | DDD

共计 1956 个字符,预计需要花费 5 分钟才能阅读完成。

实体是什么

在领域驱动设计(Domain Driven Design)中,实体(Entity)是指具有唯一标识的领域对象,其生命周期可能会在系统中发生变化。实体通常是领域模型中最重要的元素之一。

实体具有以下特点:

  1. 唯一标识:实体通过唯一标识来区分不同的对象。这个标识可以是一个唯一的ID或者是几个属性值的组合来确定对象的唯一性。

  2. 状态和数据:实体包含了一组属性和数据,代表了对象的状态。这些属性可以是可变的,可以根据业务规则和领域逻辑进行更新和修改。

  3. 方法和行为:实体可以定义一些方法和行为,这些方法一般用于操作实体的状态、验证状态的变化或者处理领域逻辑。

实体具有业务属性、业务逻辑和业务行为,是是实实在在的业务对象。

应用服务都是围绕着数据库来实现的,但是在领域驱动设计里,开发者不应该仅仅只关心数据,而要关注模型。

数据+行为=模型。实体就是领域服务里的模型。

它跟数据库模型的区别在于,它不仅仅有数据,还有业务行为。跟值对象的区别在于,每个实体对象都是唯一的,有唯一键。一个实体可以对应一个带有唯一键的数据库表数据,还有一些场景也可以对应多个表,或者是一堆键值对数据组成的。

领域模型-实体 | DDD

实体是可变的,每个实体都有自己的唯一性,我们用id来进行区分。值对象是不变的,是共性,实体都有相同的值对象,例如上述的用户实体都有国家等信息。我们以此区分好实体与值对象。

为什么要使用实体

开发者在设计系统时,最简单的就是不考虑建模只设计数据库模型和CURD操作,这其实也是一种建模方式,但是这种方式仅能应对简单的模型。但是随着业务的发展,这样的操作当更复杂的业务和更复杂的模型出现后是驾驭不了的。

领域模型-实体 | DDD

  1. 唯一标识与身份:实体通过一个唯一的标识符来识别自己,这个标识符可以是一个字符串、数字、UUID等。这个标识符通常在实体创建时被赋予,并且在实体的整个生命周期中保持不变。比如用户实体的标识可以id,也可以是name(如果name是唯一的话)

  2. 保持状态和数据:实体可以通过它们的属性来记录和保持自己的状态和数据。这些数据可以是实体的核心属性,也可以是与实体相关的其他属性。

  3. 封装行为和领域逻辑:实体不仅仅是数据的容器,它们还应该具备行为和领域逻辑。通过在实体中定义方法,可以集中实现与实体自身相关的业务逻辑和行为。比如用户实体的修改密码,修改用户名等行为。

  4. 领域对象的标识性角色:实体可以作为领域模型中的重要对象,起到标识性的角色。在DDD中,实体在领域模型中承担着核心的业务概念,并与其他实体、值对象等一起构建领域模型。

通过使用实体,可以将业务规则和逻辑聚集到一个具体的对象中,并且将数据和行为封装在该对象中。实体能够提供更好的可维护性、可测试性和可扩展性,使领域模型更加贴合实际业务需求。

基于DT-Go的实践

  1. 所有的entity都必须继承dt.Entity 接口, 这里为实体注入了领域事件和运行时的Worker对象。

  2. 所有的entity都必须重写 Identity() string 方法。

  3. 实体可以选择的继承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
}
正文完
 
Dustin
版权声明:本站原创文章,由 Dustin 2022-08-01发表,共计1956字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。