手撸 golang GO 与微服务 聚合模式之 2
缘起
最近浏览 [Go 微服务实战] (刘金亮, 2021.1)
本系列笔记拟采纳 golang 练习之
聚合模式
DDD 中有两个十分重要的模式:聚合(Aggregate)和聚合根(AggregateRoot)。聚合是对概念上属于同一实体(entity)或值对象(value object)的封装。而聚合根的含意是指,任何对该聚合的拜访都仅达到聚合根。比方 Car 就是聚合根,尽管 Car 有轮胎、车灯,然而显然内部拜访都只须要拜访 Car,聚合根确保了聚合的完整性。聚合的规定
1. 只有聚合根可被内部拜访
2. 聚合之间的分割通过主键编码而不是援用
3. 单个事务只能创立或更新一个聚合
摘自 [Go 微服务实战] 刘金亮 2021.1
指标 (Day 2)
- 设计合乎聚合准则的订单服务
- Day 1 的设计太仓促瞎搞了, 推倒重来
设计
- IOrder: 订单接口, 定义订单的数据及操作方法
- IOrderService: 订单服务接口, 定义创立 / 获取订单的办法
- OrderHeaderDTO: 订单低头数据, 纯值对象
- OrderItemDTO: 订单产品明细, 纯值对象
- iOrderResponsity: 订单存储库接口, 提供订单数据的 CRUD 以及本地事务管理
- tOrderHeaderEntity: 订单低头的实体类, 用于 ORM
- tOrderItemEntity: 订单明细的实体类, 用于 ORM
- tMockOrderResponsity: 虚构的订单存储库, 实现 iOrderResponsity 接口
- tOrderImplement: 订单畛域对象的实现, 治理具体的订单数据
- tOrderServiceImplement: 订单服务, 实现 IOrderService 接口
IOrder.go
订单接口, 定义订单的数据及操作方法
package order
type IOrder interface {GetHeader() *OrderHeaderDTO
SaveHeader(it *OrderHeaderDTO) error
GetItems() []*OrderItemDTO
AddItem(item *OrderItemDTO) error
DelItem(item *OrderItemDTO) error
}
IOrderService.go
订单服务接口, 定义创立 / 获取订单的办法
package order
type IOrderService interface {Create(header *OrderHeaderDTO, items []*OrderItemDTO) IOrder
Get(orderId int64) IOrder
}
OrderHeaderDTO.go
订单低头数据, 纯值对象
package order
type OrderHeaderDTO struct {
OrderID int64
ConsumerID int64
CreateTime int64
Status int
Timestamp int64
}
OrderItemDTO.go
订单产品明细, 纯值对象
package order
type OrderItemDTO struct {
ItemID int64
SkuID int64
Qty int
Price float64
Timestamp int64
}
iOrderResponsity.go
订单存储库接口, 提供订单数据的 CRUD 以及本地事务管理
package order
type iOrderResponsity interface {NewOrderID() int64
NewItemID() int64
LoadOrderHeader(orderID int64) (error, *tOrderHeaderEntity)
SaveOrderHeader(it *tOrderHeaderEntity) (error, *tOrderHeaderEntity)
LoadOrderItemsByOrderID(orderID int64) (error, []*tOrderItemEntity)
LoadOrderItem(itemID int64) (error, *tOrderItemEntity)
SaveOrderItem(it *tOrderItemEntity) (error, *tOrderItemEntity)
RemoveOrderItem(it *tOrderItemEntity) error
Transaction(func() error) error
}
tOrderHeaderEntity.go
订单低头的实体类, 用于 ORM
package order
type tOrderHeaderEntity struct {
OrderID int64
ConsumerID int64
CreateTime int64
Status int
Timestamp int64
}
func (me *tOrderHeaderEntity) Clone() *tOrderHeaderEntity {
return &tOrderHeaderEntity{me.OrderID, me.ConsumerID, me.CreateTime, me.Status, me.Timestamp,}
}
func (me *tOrderHeaderEntity) ToOrderHeader() *OrderHeaderDTO {
return &OrderHeaderDTO{me.OrderID, me.ConsumerID, me.CreateTime, me.Status, me.Timestamp,}
}
func (me *tOrderHeaderEntity) Read(it *OrderHeaderDTO) {
me.OrderID = it.OrderID
me.ConsumerID = it.ConsumerID
me.CreateTime = it.CreateTime
me.Status = it.Status
me.Timestamp = it.Timestamp
}
tOrderItemEntity.go
订单明细的实体类, 用于 ORM
package order
type tOrderItemEntity struct {
ItemID int64
OrderID int64
SkuID int64
Qty int
Price float64
Timestamp int64
}
func (me *tOrderItemEntity) Clone() *tOrderItemEntity {
return &tOrderItemEntity{me.ItemID, me.OrderID, me.SkuID, me.Qty, me.Price, me.Timestamp,}
}
func (me *tOrderItemEntity) ToOrderItemData() *OrderItemDTO {
return &OrderItemDTO{me.ItemID, me.SkuID, me.Qty, me.Price, me.Timestamp,}
}
func (me *tOrderItemEntity) Read(it *OrderItemDTO) {
me.ItemID = it.ItemID
me.SkuID = it.SkuID
me.Qty = it.Qty
me.Price = it.Price
me.Timestamp = it.Timestamp
}
tMockOrderResponsity.go
虚构的订单存储库, 实现 iOrderResponsity 接口
package order
import (
"errors"
"fmt"
"sync"
"sync/atomic"
"time"
)
type tMockOrderResponsity struct {
rwmutex *sync.RWMutex
orders map[int64]*tOrderHeaderEntity
items map[int64]*tOrderItemEntity
}
func newMockOrderResponsity() iOrderResponsity {it := new(tMockOrderResponsity)
it.init()
return it
}
func (me *tMockOrderResponsity) init() {me.rwmutex = new(sync.RWMutex)
me.orders = make(map[int64]*tOrderHeaderEntity)
me.items = make(map[int64]*tOrderItemEntity)
}
func (me *tMockOrderResponsity) LoadOrderHeader(orderID int64) (error, *tOrderHeaderEntity) {me.rwmutex.RLock()
defer me.rwmutex.RUnlock()
it, ok := me.orders[orderID]
if ok {return nil, it.Clone()
}
return gErrorNotFound, nil
}
func (me *tMockOrderResponsity) SaveOrderHeader(it *tOrderHeaderEntity) (error, *tOrderHeaderEntity) {me.rwmutex.Lock()
defer me.rwmutex.Unlock()
origin, ok := me.orders[it.OrderID]
if ok {
if origin.Status != it.Status || origin.Timestamp != it.Timestamp {return gErrorVersionChanged, nil}
}
it.Timestamp = time.Now().UnixNano()
me.orders[it.OrderID] = it.Clone()
return nil, it
}
func (me *tMockOrderResponsity) LoadOrderItem(itemID int64) (error, *tOrderItemEntity) {me.rwmutex.RLock()
defer me.rwmutex.RUnlock()
it, ok := me.items[itemID]
if ok {return nil, it.Clone()
}
return gErrorNotFound, nil
}
func (me *tMockOrderResponsity) SaveOrderItem(it *tOrderItemEntity) (error, *tOrderItemEntity) {me.rwmutex.Lock()
defer me.rwmutex.Unlock()
origin, ok := me.items[it.ItemID]
if ok {
if origin.Timestamp != it.Timestamp {return gErrorVersionChanged, nil}
}
it.Timestamp = time.Now().UnixNano()
me.items[it.ItemID] = it.Clone()
return nil, it
}
func (me *tMockOrderResponsity) RemoveOrderItem(it *tOrderItemEntity) error {me.rwmutex.Lock()
defer me.rwmutex.Unlock()
origin, ok := me.items[it.ItemID]
if ok {
if origin.Timestamp != it.Timestamp {return gErrorVersionChanged}
}
delete(me.items, it.ItemID)
return nil
}
func (me *tMockOrderResponsity) LoadOrderItemsByOrderID(orderID int64) (error, []*tOrderItemEntity) {me.rwmutex.Lock()
defer me.rwmutex.Unlock()
lst := []*tOrderItemEntity{}
for _,v := range me.items {
if v.OrderID == orderID {lst = append(lst, v)
}
}
return nil, lst
}
func (me *tMockOrderResponsity) NewOrderID() int64 {return atomic.AddInt64(&gOrderID, 1)
}
func (me *tMockOrderResponsity) NewItemID() int64 {return atomic.AddInt64(&gItemID, 1)
}
func (me *tMockOrderResponsity) Transaction(action func() error) error {fmt.Println("tMockOrderResponsity.Transaction begin")
e := action()
if e != nil {fmt.Printf("tMockOrderResponsity.Transaction rollback, e=%v\n", e)
} else {fmt.Println("tMockOrderResponsity.Transaction commit")
}
return e
}
var gErrorNotFound = errors.New("not found")
var gErrorVersionChanged = errors.New("version changed")
var MockOrderResponsity = newMockOrderResponsity()
var gOrderID = time.Now().UnixNano()
var gItemID = time.Now().UnixNano()
tOrderImplement.go
订单畛域对象的实现, 治理具体的订单数据
package order
type tOrderImplement struct {state *tOrderHeaderEntity}
func newOrderImplement(order *tOrderHeaderEntity) IOrder {it := new(tOrderImplement)
it.init(order)
return it
}
func (me *tOrderImplement) init(order *tOrderHeaderEntity) {me.state = order}
func (me *tOrderImplement) GetHeader() *OrderHeaderDTO {return me.state.ToOrderHeader()
}
func (me *tOrderImplement) SaveHeader(it *OrderHeaderDTO) error {entity := new(tOrderHeaderEntity)
entity.Read(it)
err, entity := MockOrderResponsity.SaveOrderHeader(entity)
if err != nil {return err}
me.state = entity
return nil
}
func (me *tOrderImplement) GetItems() []*OrderItemDTO {err, items := MockOrderResponsity.LoadOrderItemsByOrderID(me.state.OrderID)
if err != nil {return nil}
lst := make([]*OrderItemDTO, len(items))
for i,it := range items {lst[i] = it.ToOrderItemData()}
return lst
}
func (me *tOrderImplement) AddItem(item *OrderItemDTO) error {entity := &tOrderItemEntity{}
entity.Read(item)
entity.ItemID = MockOrderResponsity.NewItemID()
entity.OrderID = me.state.OrderID
return MockOrderResponsity.Transaction(func() error {
// lock header
err, header := MockOrderResponsity.SaveOrderHeader(me.state)
if err != nil {return err}
me.state = header
// save item
err, _ = MockOrderResponsity.SaveOrderItem(entity)
return err
})
}
func (me *tOrderImplement) DelItem(item *OrderItemDTO) error {entity := &tOrderItemEntity{}
entity.Read(item)
entity.OrderID = me.state.OrderID
return MockOrderResponsity.Transaction(func() error {
// lock header
err, header := MockOrderResponsity.SaveOrderHeader(me.state)
if err != nil {return err}
me.state = header
// del item
return MockOrderResponsity.RemoveOrderItem(entity)
})
}
tOrderServiceImplement.go
订单服务, 实现 IOrderService 接口
package order
type tOrderServiceImplement struct {
}
func newOrderServiceImplement() IOrderService {it := new(tOrderServiceImplement)
return it
}
func (me *tOrderServiceImplement) Create(header *OrderHeaderDTO, items []*OrderItemDTO) IOrder {ret := []IOrder{nil}
_ = MockOrderResponsity.Transaction(func() error {hd := new(tOrderHeaderEntity)
hd.Read(header)
hd.OrderID = MockOrderResponsity.NewOrderID()
e, he := MockOrderResponsity.SaveOrderHeader(hd)
if e != nil {return e}
for _,v := range items {item := new(tOrderItemEntity)
item.Read(v)
item.ItemID = MockOrderResponsity.NewItemID()
item.OrderID = he.OrderID
e, _ = MockOrderResponsity.SaveOrderItem(item)
if e != nil {return e}
}
ret[0] = newOrderImplement(he)
return nil
})
return ret[0]
}
func (me *tOrderServiceImplement) Get(orderId int64) IOrder {e, hd := MockOrderResponsity.LoadOrderHeader(orderId)
if e != nil {return nil}
return newOrderImplement(hd)
}
var OrderService = newOrderServiceImplement()
(end)