Scala 2014大会:建立一个Reactive应用案例
这是来自ScalaDays 2014大会上有关如何使用DDD EventSourcing/CQRS和Akka构建一个reactive的微服务应用系统的演讲,大意如下,英文PPT下载
这是以VPower系统为案例,在1.x阶段是一个整体Monolithic系统,使用的是Spring Hibernate和一些Scala实现,数据库是Postgres SQL.Server,这种铁板一块的系统带来可伸缩等很多问题,他们的目标是:
•Scala&&&Akka
• 模块化/分布式Modular/Distributed
• 松耦合Loosely&coupled
• 可扩展伸缩Scalable
• 失败冗余Fault&tolerant
• 快速响应Responsive
•不可变与领域模型 Immutable&domain&model
• 更少的数据表结构Schema@less&data&model
因此,他们选择了:
•DDD领域驱动设计Domain Driven Design
• CQRS
•最终一致性 Eventual-Consistency
• 事件溯源Event-Sourcing
• 无数据结构Schema-less
• 微服务Micro-service based
• RESTFul服务 Headless via Rest
具体实现工具是:
• Scala
• Akka
• Eventsourced/Akka3Persistence
• Spray.io
• Mongo
• Angular.js
• D3.js
这里省去DDD CQRS EventSourcing介绍。
在CQRS的Command中,我们关心的是行为而不是以数据为中心,这就导致DDD一个更加真实的实现。Command是系统的一个请求,用于执行一个任务或动作,比如:
• RegisterClient
• ChangeClientLocale
Command是imperative的。它们是针对可变状态的请求,它们代表客户端想采取的动作,以消息而非DTO方式传递。
•可以看成一系列方法调用
• command-handler-can-say-NO
• 内部状态不能暴露
• 仓储层变得简单。
CQRS命令handler是专门处理命令:
• 客户端以消息形式发送命令。
• 消息被command handler处理
• 命令是可以拒绝的
• 转换为一个或多个事件,这些事件能被持久化。
CommandHandler代码:
|
上面代码中receiveCommand方法是以消息形式接受客户端的一个命令,然后更改内部状态。(banq注:通常状态是由聚合根守卫,更改状态需要通过聚合根实体,这里将CommandHandler和聚合根混为一体,或者可能是将聚合根的修改状态职责迁移到commandHandler,这样做的原因估计是Akka的Actor因为要继承PersistentActor 这样非业务父类,失去直接作为实体纯洁性,因为聚合根实体是纯业务的,不能和任何技术框架绑定。)
接下来谈传统的CRUD增删改查的问题。
•DTO模型会远离领域,最后导致代码偏离需求
•getter方法会暴露内部状态,导致状态修改不一致
• DTO不同于领域模型的模型。
• 通常需要额外的映射转换。
•通常在仓储方法中有大量读方法,
• 实现针对查询优化比较难,因为读写捆绑在一起
• 其实查询的对象并不等同于数据模型。
• 对象模型需要被转换为数据模型。
• 对象和数据两种模型不匹配阻抗难以避免
使用CQRS的读写分离,Command负责写,而Query负责查询:
• DTO负责从存储中读。
• 无需复杂的ORM
• 属于根据屏幕直接抓取数据结构。
• 易于针对读优化
..
Eventual Consistency最终一致性
• 业务自己决定在读和写多长时间进行同步(NoSQL之类数据库也是采取读写同步,但是必须脱离业务在数据库产品中配置)
• 从写这边push推一个异步消息
• 读这边有监听
• 两段事务2PC不再需要
• 使用事件存储event store作为查询
下面是最终一致的读代码:
|
[该贴被banq于2014-07-05 13:18修改过]