乐趣区

关于golang:手撸golang-GO与微服务-聚合模式之2

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

退出移动版