前言: 实体具有业务属性、业务逻辑和业务行为,是是实实在在的业务对象。在事件风暴中,我们可以根据命令、操作与事件将业务上紧密结合在一起的多个实体与值对象进行聚合形成聚合根。

实体是什么

虽然数据库的设计占据了主导地位(这个是没错的),但开发者也不应该只关注数据,而且要关注模型。

数据+行为= 模型,实体就是含有领域概念的模型。它是一个唯一的东西,在相当长的时间里数据状态在持续地变化,并且一定有唯一键,这区别于值对象。

注意的是如果非要用表结构里的一条含有主键的数据去理解实体也是可以的,但不少情况下可能是有多个表或者k/v数据来组成的一个实体。

实体、值对象与数据模型示例

实体、值对象与数据模型示例

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

为什么使用实体

CRUDok

实体的数据与行为

唯一标识

usernameusername

实践

https://github.com/8treenet/freedom/tree/master/example/fshop/domain/entity

entityfreedom.EntityWorkerentity Identity() stringPODTOPOGet/Set

发布领域事件

type Entity interface {    //发布领域事件  DomainEvent(string,interface{},...map[string]string)    //唯一ID  Identity() string    //获取请求运行时对象  GetWorker() Worker  SetProducer(string)  Marshal() []byte}

商品的属性和行为

商品实体

package entityimport (  "errors"  "strconv"  "github.com/8treenet/freedom"  "github.com/8treenet/freedom/example/fshop/domain/po")const (  //热销  GoodsHotTag = "HOT"  //新品  GoodsNewTag  = "NEW"  GoodsNoneTag = "NONE")// 商品实体type Goods struct {  freedom.Entity //继承实体基类接口  po.Goods //继承持久化的商品模型,包含了商品的列和属性方法}// Identity 唯一func (g *Goods) Identity() string {  return strconv.Itoa(g.Id)}// CutStock 扣库存func (g *Goods) CutStock(num int) error {  if num > g.Stock {    return errors.New("库存不足")  }  g.AddStock(-num) //po对象的方法,增加库存  return nil}// MarkedTag 为商品打tagfunc (g *Goods) MarkedTag(tag string) error {  if tag != GoodsHotTag && tag != GoodsNewTag && tag != GoodsNoneTag {    return errors.New("Tag doesn't exist")  }  g.SetTag(tag) //po对象的方法,设置tag  return nil}
用户实体
package entityimport (  "errors"  "strconv"  "github.com/8treenet/freedom"  "github.com/8treenet/freedom/example/fshop/domain/po")// 用户实体type User struct {  freedom.Entity //继承实体基类接口  po.User  //继承持久化的用户模型,包含了用户的列和属性方法}// Identity 唯一func (u *User) Identity() string {  return strconv.Itoa(u.Id)}// ChangePassword 修改密码func (u *User) ChangePassword(newPassword, oldPassword string) error {       //判断旧密码是否正确  if u.Password != oldPassword {    return errors.New("Password error")  }  u.SetPassword(newPassword) //po对象的方法,可以设置密码  returnnil}

订单实体

package entityimport (  "github.com/8treenet/freedom"  "github.com/8treenet/freedom/example/fshop/domain/po")const (  OrderStatusPAID       = "PAID" //付款  OrderStatusNonPayment = "NON_PAYMENT" //未付款  OrderStatusShipment   = "SHIPMENT" //发货)// 订单实体type Order struct {  freedom.Entity //继承实体基类接口  po.Order  //继承持久化的订单模型,包含了订单的列和属性方法  Details []*po.OrderDetail  //定义订单商品详情成员变量,一个订单包含多个商品}// Identity 唯一func (o *Order) Identity() string {  return o.OrderNo}// AddOrderDetal 增加订单详情func (o *Order) AddOrderDetal(detal *po.OrderDetail) {  o.Details = append(o.Details, detal) //增加订单详情,repository会做持久化处理}// Pay 付款func (o *Order) Pay() {  o.SetStatus(OrderStatusPAID) //po对象的方法,设置状态}// Shipment 发货func (o *Order) Shipment() {  o.SetStatus(OrderStatusShipment) //po对象的方法,设置状态}// IsPay 是否支付func (o *Order) IsPay() bool {        //判断是否付款  if o.Status != OrderStatusPAID {    return false  }  return true}

目录

  • golang领域模型-开篇
  • golang领域模型-六边形架构
  • golang领域模型-实体
  • golang领域模型-资源库
  • golang领域模型-依赖倒置
  • golang领域模型-聚合根
  • golang领域模型-CQRS
  • golang领域模型-领域事件

项目代码 https://github.com/8treenet/freedom/tree/master/example/fshop