ENode框架Conference案例分析系列之 - 架构设计

Conference架构概述

先贴一下Conference案例的在线地址,UI因为完全拿了微软的实现,所以都是英文的,以后我有空再改为中文的。

前一篇文章介绍了Conference案例的上下文划分和领域模型的设计思路,本文想介绍一下Conference案例的架构设计。我做Conference案例的出发点是为了给大家展示如何使用ENode框架来开发DDD+CQRS+ES+EDA风格的应用程序。所以,Conference案例的架构自然就是使用这个架构了。下面我展开来说一下:

  • DDD:是软件设计的一种方式,出发点是通过领域模型封装业务逻辑和业务规则,解决领域内的复杂业务问题;
  • CQRS+ES:命令查询分离的架构,通过将命令查询分离,做到处理业务逻辑的部分和查询的部分可以分离,方便两部分可以各自发展,不受对方约束;CQRS的实现方式主要有两种:1)共享存储的CQRS,即背后的数据库存储是一份,数据是一份,只是在代码、架构层面做到命令和查询逻辑的分离。这种方式很好的利用了CQRS的思想,同时也不会有数据一致性的问题,因为是共享存储的,所以CQ两端不需要有消息通信,大部分应用程序用这种方式即可。2)存储分离的CQRS,这种方式主要用ES(Event Sourcing,事件溯源)技术来彻底实现CQ两端的完全分离,这种架构比较复杂。C端不存储对象的最新状态,而是存储对象产生的所有事件;让我们要还原一个对象时,通过ES的方式来还原。然后Q端通过订阅C端产生的事件来更新读库。这种架构是一种EDA的架构,CQ两端需要通过事件来进行联系,所以是一种面向最终一致性思路的架构。那这种方式有何好处呢?它和前面我说的第一种方式的CQRS架构,我觉得主要的好处是,我们可以设计一套框架,帮我们从架构层面解决并发问题、消息的幂等处理问题;同时结合in-memory, group commit等技术,还能大大提高系统的吞吐量以及抵御高峰的能力(因为消息可以堆积);从而可以让开发人员不用关心技术问题,专心实现业务逻辑和设计业务流程即可。而共享存储的CQRS架构,是需要我们每次Command修改完聚合根之后,需要主动保存(可能通过ORM实现)聚合根的状态的。总之两种实现方式各有优缺点,关于CQRS架构的更多介绍,我博客里已经写过很多文章了,有兴趣的朋友可以进一步看看。
  • EDA:这是一种事件驱动的架构,他和SOA架构属于同一个层次;EDA是事件驱动的思想,即B订阅A产生的消息来进行主动响应的思路;而SOA是一种面向服务然后通过服务之间相互调用(RPC)的思想;这是两种不同的架构风格,我们在不同的场景会使用不同的方式。ENode实现的是EDA架构,面向的是最终一致性。ENode在很多方面都体现出了EDA的思想。比如:
    1. ENode规定,一个Command不能同时修改两个聚合根,必须通过事件驱动的方式,先修改一个聚合根,然后该聚合根产生事件,然后另一个聚合根订阅响应事件,再修改自己的状态,从而实现两个聚合根之间的交互。这个思路 背后的原则是:聚合内强一致性、聚合之间最终一致性。
    2. CQRS两端,也是最终一致性,通过事件来同步数据。C端产生的事件通过MQ被Q端订阅,然后Q端更新自己的读库,从而实现数据的最终一致性;
    3. 两个BC(Bounded Context,上下文)之间的数据传递,也是基于消息驱动的思想。比如支付上下文在完成支付后,会产生一个消息,然后订单上下文订阅响应该消息,实现上下文之间的交互。

ENode架构图

也许有人没看过ENode架构图,呵呵。我这里再贴一下,谁如果要进一步了解ENode的架构设计,可以看我博客中的其他关于ENode框架的介绍文章。

Conference项目结构介绍


 

 

 


前篇文章中我们了解到,Conference案例共有三个上下文,分别是:会议管理(ConferenceManagement)、订单处理(Registration)、订单支付(Payment)。关于这个案例,微软的实现和我的实现有所不同,但上下文的划分是一致的。微软的实现中,三个上下文用的技术架构是不同的。

  • ConferenceManagement,由于只是后台管理系统,业务逻辑相对不是很复杂,所以,采用的是普通的三层结构;
  • Registration,由于是整个系统的整个Conference系统的核心,业务逻辑和流程都比较负责,所以使用的是CQRS+ES的架构;
  • Payment,由于Conference这个项目只是一个案例,所以其实没有真正实现支付的功能,只是简单示范了一下功能,所以这个上下文的业务逻辑也不太复杂。但是由于访问量也比较大,每个订单都会需要支付,所以也是采用的CQRS的架构,但是因为业务逻辑不复杂(不需要在存储层面也分离),所以没有采用ES技术。Payment聚合根里产生事件,最后Repository保存聚合根的最新状态,然后通过事件总线发布事件,通知Registration上下文订单支付结果。

上面我简要分析了一下微软的实现。我觉得微软的实现还是非常有参考价值的,因为它充分展示了DDD中不同的BC可以采用不同的技术架构实现,然后通过EDA的整体架构,来实现3个BC之间的数据交互。非常棒。

下面我说一下ENode实现的Conference案例。

前面说过,本案例主要是为了展示ENode框架的使用,所以我给这3个BC都是使用ENode框架实现,所以每个BC都是采用的DDD+CQRS+ES的技术架构。由于有ENode框架的支持,所以代码实现还不算复杂,可以让开发只需要专注于业务逻辑的实现即可,不需要关心消息传递,消息不丢失,消息幂等处理,并发问题,C端数据持久化等技术问题。这些技术问题如果没有框架支持,要由应用开发人员自己实现,是很有难度的。通过这个案例实践下来,基本可以证明ENode框架是可以被使用来开发出一个可实际使用的项目的,这点我目前很有信心。

采用ENode框架开发一个BC的实现的时候,我们一般需要定义以下的一些工程: 

  • Commands,定义所有的命令;
  • CommandHandlers,定义所有的CommandHandlers;
  • Domain,就是对应DDD领域层;
  • ReadModel,表示CQRS的Query Side的实现,主要包括query service的实现,以及event handler用户更新读库;
  • Messages,定义了当前BC所有对外的消息,其他BC可以订阅这些消息;消息都是DTO。
  • MessagePublishers,该项目的职责是订阅当前BC内部的Domain Event,然后转换为Message,然后把Message发布出去,从而实现通知到其他的BC。之所以要定义出Message这种DTO,是因为,我认为DDD中的Domain Event最好不要跨BC,因为Domain Event是属于当前Domain的东西,Domain Event中可能会包括当前Domain里定义的各种值对象;如果直接发布到其他BC,就会导致BC的边界不清。
  • Repositories,这种工程的作用就是实现Domain里可能定义的仓储接口。为何使用ENode框架后,还需要定义仓储接口?因为有些情况下,我们需要有一些二级索引的检查,比如创建会议时,我们要判断会议的某个属性是唯一的,但是ES不支持二级索引,而我们又不能通过查询读库来判断唯一性。所以我们需要在C端设计一些存储聚合根索引信息的仓储,用来支持二级索引。左边的图里我把项目名称命名为Repositories.Dapper,说明这个仓储是用Dapper来实现的。如果我们用EF来实现,那可以命名为Repositories.EF。
  • ProcessManagers,这种项目是用来承载CQRS架构中的Saga的,即流程管理器。Saga的作用是对业务流程进行建模。Saga的原理也是事件驱动,一个Saga会响应事件,然后发送命令。通过事件+命令串联的方式实现事件驱动架构的业务流程。Saga(ProcessManager)是无状态的,所有的状态应该都在聚合根里。这点我和微软的Saga的设计也是不同的,有兴趣的朋友可以看一下微软的实现。

另外,上面介绍了单个BC内部可能出现的项目,这些项目是我通过做这个案例后总结出来的觉得可以给开发者做参考的相互划分方式。大家如果觉得这样的方式不好,可以自己决定如何划分。通过上面的划分,我们的顶层Web项目只需要依赖简单的Commands,ReadModel这两个项目,就可以实现命令的发送和读库数据的查询了。而BC之间的交互,另一个BC只需要依赖当前BC的Messages项目就行,做到了最小的依赖。

最后介绍一下剩余的几个顶层项目:

Conference案例有两个Web项目,分别为用户提供Conference的后台管理和前台订单预定的Web界面。这个不需要多解释了应该。然后,还有3个ProcessorHost项目。这3个项目分别是3个BC负责处理后台业务逻辑的顶层宿主项目。它们只需要用控制台应用或者Windows服务的方式启动即可(案例里的实现同时支持这两种方式的启动,会自动识别当前的应用程序类型)。这3个后台服务,它们都是从EQueue订阅消息,然后处理消息的方式,实现自己的功能。所以它们的唯一数据来源就是EQueue。那EQueue消息队列的服务端是哪个呢?就是最下面的MessageBroker,这个项目承载了整个系统的消息中心,也就是EQueue的Broker。所有的消息都会发送给MessageBroker,然后相关的ProcessorHost订阅相关的Topic,实现消息的消费。

结尾

下一篇文章打算从代码的角度,以创建一个会议为例,从前台Controller到最后更新读库的整个代码链路简单介绍一下,方便读者能对实现某个功能要写哪些代码先有一个清晰总体的认识。

时间: 2024-10-30 16:24:43

ENode框架Conference案例分析系列之 - 架构设计的相关文章

ENode框架Conference案例分析系列之 - 复杂情况的读库更新设计

问题背景 Conference案例,是一个关于在线创建会议(类似QCon这种全球开发者大会).在线管理会议位置信息.在线预订某个会议的位置的,这样一个系统.具体可以看微软的这个项目的主页:http://cqrsjourney.github.io. 然后我们设计了一个Conference聚合根,对应领域中的会议这个领域概念.Conference聚合根下面,有一些位置信息SeatType.一个会议聚合根下面可以添加不同类型的位置,每种类型的位置可以指定数量以及价格.所以,Conference是聚合根

ENode框架Conference案例分析系列之 - Quick Start

前言 前一篇文章介绍了Conference案例的架构设计,本篇文章开始介绍Conference案例的代码实现.由于代码比较多,一开始就全部介绍所有细节,估计很多人接受不了,也理解不了.所以,我先进行一次QuickStart的介绍,即选取某个简单典型的场景从前到后过一下每个环节.这样大家就能够快速对代码的重要关键环节有大概的理解.另外,我现在正在做ENode的官网,到时会像axon framework一样,介绍ENode框架本身.使用场景.性能数据.案例,以及论坛社区等功能: 本文打算选择Conf

ENode框架Conference案例分析系列之 - 上下文划分和领域建模

前面一片文章,我介绍了Conference案例的核心业务,为了方便后面的分析,我这里再列一下: 业务描述 Conference是这样一个系统,它提供了一个在线创建会议以及预订会议座位的平台.这个系统的用户有两类:1)客户,可以创建和管理会议:2)会议座位预定者,可以预订会议座位.具体的关键业务描述如下: 客户创建一个会议,并录入会议的基本信息,比如名称.时间段.地点,等:会议创建后,系统会为客户自动生成一个AccessCode,客户可以通过AccessCode访问自己创建的会议: 客户定义某个会

ENode框架Conference案例分析系列之 - 业务简介

前言 ENode是一个应用开发框架.通过ENode,我们可以方便的开发基于DDD+CQRS+EventSourcing+EDA架构的应用程序.之前我已经写了很多关于ENode的架构以及设计原理的文章,但是因为没有和具体的例子结合来进行分析,所以可能很多人还是无法理解ENode的功能和设计.所以,接下来,我想通过一个较为完整的案例来一步步从业务分析到领域模型设计再到代码实现,以案例的方式讲解ENode如何帮助我们落实DDD的编码实现. 本文是这个系列的第一篇,所以需要先介绍这个案例的一些业务. 前

enode框架入门:开篇

前言 今天是个开心的日子,又是周末,可以安心轻松的写写文章了.经过了大概3年的DDD理论积累 ,以及去年年初的第一个版本的event sourcing框架的开发以及项目实践经验,再通过今年上半年利用业余时 间的设计与开发,我的enode框架终于可以和大家见面了. 自从Eric Evan提出DDD领域驱动设计以来已 经过了很多年了,现在已经有很多人在学习或实践DDD.但是我发现目前能够支持DDD开发的框架还不多,至少 在国内还不多.据我所知道的java和.net平台,国外比较有名的有:基于java

日本运营商数据业务发展策略案例分析报告

易观国际近期发布<运营商数据业务海外http://www.aliyun.com/zixun/aggregation/7734.html">案例分析系列-日本运营商数据业务发展策略案例分析报告>数据显示,日本移动互联网市场启动时间较早,且发展极为迅速,在2000年其移动互联网用户总数就已经超过了全国移动用户总数的50%.以NTT DoCoMo为例,截至2007年底,其移动互联网用户数达到4757万,占其总用户数的90.41%,稍高于国家整体移动互联网用户占比.易观国际分析认为,日

enode框架入门:事件驱动架构(EDA)思想的在框架中如何体现

开源地址:https://github.com/tangxuehua/enode 上一篇文章,我给大家分享了我的一个基于DDD 以及EDA架构的框架enode,但是只是介绍了一个大概.接下来我准备用很多一篇篇详细但不冗长的文章介绍每 个点.尽量争取一次不介绍太多内容,但希望每次介绍完后都能让大家知道这个小点的设计思想,以及为了解 决的问题. 好了,这篇文章,我主要想介绍的是EDA思想在enode框架中如何体现? 经典DDD的基 于领域服务的实现方式 一般的应用程序,如果一个用户动作会涉及多个聚合

案例分析:基于消息的分布式架构

原文:案例分析:基于消息的分布式架构 美国计算机科学家,LaTex的作者Leslie Lamport说:"分布式系统就是这样一个系统,系统中一个你甚至都不知道的计算机出了故障,却可能导致你自己的计算机不可用."一语道破了开发分布式系统的玄机,那就是它的复杂与不可控.所以Martin Fowler强调:分布式调用的第一原则就是不要分布式.这句话看似颇具哲理,然而就企业应用系统而言,只要整个系统在不停地演化,并有多个子系统共同存在时,这条原则就会被迫打破.盖因为在当今的企业应用系统中,很难

《容器技术系列》一1.4 Docker运行案例分析

1.4 Docker运行案例分析 1.3节着重介绍了Docker架构中各个模块的功能,学完后我们可以对Docker的架构有一个宏观的认识.熟悉一款软件,研究一个系统,从静态的角度认识架构的各个模块,仅仅是第一步:从动态的角度,掌握软件或者系统的运行原理,即熟知架构中模块间的通信逻辑,无疑会让自己对软件或系统的理解更上一层楼.本节将从实际的Docker运行案例出发,串联Docker各模块,从而学习Docker的运行流程.分析原型为Docker中的docker pull与docker run两个命令