转载

构建微服务:使用Node.js和DDD、CQRS以及Event Sourcing - 第二部分

概览

构建微服务:使用Node.js和DDD、CQRS以及Event Sourcing - 第二部分

构建微服务要求整体思维。理解微服务架构及其适用规则很重要。这是什么意思呢?思考的一种方式是理解给定应用程序里的所有域,以及服务边界的细分。

微服务必须完全和表示给定域的关联上下文匹配——应该是松耦合高内聚的。

在第一部分,我们讨论了示例项目中使用的DDD,CQRS以及event sourcing模式。希望你已经查看了GitHub代码库并且复制了代码。这一部分会详细讨论实现细节——我也推荐大家超越本文的范畴,学习更多的相关概念。

这里将项目的微服务架构分解为流程图:

构建微服务:使用Node.js和DDD、CQRS以及Event Sourcing - 第二部分
  • 域模型 实现了聚合根,是微服务的核心——它通过 command处理器聚合 的角度验证并且持久化数据。
  • event 在处理给定事件之前自动持久化到event store。
    使用DDD带来的额外好处是,可以创建易于理解的目录树。这是示例项目的目录树:
    构建微服务:使用Node.js和DDD、CQRS以及Event Sourcing - 第二部分

注意:通过拷贝给定 .env.example 文件在根目录创建名为 .env 的新文件。

选择Node.js技术是因为它遵循模块化的方案构建应用程序——它完美契合微服务以及Unix的设计哲学:

小而美。让每个程序只干一件事情。( 来源 )

模块化仅仅是构建长期运行的应用程序的一个方面。选择最佳实践以及设计模式也很重要——从开发人员和编译器的角度看,这些方面需要仔细思考。

选择Nest.js作为框架是因为Nest.js框架库遵循最佳实践,并且提倡最佳的设计模式,比如依赖注入。这些模式改进代码质量和可维护性——并允许进一步改进。阅读Nest的 文档 ,在继续之前了解它的基础特性。

接下来让我们看看代码!

这是 AppModule ,引入了 UsersModule

构建微服务:使用Node.js和DDD、CQRS以及Event Sourcing - 第二部分

注意:AppModule用来注册微服务使用的所有模块。在示例项目中,涉及的模块包括 UsersModuleEventStoreModule

很简单对吧?这是UsersModule:

enter link description here

注意:UsersModule用来bootstrap所有Users域特定的逻辑。

User请求

构建微服务:使用Node.js和DDD、CQRS以及Event Sourcing - 第二部分

在写这篇文章的时候, Fastify 处理大约76835请求/秒, Express 处理大约50933请求/秒。我的项目实现了Fastify,因为我们希望得到尽可能大的吞吐量。可以很容易得从Fastify改成其他的路由框架。

文档是 UsersModule 里控制器实现的核心!这很有用,通过流化创建及维护微服务API文档的流程可以节省大量时间。

构建微服务:使用Node.js和DDD、CQRS以及Event Sourcing - 第二部分

本项目最重要的是创建用户的流程:

构建微服务:使用Node.js和DDD、CQRS以及Event Sourcing - 第二部分

通过用户接口触发command。当UserCreatedEvent被捕获后,userCreated saga继续欢迎用户的事务。

Commands——实现&处理器

构建微服务:使用Node.js和DDD、CQRS以及Event Sourcing - 第二部分

聚合的所有变化都必须通过 command 来处理。当某个command被调度后,相关的 command处理器 验证并恢复给定请求——如果成功了,会尝试将新事件持久化到event store里。

客户端应用程序创建command,随后发送到域层。Command是通知某个特定实体执行特定操作的消息。( 来源 )

构建微服务:使用Node.js和DDD、CQRS以及Event Sourcing - 第二部分

CreateUserCommand实现——src/users/commands/impl/create-user.command.ts

注意:command实现用来构建command。实现应该易于理解——即使对非开发人员来说。

构建微服务:使用Node.js和DDD、CQRS以及Event Sourcing - 第二部分

CreateUserCommand处理器 — src/users/commands/handlers/create-user.command.ts

注意:command处理器用来管理某个命令请求。在调度事件时需要按顺序commit。

聚合根——验证业务的域模型

构建微服务:使用Node.js和DDD、CQRS以及Event Sourcing - 第二部分

域模型是域规则定义的地方。用聚合,而不是域模型来描述User——在DDD里,聚合是域对象的集合,可以当作单个单元来处理。记住,域模型对象是不可变的,因此需要聚合。

聚合形成对象关系的树形结构或图。聚合根是“最上层“的那个对象,它代表整体,也可能委托给其他对象。它很重要因为外部是通过它来通信的。( 来源 )

构建微服务:使用Node.js和DDD、CQRS以及Event Sourcing - 第二部分

User — src/users/models/user.model.ts

注意:User模型仅仅触发相关的事件。

构建微服务:使用Node.js和DDD、CQRS以及Event Sourcing - 第二部分

UserRepository — src/users/repository/user.repository.ts

注意:Users repository使用User模型来持久化并且处理事件。

存储事件——乐观锁&幂等

构建微服务:使用Node.js和DDD、CQRS以及Event Sourcing - 第二部分

event sourcing领域的并发是很重要的。使用分布式command处理器时,聚合的并发访问或者修改一般不是个问题——可以做一定程度的冲突管理。

示例项目选择了乐观锁——了解event sourcing里乐观vs.悲观锁机制也很重要。

Event Store使用如下端口:

http://localhost:1113 (tcp)

http://localhost:2113 (http)

Event Store的所有HTTP请求都是幂等的。

发布事件到event store很简单,代码如下:

构建微服务:使用Node.js和DDD、CQRS以及Event Sourcing - 第二部分

Event Bus 发布 — src/event-store/event-store.ts

在项目的 event-store.ts 文件里,包含Event Store bridge的逻辑,它订阅了给定域分类流里的所有事件。在这里分类流是Users域——它使用了名为 $ce-users 的类projection。

Event Store自带接口。当事件发生时,接口会通知用户,并且实时处理事件。

构建微服务:使用Node.js和DDD、CQRS以及Event Sourcing - 第二部分

用Docker运行在本地的Event Store实例— http://localhost:2113

event store的设计保证event持久化和流化的原子性。可以试试,发送请求创建新的User,然后监控Event Store看看发生了什么。

Event由处理器实现——和command类似。

构建微服务:使用Node.js和DDD、CQRS以及Event Sourcing - 第二部分

UserCreatedEvent实现 — src/users/events/impl/user-created.event.ts

注意:事件实现用来构造事件。

构建微服务:使用Node.js和DDD、CQRS以及Event Sourcing - 第二部分

UserCreatedEvent处理器 — src/users/events/handlers/user-created.handler.ts

注意:事件处理器用来管理事件。

事件处理器负责写入物化状态或者缓存来更快的读取。

最后,User saga继续Users创建事务

构建微服务:使用Node.js和DDD、CQRS以及Event Sourcing - 第二部分 构建微服务:使用Node.js和DDD、CQRS以及Event Sourcing - 第二部分

UsersSagas — src/users/sagas/users.saga.ts

UserCreatedEvent 事件存储之后,系统发出由userCreated saga触发的UserWelcomedEvent——之后用户创建事务完成。

读取物化数据

构建微服务:使用Node.js和DDD、CQRS以及Event Sourcing - 第二部分

user command小结

构建微服务:使用Node.js和DDD、CQRS以及Event Sourcing - 第二部分
  • command 是一个对象,由用户通过用户接口发送
  • 域模型 定义了将变更反应到域聚合的规则
  • command处理器 在调度事件之前验证。
    ###user查询小结
    构建微服务:使用Node.js和DDD、CQRS以及Event Sourcing - 第二部分
  • 数据库里的物化数据是由event数据的非规范化创建的。
  • 使用projection可以回放事件,从而重新创建数据
    ###运行项目
    {{{
    $ ./scripts/up.sh ## to boot up
    $ ./scripts/down.sh ## to shut down
    }}}
    ###结论
    事件让我们可以关注于域而不是数据库schema,跨团队的合作也更为容易。
    我们介绍了如何构建一个微服务,解释了DDD,CQRS和event sourcing的概念。克隆GitHub的代码库,自己多做尝试。
    记住所有的模式都有自身的优缺点,CQRS和event sourcing模式也不例外——不是解决一切问题的银弹。
    ####真实世界里
  • 外部应用程序也可以和微服务事件交互
    ####一些涉及CQRS/ES的案例
  • 审计系统
  • 股票交易系统
  • 许可证平台
  • 数据库系统
    我一直保持学习和实验,本文的示例项目还没有完结。借着分享构建项目的经验,来感谢所有参与贡献OSS(开源软件)技术的人。
    下面是项目连接和联系方式。
  • 项目的 GitHub
  • Qasim Soomro的 网站
  • Qasim Soomro的 LinkedIn
  • Qasim Soomro的 AngelList
  • Qasim Soomro的 Twitter

资料

  1. Martin Fowler — CQRS ( 文章 )
  2. Greg Young — Event Sourcing ( 视频 )
  3. MSDN — CQRS Journey ( 文章 )
  4. Eric Evans — Domain-Driven Design, Tackling Complexity in the Heart of Software ( 书 )
  5. Christopher Richardson — Microservice Patterns ( 书 )
  6. PayPal — API Guidelines ( 网站 )
  7. Kanasz Robert — Introduction to CQRS ( 来源 )
  8. Werner Vogels — Eventually Consistent ( 来源 )
  9. CQRS.nu — FAQ( 来源 )
原文链接: Building Microservices: Using Node.js with DDD, CQRS, and Event Sourcing — Part 2 of 2 (翻译:崔婧雯)

===========================

译者介绍

崔婧雯,现就职于IBM,高级软件工程师,负责IBM WebSphere业务流程管理软件的系统测试工作。曾就职于VMware从事桌面虚拟化产品的质量保证工作。对虚拟化,中间件技术,业务流程管理有浓厚的兴趣。

原文  http://dockone.io/article/9884
正文到此结束
Loading...