手撸golang GO与微服务 聚合模式之2golang
最近阅读 [Go微服务实战] (刘金亮, 2021.1)
本系列笔记拟采用golang练习之app
DDD中有两个很是重要的模式:聚合(Aggregate)和聚合根(AggregateRoot)。 聚合是对概念上属于同一实体(entity)或值对象(value object)的封装。 而聚合根的含义是指,任何对该聚合的访问都仅到达聚合根。 好比Car就是聚合根,虽然Car有轮胎、车灯, 可是显然外部访问都只须要访问Car,聚合根确保了聚合的完整性。 聚合的规则 1. 只有聚合根可被外部访问 2. 聚合之间的联系经过主键编码而不是引用 3. 单个事务只能建立或更新一个聚合 摘自 [Go微服务实战] 刘金亮 2021.1
订单接口, 定义订单的数据及操做方法微服务
package order type IOrder interface { GetHeader() *OrderHeaderDTO SaveHeader(it *OrderHeaderDTO) error GetItems() []*OrderItemDTO AddItem(item *OrderItemDTO) error DelItem(item *OrderItemDTO) error }
订单服务接口, 定义建立/获取订单的方法编码
package order type IOrderService interface { Create(header *OrderHeaderDTO, items []*OrderItemDTO) IOrder Get(orderId int64) IOrder }
订单抬头数据, 纯值对象atom
package order type OrderHeaderDTO struct { OrderID int64 ConsumerID int64 CreateTime int64 Status int Timestamp int64 }
订单产品明细, 纯值对象设计
package order type OrderItemDTO struct { ItemID int64 SkuID int64 Qty int Price float64 Timestamp int64 }
订单存储库接口, 提供订单数据的CRUD以及本地事务管理code
package order type iOrderRepository 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 }
订单抬头的实体类, 用于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 }
订单明细的实体类, 用于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 }
虚拟的订单存储库, 实现iOrderResponsity接口事务
package order import ( "errors" "fmt" "sync" "sync/atomic" "time" ) type tMockOrderRepository struct { rwmutex *sync.RWMutex orders map[int64]*tOrderHeaderEntity items map[int64]*tOrderItemEntity } func newMockOrderRepository() iOrderRepository { it := new(tMockOrderRepository) it.init() return it } func (me *tMockOrderRepository) init() { me.rwmutex = new(sync.RWMutex) me.orders = make(map[int64]*tOrderHeaderEntity) me.items = make(map[int64]*tOrderItemEntity) } func (me *tMockOrderRepository) 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 *tMockOrderRepository) 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 *tMockOrderRepository) 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 *tMockOrderRepository) 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 *tMockOrderRepository) 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 *tMockOrderRepository) 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 *tMockOrderRepository) NewOrderID() int64 { return atomic.AddInt64(&gOrderID, 1) } func (me *tMockOrderRepository) NewItemID() int64 { return atomic.AddInt64(&gItemID, 1) } func (me *tMockOrderRepository) Transaction(action func() error) error { fmt.Println("tMockOrderRepository.Transaction begin") e := action() if e != nil { fmt.Printf("tMockOrderRepository.Transaction rollback, e=%v\n", e) } else { fmt.Println("tMockOrderRepository.Transaction commit") } return e } var gErrorNotFound = errors.New("not found") var gErrorVersionChanged = errors.New("version changed") var MockOrderRepository = newMockOrderRepository() var gOrderID = time.Now().UnixNano() var gItemID = time.Now().UnixNano()
订单领域对象的实现, 管理具体的订单数据
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 := MockOrderRepository.SaveOrderHeader(entity) if err != nil { return err } me.state = entity return nil } func (me *tOrderImplement) GetItems() []*OrderItemDTO { err, items := MockOrderRepository.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 = MockOrderRepository.NewItemID() entity.OrderID = me.state.OrderID return MockOrderRepository.Transaction(func() error { // lock header err, header := MockOrderRepository.SaveOrderHeader(me.state) if err != nil { return err } me.state = header // save item err, _ = MockOrderRepository.SaveOrderItem(entity) return err }) } func (me *tOrderImplement) DelItem(item *OrderItemDTO) error { entity := &tOrderItemEntity{} entity.Read(item) entity.OrderID = me.state.OrderID return MockOrderRepository.Transaction(func() error { // lock header err, header := MockOrderRepository.SaveOrderHeader(me.state) if err != nil { return err } me.state = header // del item return MockOrderRepository.RemoveOrderItem(entity) }) }
订单服务, 实现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 } _ = MockOrderRepository.Transaction(func() error { hd := new(tOrderHeaderEntity) hd.Read(header) hd.OrderID = MockOrderRepository.NewOrderID() e, he := MockOrderRepository.SaveOrderHeader(hd) if e != nil { return e } for _,v := range items { item := new(tOrderItemEntity) item.Read(v) item.ItemID = MockOrderRepository.NewItemID() item.OrderID = he.OrderID e, _ = MockOrderRepository.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 := MockOrderRepository.LoadOrderHeader(orderId) if e != nil { return nil } return newOrderImplement(hd) } var OrderService = newOrderServiceImplement()
(end)