项目地址: https://github.com/kbastani/event-stream-processing-microservices
首先看了 spint-boot-starter/spring-boot-starter-data-events 项目下的 Aggregate 基类,没有完全看明白,遂决定先不看基类,从子类开始看。
然后看 account/account-web 项目。一个 domain.Account (聚合根)的行为,本来在聚合根中自己处理就行了,但是先调用 action.SomeAction,再调用 domain.AccountService。这点完全没有看懂,这里的 Action、Module、包括 Service,貌似都是聚合根的辅助类,不明白为什么要加这些,尤其是 AccountService 还容易跟领域服务搞混。
再接下来看到一个行为,我就直接开始怀疑人生了。
有没有见过这样代码架构的,解释和推广一下。
1
passerbytiny OP 好吧,整体架构上发现也不对,Web 与 Worker 的职责和关系是:Web 负责生产和存储事件; Worker 负责消费事件。而不是我原来想要的:Web 独立负责业务,生产事件时仅存储事件,主业务与存储事件有强一致性(即在同一个事务中); Worker 负责异步的将事件发送到消息中间件。
|
2
xuanbg 2018-08-23 07:32:32 +08:00
大致看了一下,我觉得他的设计的核心就是前端的功能路由由后端来提供。。。譬如创建了一个账号,就会返回给前端一些账号信息和 3 个 url,点这 3 个 url 进去优惠得到一些数据和 url。前端只要按一定的规范展示这些数据和 url 就行了,典型的软狗思维。
过度设计,套了一些流行概念,很是能哄骗一些入世不深的小年轻。其实根本没鸟用,产品经理 /乙方老板才不要按你的这个套路来呢。 |
3
passerbytiny OP |
4
xuanbg 2018-08-23 10:57:15 +08:00
@passerbytiny 我不至于连 RESTful 也不知道。。。我说的路由是这个货:
"_links": { "self": { "href": "http://localhost:54656/v1/accounts/1" }, "events": { "href": "http://localhost:54656/v1/accounts/1/events" }, "commands": { "href": "http://localhost:54656/v1/accounts/1/commands" } } 这 3 个 url 是包含在账号信息里面的,文档里面也说得很清楚,就是进一步可以点击的 URL。 我们也是 RESTful 的接口,但只返回账号信息数据,不返回这些进一步操作的 URL。你明白? |
5
passerbytiny OP @xuanbg 这就是 RESTful 的 links
|
6
passerbytiny OP |
7
feiyuanqiu 2018-08-26 04:16:15 +08:00
@xuanbg Hypermedia As The Engine Of Application State (HATEOAS)
|
8
feiyuanqiu 2018-08-26 05:19:20 +08:00
只看了 account-web 下面的 account 创建的逻辑,感觉没什么问题,基本是按照经典的领域驱动设计做的
AccoutService 是 Account 领域服务这个没什么好说的,AccountService 在 registerAccount 时,先调用数据仓库持久化实体,再发布领域事件 ACCOUNT_CREATED,领域事件实际上就是个订阅发布者模式,account-worker 订阅了 AccountEvent,收到事件消息后,会再做一些处理 (我不太喜欢它这里实体继承 Aggregate 的做法,account.sendAsyncEvent 并不是 account 的领域行为,不应该放在实体里,这样让实体不单纯了,同时还反向依赖了 AccountService,也不好测试) |
9
passerbytiny OP @feiyuanqiu
AccoutService 从它的位置来看是领域服务,但是从它的作用来看并不像领域服务。它只处理 Accout 的内部行为,换句话说它就是 Accout 实体的行为的集合。另外,在实体中调用领域服务是一个很危险的行为。领域服务是允许处理多个聚合根的,实体调用领域服务,一不小心就通过它调用了另外的聚合根。所以我很怀疑:在 account-web 的架构中,AccoutService 不是领域服务,而是 Account 实体的动作(由各个 Action 定义)的实现。 sendAsyncEvent 发布异步领域事件,其中的发布领域事件就是聚合根的领域行为,这个没问题。但是发布时就确定异步这个处理是否合理就不确定了,我的意见是同步或异步应当由订阅方而不是发布方决定。 我其实要找的是:强一致性的事件存储、部分事件内部直接订阅处理、部分事件读取事件存储发布到消息中间件(可重试)、外部限界上下文通过消息中间件订阅事件(可纠正顺序)、使用 Spring Cloud Stream。然而它那个架构并不是这样的,它只是 account-web 存储并立刻发送事件到消息中间件(不重试)、accout-worker 通过消息中间件订阅事件(未对接受顺序纠错)。 |
10
RandomJoke 2018-09-04 15:00:53 +08:00
这个代码我倒是没怎么看过,不过 LZ 寻找的东西貌似和我很相似,在网上找了很多例子发现都不太符合自己的需求。所以最后我是 axon 实现了 Event Sourcing + CQRS,然后拓展了下事件的存储来保证事件发布到消息中间件的一致性问题(通过 Spring Cloud Stream ),Consumer 的消费顺序问题,我没有做深入的处理,只是所有的 Domain Event 都会带上 axon 中的 seq,在 Consume 的时候碰到 seq 不对的,在重试几次之后就直接丢到 DLQ 中走失败流程了。
|
11
passerbytiny OP @jokefaker Event Sourcing + CQRS 走得太远了,这个架构以后再处理。我现在需要把常规的、微服务用的事件驱动架构做出来。
|