CQRS体系结构模式实践案例:Tiny Library:系统架构

写在前面

也许在阅读了上篇文章中我列出的那部分资料后,还是有很多朋友对领域驱动设计不了解。正如上文评论中有网友提到微软西班牙团队也做了一个面向领域的分布式应用系统架构的案例,地址是http://microsoftnlayerapp.codeplex.com/。在这个站点的首页上,又对领域驱动设计做了诠释,我觉得总结的很好,特地将其翻译成中文写在这里,供大家参考:

DDD is much more than this!

We're talking about complex apps…, all their business rules (Domain logic) are points that, in most of the cases, need to be changed quite a lot during the app’s life. So it is critical to be able to change the app and test it in an easy way and independently from infrastructure areas (technology, data stores, data access technolgies, ORMs, etc.). Because of that, it is important to have the Domain/Business Layer (domain logic, entities, etc.) decoupled from the other layers.
Having said so, in our opinion, the best actual architectural style that fits with those requirements is a Domain Oriented N-Layered Architecture which is part of DDD (Domain Driven Design). And this Sample-app is showing a way to implement a Domain Oriented N-Layered Architecture.
But, and this is extremely important, DDD is on the other hand, much more than just a proposed Architecture and patterns. DDD is a way to build apps, a way for the team, to work in projects. According to DDD, the project’s team should work in a specific way, should have direct communication with the Domain Experts (the customer, in many cases). The team should use an ‘Ubiquitous Language’ which has to be the same language/terms used by the domain experts, etc. But, all those ways to work are not part of this Sample-App, because it is process, kind of ALM, so, if you want to really “Do DDD”, you’ll need to read Eric-Evans' book or any other DDD book where they talk about the DDD process, about the project and the team, not just the Architecture and patterns. Architecture and patterns are just a small part of DDD, but, in this case those are the points we are showing here (DDD architecture and patterns). But we want to highlight that DDD is much more than Architecture and design patterns.

 

DDD - 不仅仅是这些

我们讨论的是复杂的应用系统……,通过这些应用系统的业务规则(领域逻辑)我们可以了解到,在大多数情况下,在应用系统的整个生命周期里,业务规则是可变的一部分。因此,能够很方便地对应用系统进行变更并测试,并使其独立于基础结构层设施(比如:技术、数据存储、数据访问技术、ORM等等),就显得非常重要。正因为如此,我们很有必要将领域/业务层(领域逻辑、实体等等)从其它分层中解耦出来。

话虽如此,根据我们的建议,能够适应上述需求的架构风格就是面向领域的多层架构,但它仅仅是领域驱动设计(DDD)的一个部分。本案例展示了实现面向领域的多层架构的一种方式。但需要注意的,而且非常重要的是,DDD不仅仅只是架构+模式。DDD是开发应用程序的一种方式,是团队在项目中工作的一种方式。根据DDD,项目团队需要以一种特殊的方式进行合作,应该能够直接与领域专家(通常就是客户)进行沟通。整个团队需要使用“通用语言”这一能够被所有人接受的语言,等等。然而,本案例没有包含这些内容,因为这是一种“过程”,一种ALM。所以,如果你真的希望100%实践DDD,你需要阅读Eric Evans写的《领域驱动设计-软件核心复杂性应对之道》一书,或者其它的一些讨论DDD过程的书籍,这些书籍会对DDD过程、项目和团队做比较详细的介绍,而不仅仅是谈论架构和模式。架构和模式仅仅是DDD中很小的一部分,而我们在这里展示的就恰好是这一部分。总之,我们必须强调,DDD不仅仅是架构+模式。

很多网友对什么是DDD有很大争议,我想,看了上面的文字,问题应该大致清楚了。也在此借用这段文字,说明我研究学习DDD实践的意图。言归正传,本文将对Tiny Library CQRS(简称tlibcqrs,下同)的解决方案结构和系统架构做个简要的介绍。

 

Tiny Library CQRS解决方案结构

image

从Solution Explorer上可以看到,tlibcqrs包含9个项目,依次为:

  • TinyLibrary.CommandHandlers:命令处理器,负责处理来自客户端的命令请求
  • TinyLibrary.Commands:命令集,其中包含了tlibcqrs所用到的命令,客户端请求将转化为命令,并由命令处理器负责处理执行
  • TinyLibrary.Domain:领域模型,业务逻辑与之前系列文章中的Tiny Library一样,话不多说。不过你会发现,tlibcqrs的领域模型与经典Tiny Library的领域模型在实现上会有区别
  • TinyLibrary.EventHandlers:领域事件处理器,负责处理由领域仓储转发的领域事件
  • TinyLibrary.Events:领域事件集,其中包含了tlibcqrs中所有的领域事件。就是这些领域事件造成了领域模型中实体的状态更新
  • TinyLibrary.QuerObjects:查询对象集,负责数据的查询和传递,它是用户界面的数据映射,是一些DTO
  • TinyLibrary.Services:.NET WCF Services,包含两个endpoint:CommandService和QueryService,为用户界面层提供服务
  • TinyLibrary.Tests:单体测试项目
  • TinyLibrary.WebApp:基于ASP.NET MVC实现的用户界面

仍然说明一下,本案例没有加入太多的cross-cutting基础结构层设施,因此,诸如日志、缓存、异常处理都很弱甚至没有,朋友们不要觉得好奇,这只是一个演示,为了节约时间,我忽略了这部分内容。今后我会将其补上。

 

系统架构

tlibcqrs的系统架构就是经典的CQRS架构,之前在我的《EntityFramework之领域驱动设计实践【扩展阅读】:CQRS体系结构模式》一文中,我已经给出了整个系统的结构简图,在此再一次贴出来,以表示tlibcqrs的系统架构。

 

为了让大家看tlibcqrs源代码方便,在此特地以“创建读者”为例,将sequence描述一下。

  • Client通过TinyLibrary.Services.CommandService,向系统发送CreateReader的请求
  • CommandService获得请求,创建RegisterReaderCommand对象,并将其推送到命令总线(Command Bus)
  • 在Command Bus被提交时,它将调用RegisterReaderCommandHandler,以处理RegisterReaderCommand
  • 在RegisterReaderCommandHandler中,创建新的Reader实体,并向其发送ReaderCreatedEvent事件
  • Reader实体捕获到ReaderCreatedEvent后,根据事件中的数据,设置其自身的状态属性,同时将事件记录下来
  • RegisterReaderCommandHandler调用Domain Repository保存新建的Reader实体
  • Domain Repository从Reader实体中读取未提交的事件列表,使用DomainEventStorage将事件保存到外部持久化机制(在tlibcqrs中,是TinyLibraryEventDB这个关系型数据库)
  • 完成事件的保存后,Domain Repository将事件推送到事件总线(Event Bus),然后返回
  • 事件总线接收到事件后,使用消息派发器(Message Dispatcher)将事件发送给相应的事件处理器进行处理
  • ReaderCreatedEventHandler在接到ReaderCreatedEvent事件后,将事件数据保存到查询存储机制中(在tlibcqrs中,是TinyLibraryQueryDB这个关系型数据库),同时返回
  • 下一轮,当用户需要获取Reader信息时,直接通过TinyLibrary.Services.QueryService读取查询存储机制以获得数据

大致可以用下面的UML Sequence Diagram描述这一过程【请单击此处下载XPS文件以查看大图】:

image

 

下一讲,我会介绍一些tlibcqrs中的一些新思想,至于CQRS框架的设计,我会在另外的系列文章中详述。

posted @ 2010-12-15 10:29  dax.net  阅读(6287)  评论(13编辑  收藏  举报