Microservice 随记

我们定义一个Domain Event来描述发生在一个Aggregate上的行为。
一个事件往往代表着一个状态的改变。
比如Order Aggregate,它的状态改变包括订单创建,订单撤销,订单装配等
同时也可以定义来表示违反某种业务规则的行为。

Order Aggregate创建一个NEW状态,发布一个OrderCreated事件。
Customer Aggregate获取OrderCreated事件,为订单预留信用额度,并发布一个CreditReserved事件。
Order Aggregate消费CreditReserved,将它的状态修改为APPROVED

如果信用额度因没有足够的资金检查失败,Customer aggregate发布一个CreditLimitExceeded事件。
该事件不会因为相应状态的变化,而是表现为不能违反商业规则。
Order Aggregate消费该事件将订单状态修改为CANCELLED 状态。

微服务架构是一个事件驱动聚合的web
每个服务都有一个或者多个聚合Aggregate组成,每个事物执行都是通过一个服务更新或者创建一个单独的聚合。
服务通过事件在聚合之间维护数据的一致性。

Event Sourcing是一种稳定更新状态和发布事件的方案。以此为基础设计事件驱动架构。
Snapshot能够提高查询聚合,组合所有事件到一个特定的时间点上。

事件源为查询带来挑战,但是这些可以通过CQRS原则和materialized views实体化视图来解决。

事件源和CQRS 不需要特别的工具和软件,目前很多框架都能为其提供底层支持。

为每个服务实现业务逻辑作为一个DDD聚合体。
每个事务更新或者创建一个聚合体,使用事件来在聚合体或者服务之间维护数据的consistency一致性。

如何使用事件来自动更新一个聚合体和发布一个事件。我们这里使用事件源,它使用事件集中方式来设计业务逻辑和持久化。
微服务架构会对查询带来一定的挑战,我们使用Command Query Responsibility Segregation(CQRS)来实现可扩展性和执行查询。

当一个服务在数据库里创建或者更新一个聚合体时只需要发布一个事件即可。
但是有个问题就是更新数据库和发布事件必须是自动的。
否则,如果一个服务在更新了数据库后在发布事件之前阻塞了,系统会保持在一个不一致的状态中。
传统的解决方案是使用一个分布式事务包含数据库和消息代理在里面。

我们的解决方案是通过发布事件到消息代理(比如Apache Kafka)来更新数据库。
消息的接收者预定消息代理的消息最终完成更新数据库。
这种机制保证了既发送了事件消息,又更新了数据库。
这样做的缺点在于实现起来比较复杂,一个应用程序不能立刻读取自己刚刚写入的内容。


Service A--->publish Message ---> Message broker  --> Consumer get events-->write database
Service B--->get event message -->perform other operation
Service A ---> read database

另外一个方案是:应用程序追踪数据库的事务日志,将每个事务日志转化为一个事件,发布到消息代理。
该方案的一个最大好处就是不需要对应用程序做任何改变。
缺点在于很难进行高水平业务事件的反响工程,原因在于数据库更新是从底层数据库表的行上发生的
很难体现出业务处理层面的问题和流程。

第三种方案是使用一张数据库表作为临时消息队列,当一个服务更新一个聚合体时,它插入一个事件记录到EVENTS数据库表,作为本地ACID事件的一部分。
另外一个进程从EVENTS表中获取记录并发布成事件到消息代理。
该方案的好处在于可以发布高级别的业务事件,缺点在于error-prone 容易出错,
因为事件发布代码必须和业务逻辑同步。

以上三种方案都有相当多的缺陷:
发布消息到消息代理然后更新数据库不能做到写入读取一致。
追踪数据库事务日志能够提供一致性的读取,但不能发布高级别的业务事件。
使用数据库表作为消息队列提供了读取一致和发布高水平业务事件但是依赖于开发者记得在状态变化时发布事件。

幸运的是,我们还有另外一个选项,就是
Event-centric 事件中心方式就是将持久化和业务逻辑作为事件源。

一个服务使用事件源持久化每个聚合体为一个事件序列。
当它创建或者更新一个聚合体时,服务保存一个或者多个事件到数据库,该数据库被称为事件存储。
通过加载这些事件并重放它们来重塑当前聚合体状态。
在函数编程里,一个服务通过在事件上执行一个增减函数来重构聚合状态。

因为事件及状态,我们就没有什么自动更新状态和发布事件之分了。

比如,订单服务,我们在ORDERS表里不保存每个订单记录行,而是保存每个订单聚合体成一系列事件
订单创建,订单通过,订单装配等
event_id,event_type,entity_type,entity_id,event_data
102      Order Created  Order     101      {...}
103      Order Approved Order     101      {...}
104      Order Shipped  Order     101      {...}
105      Order Delivered Order    101      {...}
....

entity_type 和 entity_id 列指定聚合体
event_id 指定事件
event_type 指定事件类型
event_data 将事件的属性序列化成JSON格式保存

有些事件会包含大量数据,比如订单创建事件,包含完成订单的购买项目,支付信息,配送信息等。
其它事件,比如订单配送事件,包含较少数据,可能没有数据,只是表示一种状态。

事件源和事件发布
严格来讲事件源持久化聚合体为事件,很容易用做可靠的事件发布机制。
保存事件作为一个内部的原子性操作来保证事件存储,交付事件到对其感兴趣的服务。
比如,我们将事件保存到上面描述的EVENTS表中,预订者可以很容易的从该表中拉取新事件。

比如Eventuate Local使用事务日志追踪。
它从MYSQL的复制流读取事件插入到EVENTS表中,并发布它们到Apache Kafka

使用快照来提供性能
一个订单聚合体很少有状态转换所以它有较少的事件。
通过读取这些事件存储来重构订单聚合体非常高效。
有一些聚合体包含了庞大的事件数量,比如客户聚合体可能有很多信用保留事件,随着时间的增加
它会变的很大,加载效率降低。
一个常用的解决方案是 定期保存聚合体状态快照,应用程序通过加载最近的快照和从快照以后发生的事件记录回放
来恢复聚合体状态。
在函数编程里,快照是fold的初始值,如果聚合体有一个简单容易序列化的结构,快照也就容易JSON序列化。
更复杂的聚合体可以使用Memento Pattern 获取快照。

实现 事件源:
一个事件存储是一个数据库和一个消息代理的组合。
数据库是因为它有API可以插入和通过主键获取聚合体的事件记录。
同时又是消息代理是因为它提供API用于订阅事件。

我们有很多不同的方式实现事件存储。
一种是编写自己的事件源框架。比如我们可以持久化事件到一个RDBMS,简单但是从EVENTS表抽取并发布事件用于订阅比较低效。

另外的一个选择是使用一个特定目的的事件存储,它通常提供一个丰富的内容和更好的性能和可扩展性。
比如:http://eventuate.io/ 基于云服务和Kafka/RDBMS的开源事件源项目
https://www.lightbend.com/platform/development/lagom-framework
https://geteventstore.com/

它能够在聚合体状态发生变化时可靠的发布事件。
是事件驱动的微服务架构基础。
因为事件可以记录制造变化用户的身份信息,事件源能够提供审计日志来保证准确。
事件流可以被用于各种不同的目的,包括发送通知给用户,应用集成等。

事件源保存了每个聚合体的整个历史,这样我们能够很容易的实现短暂查找聚合体过去的状态。
我们要决定一个聚合体某一时点的状态,只需要打包到那个时点为止发生过的事件即可。

比如要计算过去某个时点上用户的信用额度。

事件源几乎避免了O/R 异构问题。这是因为它持久化的是事件而不是聚合体。事件往往都是简单容易序列化的结构。
一个服务可以快照一个复杂的聚合体的序列化一个过去的状态。
Memento纪念品模式 在聚合体和它序列化之间添加一个间接的层。

事件源的缺点在于,它是一个完全不一样的编程模型,学习曲线较大。
另外一个缺点是消息代理通常保证至少一次分发,事件处理器是非幂等的,需要决定放弃重复的事件。
事件源框架可以通过给每个通过的事件赋予一个增加的ID,事件处理器器可以通过追踪最后看到的事件ID来判断重复事件。

另外一个挑战是事件的范式和快照将会过时。
因为事件会永久存储,一个服务可能需要打包事件到多个范式版本,当它
https://geteventstore.com/
http://domainlanguage.com/ddd/reference/
https://blog.eventuate.io/2017/03/07/eventuate-local-now-supports-snapshots/
https://github.com/eventuate-local
https://www.infoq.com/vendorcontent/show.action?vcr=4179&utm_source=infoq&utm_medium=related_content_sponsored_link&utm_campaign=relatedContent_sponsored_articles_clk
https://www.infoq.com/articles/microservices-aggregates-events-cqrs-part-2-richardson
https://spring.io/blog/2014/07/24/spring-framework-4-1-handling-static-web-resources
http://ionicframework.com/docs/cli/
http://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#using-boot-structuring-your-code
http://www.typescriptlang.org/#download-links


1.SpringBoot 
2.SpringData
3.SpringCloud

Angular2
React
Axon Framework
Eventual

基于Spring Boot实现的云应用开发工具,它为基于JVM的云应用开发的配置管理,服务发现,断路器,智能路由,微代理,控制总线,全局锁,决策竞选,分布式会话和集群状态管理
等操作提供了一种简单的开发方式。
Spring Cloud包括 Spring Cloud Config,
Spring Cloud Netflix
Spring Cloud CloudFoundry
Spring Cloud AWS
Spring Cloud Security
Spring Cloud Commons
Spring Cloud Zookeeper
Spring Cloud CLI

Spring Cloud Netflix提供了:服务发现(Eureka) ,断路器(Hystrix), 智能路由(Zuul), 客户端负载均衡(Ribbon)
https://www.infoq.com/articles/microservices-aggregates-events-cqrs-part-1-richardson
https://www.infoq.com/articles/microservices-aggregates-events-cqrs-part-2-richardson?utm_campaign=rightbar_v2&utm_source=infoq&utm_medium=articles_link&utm_content=link_text

微服务架构使用服务作为模块单元。
每个服务关联一个业务功能,这些业务功能是一个组织创造价值而进行的活动。
比如一个微服务架构的在线商店,由Order Service,Customer Service,Catalog Service组件组成。
每个服务都有自己不能超越的实现边界。

Domain Models,Transactions,Queries这三类东西很难划分边界。
Aggregate 定义

微服务和数据库

Write-ahead logging(WAL)是数据库系统中提供原子性atomicity和可靠性durability(ACID中的两个)的技术。
对数据库所有的修改在应用执行之前都被写入到日志中。通常是redo日志和undo日志

ARISE 
Algorithms for Recovery and Isolation Exploiting Semantics
遵循三个原则:
Write-ahead logging
Repeating history during Redo
Logging changes during Undo

Logging
需要有两数据结构:Dirty Page Table(DPT) 
Transaction Table(TT)
DPT


Domain Models,Transactions,Queries这三类东西很难划分边界。
Aggregate 定义

由于一些NoSQL数据库不支持ACID,所以我们需要采用一种新的技术来维护数据的一致性。
以事件源技术为基础的事件驱动架构方案来解决分布式微服务系统的数据一致性问题。

查询和报表问题:
在单体集成应用程序中我们很容易通过连接表查询来获取综合查找和报表展示。
但在基于微服务的分布式架构系统中我们无法使用该方式。
比如在在线商店系统中,订单服务和客户服务的信息分别存储在两个不同服务的数据库表中。
只能通过服务提供的API来交互。甚至有些数据并不是直接来自数据库表,还有可能是事件源。
我们将使用Command Query Responsibility Segregation(CQRS)来维护具体的视图。


DDD 聚合就是我们要创建的微服务块
Aggregate 是业务领域模型的构造块,许多开发语言里说的Entity 实体。
它是具有用于持久化标识的对象。与之相对应的是没有用于持久化标识的对象:View Object
VO只包含对象属性。
Service:包含不属于任何entity和VO服务的业务逻辑。
Repository:持久化实体Entities的集合。
一个building block 构造块Aggregate,是开发微服务的关键对象。
一个构造块是一个域对象集群,可以被看做一个操作单元的域对象集群。
它必须包含一个根对象和一个或者多个关联实体对象和值对象VO。
比如在在线商店域模型中,有两个构造块:ORDER和CUSTOMER
ORDER块中有一个根实体Order,一个或者多个值对象OrderLineItem,Delivery Address,PaymentInformation对象等。
CUSTOMER块中有Customer根对象实体,DeliveryInfo和PaymentInfo等VO对象。

块的定义明确了系统加载和删除操作的范围和边界,数据加载时一般都是整个块对象整体加载。
删除一个块对象,将删除其包含的所有其它对象。

这就要求块内部的引用必须使用主键引用而不能使用对象引用。
比如:订单对象Order在引用客户信息时必须使用customerId 而不能直接应用Customer对象。
类似的,OrderLineItem引用Product对象信息时也必须使用productId 属性。
如此才能保证构造块之间的松耦合性。
我们可以轻松的将不同的构造块放到不同的微服务里。
实际上,一个业务逻辑服务是由一个域对象模型和聚合块构成的集合。
比如一个OrderService 包含Order Aggregate
CustomerService包含Customer aggregate

一个事务只能创建或者更新一个构造块Aggregate
这样就能确保一个事务只包含在一个微服务里。同时它有匹配了大多数NoSQL数据库的有限的事务模型。

我们在设计一个域模型时,一个必须要考虑的关键是设计多大的构造块Aggregate.
一方面由于构造块通过内容分离提高了模块化,它应该足够的小。
一个构造块通常是一起加载的,还要考虑有效性
其次,因为对每个构造块的更新都是顺序进行的,使用更好的粒度的块组合能够增加
同时处理请求的能力提高扩展性。
因为一个块是一个事务范围,那么我们可能需要定义一个更大的块来保持一个特定更新的原子性。

比如,在线商店系统中,Order和Customer是独立的块的设计,还可以有另外一种设计方法:
就是将Order作为Customer块的一部分。
这样做的好处是一个大的Customer块可以解决信用检查的原子性问题。
缺点是将对订单和客户信息的管理整合到一个微服务里,会降低可扩展性。
因为我们在更新同一个用户的不同订单事务需要序列化。
类似的还有,当两个用户试图为同一用户编辑不同订单时,可能会出现冲突。

随着订单数量的增加,Customer块的加载成本会增加。
所以最好不要使用这样的大块设计。

尽管一个事务只能用于一个单一构造块的创建和更新,但应用程序必须要维持各个构造块(微服务)
之间数据的一致性。
比如订单服务在创建一个新的订单构造块对象时必须要检查客户服务构造块中的信用额度是否超出。
这样的数据一致性问题,我们有很多解决方法:
一个方式是欺骗方式,在一个事务中创建和更新多个构造块。
这个只有在所有构造块都属于同一个服务并持久化到同一个关系数据库时才可能实现。
另一个更好的方式是:
使用事件驱动的方式维持各个构造块之间的最终一致性。

使用事件维护数据一致性:
微服务架构中,每个服务都有自己的数据提供者,2PC肯定是用不了的。
又加上,许多应用服务使用NoSQL数据库,它们不支持本地ACID事务。
所以只有使用通过事件驱动的最终一致性事务模型了。

我们定义一个域事件,它会在构造块发生某些变化时发生。
一个事件通常表现为一个状态的变化。
比如一个订单构造块能够发生的事件有 订单创建,订单撤销,订单配送等。
事件还可以表现为违反了某业务规则,比如客户的信用额度不够。


使用事件驱动架构:
微服务使用事件来维护构造块之间的数据一致性流程:
一个构造块在发生某些可通知的变化时发布一个事件,比如它的状态改变,发生了违反业务规则
问题等,其它的构造块预订该事件并更新自己的状态。

比如在线商店在创建一个订单时使用如下步骤校验客户信用额度:
1. 订单构造块创建一个NEW状态的订单,并发布一个OrderCreated事件。
2. 客户信息构造模块消费OrderCreated事件,为该订单预留额度,并发布一个CreditReserved事件。
3.订单构造块消费CreditReserved事件,修改其状态为APPROVED。

如果信用额度检查失败,客户信息构造块会发布一个CreditLimitExceeded事件,订单构造块会消费
该事件将订单状态改为CANCELLED。


事件驱动构造块的web应用微服务架构

每个服务的业务逻辑都是由一个或者多个构造块组成。
每个更新和创建单个构造块的服务都是在一个事务中完成的。
服务通过事件在各个构造块之间实现数据一致性。

Service A(Aggregate A,Aggregate B..)
Service B(Aggregate C, Aggregate D)
Service C(Aggregate E, Aggregate F)

微服务架构从功能上分解应用程序到不同的服务。
每个服务对应着一个业务。
我们通过Domain Driven Design Aggregate方式来分解。
每个服务都是一个独立的领域模型,它由一个或者多个DDD 聚合组成。

一个服务就是一个领域模型。
https://www.infoq.com/articles/microservices-aggregates-events-cqrs-part-1-richardson

事件源是一种用于稳定的更新状态和发布事件的技术,它客服了其它方案的局限性。
事件驱动架构的设计概念,使用事件源,以及微服务架构模式。

Snapshot快照可以在通过组合特定时间点上的所有事件用于查询构造块以提高性能。

事件源带来的查询挑战,我们可以通过遵循CQRS原则来克服实现具体视图。

事件源和CQRS不需要任何特定的工具来实现它,许多框架已经实现。

前面我们说过使用微服务架构的主要障碍在于域模型设计,事务实现和综合查询。
它们都受限于功能的分解。
我们使用的解决方案是使用一系列的DDD聚合来为每个微服务实现业务逻辑。
每个事务的更新和创建都在单个聚合体内部。
使用事件来维持各个聚合体之间的数据一致性。

接下来我们说明使用事件来原子性更新一个构造块和发布一个事件。
我们使用事件源来解决这个问题。它是以事件集中方式来进行业务逻辑设计和持久化的。

可靠的状态更新和事件发布:
表面上看使用事件维护构造块之间的数据一致性挺简单,当一个服务在数据库中创建和更新一个构造块时,
只需要简单的发布一个事件即可。
但是这里有个问题,那就是更新数据库和发布事件必须是原子性的。否则,比如一个服务在更新了数据库
后但在发布事件之前崩溃了,这样就出现了系统中相关数据不一致状态。
传统的解决办法是使用包含数据库和消息中介件的分布式事务。但是2PC在这里用不了。

所以一种情况是应用程序通过发布一个事件到消息中间件(比如Apache Kafka)来执行更新。
消息的消费者预定该消息,最终执行更新数据库。
该方式保证了数据库被更新和事件被发送。
缺点在于这是一个更加复杂的一致性模型。
应用程序无法立刻读取到自己写入的东西,有一定的时间延迟。

另外一个选项是应用程序追踪数据库的事务日志(比如commit log),将每一个变化的记录转化为一个事件
并发布到消息中间件,这样做的最大好处是不需要对应用程序进行任何的修改。
缺点在于,因为数据库的更新是在表的数据行级上的操作属于比较底层的变化,我们很难将其反向工程到
业务级别生成业务事件。

第三个方案:使用数据库作为临时消息队列:
当一个服务更新构造块时,它将一个事件插入到数据库的EVENTS表中,作为本地ACID事务操作的一部分。
另外一个独立的进程来读取EVENTS表的记录并发布事件到消息中间件。
这个方案比较好的地方是服务能够发布较高层级的业务事件。
缺点在于它有容易出错的风险,因为事件发布代码必须和业务逻辑同步。

综上:
更新状态后发布事件到消息中间件不能提供读写一致性。
跟踪事务日志能够提供读写一致但不能发布高层级的业务事件。
使用数据库表作为消息队列提供了一致性读写和高级业务事件的发布但是依赖于开发者记得在状态改变是发布事件。

所以我们采用以业务逻辑作为事件源发布事件,以事件为中心的方式来进行持久化。
事件源是以事件集中的方式来执行持久化。
一个服务将每一个构造块的持久化活动作为一个事件序列保存起来。
当服务创建或者更新一个构造块是,服务会保存一个或者多个事件到数据库中。
该数据库作为事件存储。
我们能够通过加载并重放这些构造块的相关事件来重构其当前状态。
在函数编程方面,一个服务通过执行一个函数包重构一个构造块状态。
因为事件就是状态,所以我们不存在更新状态的原子性和发布事件一致问题。

比如订单服务,我们在一个ORDERS表中不再将一个订单记录作为一行存储。
而是将订单构造块的一系列事件比如Order Created, Order Approved,Order Shipped等存储。
event_id,event_type,entity_type,entity_id,event_data
其中entity_type 和 entity_id 用于指定构造块
event_id 指定事件
event_type 指定事件类型
event_data 事件属性序列化的JSON数据形式

有些事件包含大量数据,比如订单生成事件,包含完整的订单以及订单的项目行信息,支付信息,配送信息等

事件源和发布事件:
严格来说,事件源仅仅是持久化构造块为事件。
保存一个事件是一个本质上就具有原子性操作,事件存储将交付事件给对它感兴趣的服务。
比如 事件被存储到EVENTS表中,预定该事件的服务可以从该表中拉数据。

Eventuate Local使用日志追踪技术,它读取插入到EVENTS表的数据并从MySQL复制流并发布到Apache Kafka。

使用快照来提高性能:
一个订单构造块有少数几个相关的状态转换,它有较少的事件发生。
我们可以很有效的查询它的事件存储并重构一个订单构造块。
但是有些构造块拥有大量的事件,比如一个客户信息构造块潜在的有Credit Reserved events, 随着时间的推移,
它的载入和批量事件处理的新能就会降低。

一个通用的解决方案是周期性的持久化一个构造块状态的快照。
应用程序可以通过载入最近的快照以及自从快照创建以来所有相关的事件重放,便可以重建状态。

从功能上说快照是批量操作的起始基础。
如果一个构造块比较简单,并且容易序列化,那么它的快照也会比较简单比如JSON 序列化。
这样在恢复该构造块状态时,只需要载入快照并将其后的事件日志并执行,并可以得到想要的环境。

实现事件源:
一个事件源其实是一个数据库和一个消息中间件的混合体。
它是一个数据库,因为它提供API用于插入和并通过主键获取一个构造块的事件。
它同时又是一个消息中间件,因为它提供API来预定事件。

这里有几种不同的实现方式:
一种就是编写自己的事件源框架。我们可以将事件保存到RDBMS。
一种简单低效的方式来发布事件,预订者从EVENTS表中拉事件。

另外一个选项是使用特点目的的事件存储,它通常应该提高丰富内容和更好的性能以及扩展性。
Event Store(https://geteventstore.com/)
Lightbend(http://www.lightbend.com/)
Eventuate(http://eventuate.io/)
其中Eventuate可以作为云服务和Kafka/基于RDBMS的开源项目。

事件源的好坏:
事件源一个主要的好处是无论构造块状态发生什么变化都能稳定的发布事件。
它是事件驱动的微服务架构基石。
由于每个事件都可以记录造成变化的用户的标识,事件源提供审计日志来保证准确性。
事件流可以被用于各种其它目的,包括发送通知给用户,以及应用集成。

事件源的另一个好处是它保存了每个构造块的整个变化历史,我们可以很容易的事件临时查询
来获取过去其状态。要获取某个构造块在过去某个时间点的状态我们只需要打包从那个事件点以前的
事件并执行即可。

事件源还能最大程度的避免O/R 异构问题。
因为它持久化的是事件而非构造块。
事件通常是简单容易序列化的结构。
一个服务可以通过序列化一个状态记忆来快照一个复杂的构造块。
Memento模式在一个构造块和其序列化表现之间增加一个双向的备份。

事件源的缺点在于:
它是一个不一样的编程模型,具有一定的学习曲线。
对于一个已有的应用系统要使用事件源,我们必须重写它的业务逻辑。

另外一个缺点是消息中间件通常保证至少一次分发。
事件处理器不能幂等,必须判别并抛弃重复事件。
事件源矿建可以通过给每个事件设置唯一的自增id,让事件处理通过最终最新事件ID
来判别事件是否重复。

事件源的另一个挑战在于事件(和快照)的范式,将随着之间变化。
因为事件需要永久存储,一个服务可能需要打包事件成多个范式版本。
一个简化服务的范式是事件源框架在它从事件存储加载事件时就将所有事件转换为
最新版本的范式。这样服务只需要加载最新版本的事件。

查询事件存储也是一个挑战,比如,我们需要查找信用可靠的客户:
SELECT * FROM CUSTOMER WHERE CREDIT_LIMIT<? AND c.CREATION_DATE>?
这里没有包含信用额度列,我们必须使用一个更加复杂低效的查询。
更糟的是NoSQL事件存储通常只支持基于主键的查找。
结果我们必须使用CQRS模式实现查询。

使用CQRS实现查询:
事件源是微服务架构里实现有效查询的一个主要障碍。
SELECT *
FROM CUSTOMER c, ORDER o
WHERE
   c.id = o.ID
     AND o.ORDER_TOTAL > 100000
     AND o.STATE = 'SHIPPED'
     AND c.CREATION_DATE > ?

微服务架构里我们不能对CUSTOMER和ORDER表进行关联操作。
因为每个表分属于不同的服务都有自己的通过服务API的访问方式。

这就要求我们使用CQRS模式
分拆应用程序位两部分:一部分是Command内容,它用于处理命令(HTTP POSTs,PUTs,DELETEs等)
来创建,更新和删除构造块记录。 这些构造块都是使用事件源实现。
另一部分是Query部分,它负责处理HTTP GETs,通过查询一个或者多个具体构造块的视图来处理HTTP GETs请求。
查询端通过订阅命令端发布的事件来保持构造块和视图同步。

查询端无论是用什么类型的数据库查询都必须支持。
查询端视图存储:
1、基于主键PK的查找返回JSON结果:使用基于Document的数据存储,比如MongoDB或者基于键值对存储Redis
    比如通过维护一个MongoDB Document来为每个客户创建订单历史。
2、基础查询返回JSON数据结果:文本存储数据库MongoDB,比如实现客户视图
3、全文检索:基于文本的搜索引擎Elasticsearch, 比如通过维护一个每订单Elasticsearch 文档实现全文查询。
4、图形搜索:使用图形数据库Neo4j, 通过维护一个客户,订单和其它数据的图谱来实现反欺诈决策。
5、传统SQL报表/BI: 关系型数据库, 用于标准的业务报表和分析

大多情况下,CQRS是基于事件的,使用关系型数据库作为记录系统和全文检索引擎,比如Elasticsearch, 来处理文本查询的广泛使用的方案
CQRS可以使用的数据库范围比较广泛,不仅仅是文本查询引擎,通过事件预定机制可以在查询端视图中近乎实时的更新数据。

Customer View Service预订了Customer 和 Order 通过Command端发布的事件,它消费事件并更新使用MongoDB
保存的视图。 服务维护了一个MongoDB文档集合,每个文档是一个客户。
文档的属性都是客户的细节。它还有属性存储客户最近的订单。
集合支持各类查询。

CQRS的优缺点:
主要优点在于解决了在微服务架构里进行查询问题。
另一个优点在于命令和查询分离。

缺点在于 开发和运维系统需要更多的努力。
 

转载于:https://my.oschina.net/u/924064/blog/900141

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值