手擼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)