当前位置: 首页 > news >正文

一个关于事件溯源Event Sourcing的小荔枝,Golang实现

最后更新于2023年3月1日 10:23:13

参考的这个文章:https://martinfowler.com/eaaDev/EventSourcing.html
用C sharp实现的,我改写成Golang了

最简单的例子

func main() {eProc := NewEventProcessor()//refact := Cargo{Name: "Refactoring"}kr := &Ship{Name: "King Roy"}sfo := &Port{Name: "San Francisco", Country: "US"}//la := Port{Name: "Los Angeles", Country: "US"}//yyv := Port{Name: "Vancouver", Country: "Canada"}// Test 1ev := ArrivalEvent{DomainEvent: DomainEvent{Recorded: time.Now(),Occurred: time.Date(2005, time.November, 1, 0, 0, 0, 0, time.Local),},Port: sfo,Ship: kr,}eProc.Process(&ev)jiayulog.Logger.DebugP(sfo)jiayulog.Logger.DebugP(kr.Port)pretty.Log(eProc)
}

这里面的jiayulog是我自己搞了一个log,pretty-print结构体,用fmt.Print来代替就行。
首先main函数,定义了一艘船"King Roy"号,还有几个港口,这个例子里面就用了一个旧金山。
定义了一个到达事件,大概就是King Roy号在2005年到达了旧金山港口,这件事记录于time.Now()这个时刻。
把这个事情扔进事件处理器EventProcessor里面处理一下(更新Ship, Port等实体、记录日志)

// EventLog
type EventLog struct {ls []EventBase
}func (i *EventLog) Add(e EventBase) {i.ls = append(i.ls, e)
}// EventProcessor
type EventProcessor struct {log EventLog
}func NewEventProcessor() *EventProcessor {return &EventProcessor{log: EventLog{ls: make([]EventBase, 0)},}
}func (i *EventProcessor) Process(e EventBase) {e.Process()i.log.Add(e)
}

上面这个是事件处理器+事件日志,eventProcessor.Process方法里,首先让传进来的event调用自己的process方法,然后添加日志;
事件日志很简单,就是一个切片,一直往里面append就可以了,这个可以持久化到数据库里面。

// ArrivalEvent
type ArrivalEvent struct {DomainEventPort *PortShip *Ship
}func (i *ArrivalEvent) Process() {i.Ship.HandleArrival(*i)
}// DepartureEvent
type DepartureEvent struct {DomainEventPort *PortShip *Ship
}func (i *DepartureEvent) Process() {i.Ship.HandleDeparture(*i)
}// DomainEvent
type DomainEvent struct {Recorded time.TimeOccurred time.Time
}type EventBase interface {Process()
}

然后是实现了一个EventBase接口,下面有两种不同的event,到达event+出发event。在各自的Process方法里面调用Ship的处理方法,这里把整个event当做参数传进handle*方法里面i.Ship.HandleDeparture(*i),就很怪,不知道这样写好不好……

var PortAtSea = &Port{Name:    "AT_SEA",Country: "N/A",
}// Entities
type Ship struct {Name   stringPort   *PortCargos []Cargo
}func (i *Ship) HandleArrival(ev ArrivalEvent) {i.Port = ev.Portfor _, c := range i.Cargos {c.HandleArrival(ev)}
}func (i *Ship) HandleDeparture(ev DepartureEvent) {i.Port = PortAtSea
}type Port struct {Name    stringCountry string
}// Cargo
type Cargo struct {Name            string
}

再接着是一些实体类,船、港口、货物。需要注意的是船出发了它就在海上了,port这里就用一个PortAtSea表示没有停靠任何港口。
注意!!!HandleArrivel和HandleDeparture相当于把一个event传递进实体里面了!!!这是设计的思路!!!学吧老哥!!!

深入一点:我们的货物是否经过了加拿大

加拿大是tm挺危险,俺们那边老有人一不小心就自驾到温莎了,毕竟就是过条河的距离,习惯了。

// Cargo
type Cargo struct {Name            stringHasBeenInCanada bool
}func (i *Cargo) HandleArrival(ev ArrivalEvent) {if ev.Port.Country == "Canada" {i.HasBeenInCanada = true}
}

把上面提到的cargo这个实体改一改,做点判断。

func (i *Ship) HandleLoad(ev LoadEvent) {for _, c := range i.Cargos {if c.Name == ev.Cargo.Name {panic("重复的货物我不接,这不是数据结构和算法,不要考虑特殊情况")}}i.Cargos = append(i.Cargos, ev.Cargo)
}func (i *Ship) HandleUnload(ev UnloadEvent) {// Suppose that there must have available cargo at the ship.for index, c := range i.Cargos {if c.Name == ev.Cargo.Name {i.Cargos = append(i.Cargos[1:index], i.Cargos[index+1:]...)break}}
}

Ship也要改一改,加上装卸货的方法。

// LoadEvent
type LoadEvent struct {DomainEventCargo *CargoShip  *Ship
}func (i *LoadEvent) Process() {i.Ship.HandleLoad(*i)
}// UnloadEvent
type UnloadEvent struct {DomainEventCargo *CargoShip  *Ship
}func (i *UnloadEvent) Process() {i.Ship.HandleUnload(*i)
}

加上装卸货的事件。

func TestCanada(t *testing.T) {eProc := NewEventProcessor()refact := &Cargo{Name: "Refactoring"}kr := &Ship{Name: "King Roy"}sfo := &Port{Name: "San Francisco", Country: "US"}la := &Port{Name: "Los Angeles", Country: "US"}yyv := &Port{Name: "Vancouver", Country: "Canada"}eProc.Process(&LoadEvent{DomainEvent: DomainEvent{Recorded: time.Now(),Occurred: time.Date(2005, time.November, 1, 0, 0, 0, 0, time.Local),},Cargo: refact,Ship:  kr,})eProc.Process(&DepartureEvent{DomainEvent: DomainEvent{Recorded: time.Now(),Occurred: time.Date(2005, time.November, 1, 0, 0, 0, 0, time.Local),},Port: la,Ship: kr,})eProc.Process(&ArrivalEvent{DomainEvent: DomainEvent{Recorded: time.Now(),Occurred: time.Date(2005, time.November, 2, 0, 0, 0, 0, time.Local),},Port: yyv,Ship: kr,})eProc.Process(&DepartureEvent{DomainEvent: DomainEvent{Recorded: time.Now(),Occurred: time.Date(2005, time.November, 3, 0, 0, 0, 0, time.Local),},Port: yyv,Ship: kr,})eProc.Process(&ArrivalEvent{DomainEvent: DomainEvent{Recorded: time.Now(),Occurred: time.Date(2005, time.November, 4, 0, 0, 0, 0, time.Local),},Port: sfo,Ship: kr,})eProc.Process(&UnloadEvent{DomainEvent: DomainEvent{Recorded: time.Now(),Occurred: time.Date(2005, time.November, 4, 0, 0, 0, 0, time.Local),},Cargo: refact,Ship:  kr,})fmt.Println(refact.HasBeenInCanada)
}

整体流程是这样的:先在LA装上货,去温哥华兜一圈,再回旧金山卸货。因为温哥华在加拿大,所以refact.HasBeenInCanada字段为真。

http://www.lryc.cn/news/24302.html

相关文章:

  • Vue3 组合式函数,实现minxins
  • 什么是钉钉消息推送?
  • 利用 NVIDIATAO 和 WeightBias 加速AI开发
  • token - 令牌
  • 应用模型开发指南上新介绍
  • Dbeaver连接Hive数据库操作指导
  • 【RabbitMQ笔记09】消息队列RabbitMQ之常见方法的使用
  • Linux字符设备驱动模型之设备号
  • C++多态原理
  • PMP认证与NPDP认证哪个含金量高?
  • 改进YOLOv7-Tiny系列:首发改进结合BiFPN结构的特征融合网络,网络融合更多有效特征,高效涨点
  • PPC Insights系列:洞见安全多方图联邦
  • SQLite注入记录(目前最全、核心函数用法、布尔盲注、时间盲注、webshell、动态库,绕过方式)
  • Java简单的生成/解析二维码(zxing qrcode)
  • 若依项目导出后端响应的Excel文件流处理
  • 华为OD机试【独家】提供C语言题解 - 数组排序
  • JVM详解——内存结构
  • Jvisualvm监控Tomcat以及相关参数优化
  • 界面组件DevExpress WinForms v22.2 - 全面升级数据展示功能
  • 正点原子第一期
  • 「mysql是怎样运行的」第24章 一条记录的多幅面孔---事务的隔离级别与MVCC
  • 入门Java第十五天 线程
  • 探索用卷积神经网络实现MNIST数据集分类
  • MySQL 索引失效场景
  • Xcode开发工具,图片放入ios工程
  • 操作系统权限提升(十九)之Linux提权-SUID提权
  • 直播 | StarRocks 实战系列第三期--StarRocks 运维的那些事
  • KingabseES执行计划-分区剪枝(partition pruning)
  • Operator-sdk 在 KaiwuDB 容器云中的使用
  • 【数据挖掘】2、数据预处理