缘起

最近阅读 [Go微服务实战] (刘金亮, 2021.1)
本系列笔记拟采用golang练习之

聚合模式

  1. DDD中有两个非常重要的模式:聚合(Aggregate)和聚合根(AggregateRoot)。
  2. 聚合是对概念上属于同一实体(entity)或值对象(value object)的封装。
  3. 而聚合根的含义是指,任何对该聚合的访问都仅到达聚合根。
  4. 比如Car就是聚合根,虽然Car有轮胎、车灯,
  5. 但是显然外部访问都只需要访问Car,聚合根确保了聚合的完整性。
  6. 聚合的规则
  7. 1. 只有聚合根可被外部访问
  8. 2. 聚合之间的联系通过主键编码而不是引用
  9. 3. 单个事务只能创建或更新一个聚合
  10. 摘自 [Go微服务实战] 刘金亮 2021.1

目标(Day 2)

  • 设计符合聚合原则的订单服务
  • Day 1的设计太仓促瞎搞了, 推倒重来

设计

  • IOrder: 订单接口, 定义订单的数据及操作方法
  • IOrderService: 订单服务接口, 定义创建/获取订单的方法
  • OrderHeaderDTO: 订单抬头数据, 纯值对象
  • OrderItemDTO: 订单产品明细, 纯值对象
  • iOrderRepository: 订单存储库接口, 提供订单数据的CRUD以及本地事务管理
  • tOrderHeaderEntity: 订单抬头的实体类, 用于ORM
  • tOrderItemEntity: 订单明细的实体类, 用于ORM
  • tMockOrderRepository: 虚拟的订单存储库, 实现iOrderRepository接口
  • tOrderImplement: 订单领域对象的实现, 管理具体的订单数据
  • tOrderServiceImplement: 订单服务, 实现IOrderService接口

IOrder.go

订单接口, 定义订单的数据及操作方法

  1. package order
  2. type IOrder interface {
  3. GetHeader() *OrderHeaderDTO
  4. SaveHeader(it *OrderHeaderDTO) error
  5. GetItems() []*OrderItemDTO
  6. AddItem(item *OrderItemDTO) error
  7. DelItem(item *OrderItemDTO) error
  8. }

IOrderService.go

订单服务接口, 定义创建/获取订单的方法

  1. package order
  2. type IOrderService interface {
  3. Create(header *OrderHeaderDTO, items []*OrderItemDTO) IOrder
  4. Get(orderId int64) IOrder
  5. }

OrderHeaderDTO.go

订单抬头数据, 纯值对象

  1. package order
  2. type OrderHeaderDTO struct {
  3. OrderID int64
  4. ConsumerID int64
  5. CreateTime int64
  6. Status int
  7. Timestamp int64
  8. }

OrderItemDTO.go

订单产品明细, 纯值对象

  1. package order
  2. type OrderItemDTO struct {
  3. ItemID int64
  4. SkuID int64
  5. Qty int
  6. Price float64
  7. Timestamp int64
  8. }

iOrderRepository.go

订单存储库接口, 提供订单数据的CRUD以及本地事务管理

  1. package order
  2. type iOrderRepository interface {
  3. NewOrderID() int64
  4. NewItemID() int64
  5. LoadOrderHeader(orderID int64) (error, *tOrderHeaderEntity)
  6. SaveOrderHeader(it *tOrderHeaderEntity) (error, *tOrderHeaderEntity)
  7. LoadOrderItemsByOrderID(orderID int64) (error, []*tOrderItemEntity)
  8. LoadOrderItem(itemID int64) (error, *tOrderItemEntity)
  9. SaveOrderItem(it *tOrderItemEntity) (error, *tOrderItemEntity)
  10. RemoveOrderItem(it *tOrderItemEntity) error
  11. Transaction(func() error) error
  12. }

tOrderHeaderEntity.go

订单抬头的实体类, 用于ORM

  1. package order
  2. type tOrderHeaderEntity struct {
  3. OrderID int64
  4. ConsumerID int64
  5. CreateTime int64
  6. Status int
  7. Timestamp int64
  8. }
  9. func (me *tOrderHeaderEntity) Clone() *tOrderHeaderEntity {
  10. return &tOrderHeaderEntity{
  11. me.OrderID, me.ConsumerID, me.CreateTime, me.Status, me.Timestamp,
  12. }
  13. }
  14. func (me *tOrderHeaderEntity) ToOrderHeader() *OrderHeaderDTO {
  15. return &OrderHeaderDTO{
  16. me.OrderID, me.ConsumerID, me.CreateTime, me.Status, me.Timestamp,
  17. }
  18. }
  19. func (me *tOrderHeaderEntity) Read(it *OrderHeaderDTO) {
  20. me.OrderID = it.OrderID
  21. me.ConsumerID = it.ConsumerID
  22. me.CreateTime = it.CreateTime
  23. me.Status = it.Status
  24. me.Timestamp = it.Timestamp
  25. }

tOrderItemEntity.go

订单明细的实体类, 用于ORM

  1. package order
  2. type tOrderItemEntity struct {
  3. ItemID int64
  4. OrderID int64
  5. SkuID int64
  6. Qty int
  7. Price float64
  8. Timestamp int64
  9. }
  10. func (me *tOrderItemEntity) Clone() *tOrderItemEntity {
  11. return &tOrderItemEntity{
  12. me.ItemID, me.OrderID, me.SkuID, me.Qty, me.Price, me.Timestamp,
  13. }
  14. }
  15. func (me *tOrderItemEntity) ToOrderItemData() *OrderItemDTO {
  16. return &OrderItemDTO{
  17. me.ItemID, me.SkuID, me.Qty, me.Price, me.Timestamp,
  18. }
  19. }
  20. func (me *tOrderItemEntity) Read(it *OrderItemDTO) {
  21. me.ItemID = it.ItemID
  22. me.SkuID = it.SkuID
  23. me.Qty = it.Qty
  24. me.Price = it.Price
  25. me.Timestamp = it.Timestamp
  26. }

tMockOrderRepository.go

虚拟的订单存储库, 实现iOrderResponsity接口

  1. package order
  2. import (
  3. "errors"
  4. "fmt"
  5. "sync"
  6. "sync/atomic"
  7. "time"
  8. )
  9. type tMockOrderRepository struct {
  10. rwmutex *sync.RWMutex
  11. orders map[int64]*tOrderHeaderEntity
  12. items map[int64]*tOrderItemEntity
  13. }
  14. func newMockOrderRepository() iOrderRepository {
  15. it := new(tMockOrderRepository)
  16. it.init()
  17. return it
  18. }
  19. func (me *tMockOrderRepository) init() {
  20. me.rwmutex = new(sync.RWMutex)
  21. me.orders = make(map[int64]*tOrderHeaderEntity)
  22. me.items = make(map[int64]*tOrderItemEntity)
  23. }
  24. func (me *tMockOrderRepository) LoadOrderHeader(orderID int64) (error, *tOrderHeaderEntity) {
  25. me.rwmutex.RLock()
  26. defer me.rwmutex.RUnlock()
  27. it, ok := me.orders[orderID]
  28. if ok {
  29. return nil, it.Clone()
  30. }
  31. return gErrorNotFound, nil
  32. }
  33. func (me *tMockOrderRepository) SaveOrderHeader(it *tOrderHeaderEntity) (error, *tOrderHeaderEntity) {
  34. me.rwmutex.Lock()
  35. defer me.rwmutex.Unlock()
  36. origin, ok := me.orders[it.OrderID]
  37. if ok {
  38. if origin.Status != it.Status || origin.Timestamp != it.Timestamp {
  39. return gErrorVersionChanged, nil
  40. }
  41. }
  42. it.Timestamp = time.Now().UnixNano()
  43. me.orders[it.OrderID] = it.Clone()
  44. return nil, it
  45. }
  46. func (me *tMockOrderRepository) LoadOrderItem(itemID int64) (error, *tOrderItemEntity) {
  47. me.rwmutex.RLock()
  48. defer me.rwmutex.RUnlock()
  49. it, ok := me.items[itemID]
  50. if ok {
  51. return nil, it.Clone()
  52. }
  53. return gErrorNotFound, nil
  54. }
  55. func (me *tMockOrderRepository) SaveOrderItem(it *tOrderItemEntity) (error, *tOrderItemEntity) {
  56. me.rwmutex.Lock()
  57. defer me.rwmutex.Unlock()
  58. origin, ok := me.items[it.ItemID]
  59. if ok {
  60. if origin.Timestamp != it.Timestamp {
  61. return gErrorVersionChanged, nil
  62. }
  63. }
  64. it.Timestamp = time.Now().UnixNano()
  65. me.items[it.ItemID] = it.Clone()
  66. return nil, it
  67. }
  68. func (me *tMockOrderRepository) RemoveOrderItem(it *tOrderItemEntity) error {
  69. me.rwmutex.Lock()
  70. defer me.rwmutex.Unlock()
  71. origin, ok := me.items[it.ItemID]
  72. if ok {
  73. if origin.Timestamp != it.Timestamp {
  74. return gErrorVersionChanged
  75. }
  76. }
  77. delete(me.items, it.ItemID)
  78. return nil
  79. }
  80. func (me *tMockOrderRepository) LoadOrderItemsByOrderID(orderID int64) (error, []*tOrderItemEntity) {
  81. me.rwmutex.Lock()
  82. defer me.rwmutex.Unlock()
  83. lst := []*tOrderItemEntity{}
  84. for _,v := range me.items {
  85. if v.OrderID == orderID {
  86. lst = append(lst, v)
  87. }
  88. }
  89. return nil, lst
  90. }
  91. func (me *tMockOrderRepository) NewOrderID() int64 {
  92. return atomic.AddInt64(&gOrderID, 1)
  93. }
  94. func (me *tMockOrderRepository) NewItemID() int64 {
  95. return atomic.AddInt64(&gItemID, 1)
  96. }
  97. func (me *tMockOrderRepository) Transaction(action func() error) error {
  98. fmt.Println("tMockOrderRepository.Transaction begin")
  99. e := action()
  100. if e != nil {
  101. fmt.Printf("tMockOrderRepository.Transaction rollback, e=%v\n", e)
  102. } else {
  103. fmt.Println("tMockOrderRepository.Transaction commit")
  104. }
  105. return e
  106. }
  107. var gErrorNotFound = errors.New("not found")
  108. var gErrorVersionChanged = errors.New("version changed")
  109. var MockOrderRepository = newMockOrderRepository()
  110. var gOrderID = time.Now().UnixNano()
  111. var gItemID = time.Now().UnixNano()

tOrderImplement.go

订单领域对象的实现, 管理具体的订单数据

  1. package order
  2. type tOrderImplement struct {
  3. state *tOrderHeaderEntity
  4. }
  5. func newOrderImplement(order *tOrderHeaderEntity) IOrder {
  6. it := new(tOrderImplement)
  7. it.init(order)
  8. return it
  9. }
  10. func (me *tOrderImplement) init(order *tOrderHeaderEntity) {
  11. me.state = order
  12. }
  13. func (me *tOrderImplement) GetHeader() *OrderHeaderDTO {
  14. return me.state.ToOrderHeader()
  15. }
  16. func (me *tOrderImplement) SaveHeader(it *OrderHeaderDTO) error {
  17. entity := new(tOrderHeaderEntity)
  18. entity.Read(it)
  19. err, entity := MockOrderRepository.SaveOrderHeader(entity)
  20. if err != nil {
  21. return err
  22. }
  23. me.state = entity
  24. return nil
  25. }
  26. func (me *tOrderImplement) GetItems() []*OrderItemDTO {
  27. err, items := MockOrderRepository.LoadOrderItemsByOrderID(me.state.OrderID)
  28. if err != nil {
  29. return nil
  30. }
  31. lst := make([]*OrderItemDTO, len(items))
  32. for i,it := range items {
  33. lst[i] = it.ToOrderItemData()
  34. }
  35. return lst
  36. }
  37. func (me *tOrderImplement) AddItem(item *OrderItemDTO) error {
  38. entity := &tOrderItemEntity{}
  39. entity.Read(item)
  40. entity.ItemID = MockOrderRepository.NewItemID()
  41. entity.OrderID = me.state.OrderID
  42. return MockOrderRepository.Transaction(func() error {
  43. // lock header
  44. err, header := MockOrderRepository.SaveOrderHeader(me.state)
  45. if err != nil {
  46. return err
  47. }
  48. me.state = header
  49. // save item
  50. err, _ = MockOrderRepository.SaveOrderItem(entity)
  51. return err
  52. })
  53. }
  54. func (me *tOrderImplement) DelItem(item *OrderItemDTO) error {
  55. entity := &tOrderItemEntity{}
  56. entity.Read(item)
  57. entity.OrderID = me.state.OrderID
  58. return MockOrderRepository.Transaction(func() error {
  59. // lock header
  60. err, header := MockOrderRepository.SaveOrderHeader(me.state)
  61. if err != nil {
  62. return err
  63. }
  64. me.state = header
  65. // del item
  66. return MockOrderRepository.RemoveOrderItem(entity)
  67. })
  68. }

tOrderServiceImplement.go

订单服务, 实现IOrderService接口

  1. package order
  2. type tOrderServiceImplement struct {
  3. }
  4. func newOrderServiceImplement() IOrderService {
  5. it := new(tOrderServiceImplement)
  6. return it
  7. }
  8. func (me *tOrderServiceImplement) Create(header *OrderHeaderDTO, items []*OrderItemDTO) IOrder {
  9. ret := []IOrder{ nil }
  10. _ = MockOrderRepository.Transaction(func() error {
  11. hd := new(tOrderHeaderEntity)
  12. hd.Read(header)
  13. hd.OrderID = MockOrderRepository.NewOrderID()
  14. e, he := MockOrderRepository.SaveOrderHeader(hd)
  15. if e != nil {
  16. return e
  17. }
  18. for _,v := range items {
  19. item := new(tOrderItemEntity)
  20. item.Read(v)
  21. item.ItemID = MockOrderRepository.NewItemID()
  22. item.OrderID = he.OrderID
  23. e, _ = MockOrderRepository.SaveOrderItem(item)
  24. if e != nil {
  25. return e
  26. }
  27. }
  28. ret[0] = newOrderImplement(he)
  29. return nil
  30. })
  31. return ret[0]
  32. }
  33. func (me *tOrderServiceImplement) Get(orderId int64) IOrder {
  34. e, hd := MockOrderRepository.LoadOrderHeader(orderId)
  35. if e != nil {
  36. return nil
  37. }
  38. return newOrderImplement(hd)
  39. }
  40. var OrderService = newOrderServiceImplement()

(end)