从架构上看,Microsoft NLayerApp对“复杂的业务系统应用程序”这样一种应用程序的架构设计提供了一系列的设计准则。所谓“复杂的业务系统应用程序”是指这样一类业务系统应用程序,这类应用程序具有相对较长的生命周期,在其生命周期中,将发生一些可预期的“革命性变更”(比如,所使用的技术/框架的版本升级甚至替换),因此后期维护会变得非常重要。于是,针对这种类型应用程序的设计,我们应该做到,当“革命性变更”来临时,将这种变更对应用程序其他部分的影响减少到最小程度,例如,我们要确保基于基础结构层的设施变更不会影响到其上层的各个部分。更确切地说,应用程序的领域模型部分应该只关注领域本身,变更应用程序的其它部分,不会影响到领域模型。在“复杂的业务系统应用程序”中,业务规则的行为方式(也就是“领域逻辑”)将会是经常变化的,因此,使其具有很好的可修改性和可测试性将会非常重要。要达到这样的效果,就需要实现领域模型部分与系统其它部分的解耦。作为领域驱动设计(DDD)的一部分的面向领域的多层分布式架构,关注的就是这样的问题。
还是那句话,DDD不仅仅只是架构+模式。DDD是开发应用程序的一种方式,是团队在项目中工作的一种方式。根据DDD,项目团队需要以一种特殊的方式进行合作,应该能够直接与领域专家(通常就是客户)进行沟通。整个团队需要使用“通用语言”这一能够被所有人接受的语言,等等。然而,本案例没有包含这些内容,因为这是一种“过程”,一种ALM。所以,如果你真的希望100%实践DDD,你需要阅读Eric Evans写的《领域驱动设计-软件核心复杂性应对之道》一书,或者其它的一些讨论DDD过程的书籍,这些书籍会对DDD过程、项目和团队做比较详细的介绍,而不仅仅是谈论架构和模式。架构和模式仅仅是DDD中很小的一部分,而我们在这里展示的就恰好是这一部分。总之,我们必须强调,DDD不仅仅是架构+模式。
在社区中,不少朋友觉得DDD风格的架构模式(经典的也好,CQRS也好)在实际项目的应用与推广中存在一定的问题,比如从老系统向新系统的过渡过程中,DDD风格架构很难找到切入点,再比如,基于CQRS架构的应用系统难度较大,复杂度高,普通的开发人员很难在短期内掌握相关知识,一旦出现团队人员流动,新加入的团队成员将在短期内无法胜任开发职位,对项目本身造成影响。不少朋友都在关注领域驱动设计以及CQRS架构,或许也从我的博客系列文章中受到了启发,于是希望能够在实际工作和项目中能够应用DDD的理念和技术进行开发,但却在应用的过程中遇到了重重阻碍,最后不得不放弃。对于我来说,我专注于.NET技术以及DDD,我只不过是一直在探索某一种类型的应用程序的架构方式,并试图将这种设计思想和架构风格展示给大家。注意这里“某一种”的措辞,DDD风格架构并不适用于所有的实际项目和应用系统,就软件团队本身而言,推行DDD的开发过程也非一朝一夕之事。架构师们需要对项目实际情况进行分析,这包括应用系统的架构本身,以及团队建设的各方面因素(比如,团队是否能够首先引入Agile开发过程,进而去适应DDD的开发模式,等等)。DDD过程以及DDD风格的架构模式只不过是摆在您面前的又一个选项。接下来的介绍或许能够帮助您更准确地做出抉择。
不选用面向领域的多层分布式架构(DDD风格架构)的理由
如果应用程序相对简单,而且在其生命周期的整个过程中,基础结构所使用的技术和框架以及业务逻辑层等各方面都不会有太大的变更,那么你就不需要选用基于DDD的多层分布式架构。你可以选择一些RAD(Rapid Application Development)的技术,比如WCF RIA Services或者Visual Studio Lightswitch等,它能使得开发简单的应用程序变得非常高效。这些简单的应用程序关注的是Time to Market(TTM),而对于合理的结构、分层解耦等概念却并不是很关注。通常,我们把这样的应用程序成为“数据驱动的应用程序”。
选用面向领域的多层分布式架构(DDD风格架构)的理由
如果你希望你的应用程序在较长的一段时间内都能够适应业务逻辑的变化,那么,强烈建议你选用面向领域的多层分布式架构。在这种情况下,领域模型将降低由业务逻辑变化而引起的高额代价,组件之间、层与层之间低耦合的结构,使得在每次出现业务逻辑变更的时候,你都能够将领域模型隔离出来进行调整和测试,而不需要更改应用程序的其它部分,这样有效地降低了需求变更带来的开发风险,并节省了项目开支。
分布式DDD(Distributed DDD, DDDD)
这个概念是Microsoft NLayerApp在其Guide Book中提及的,就是在DDD风格架构的基础上,将分布式的特性包含进来。在Eric Evans的《领域驱动设计-软件核心复杂性应对之道》一书中,他并没有提及太多的有关分布式技术的内容(比如Web Service技术等),主要也是因为针对DDD的讨论本身也是立足于Domain的。然而,在实际的应用程序实现和部署过程中,分布式技术是必不可少的。事实上,Microsoft NLayerApp是面向分布式DDD的,在实现“分布式”的过程中,采用了微软特有的技术,比如WCF等。DDDD也使得应用程序能够更好地适应分布式场景,甚至可以使应用程序更方便地部署到云计算的环境中。
面向领域的多层架构
早在《EntityFramework之领域驱动设计实践 (二):分层架构》一文中,我就对基于DDD风格的分层架构做了介绍。现在回顾一下,DDD风格架构主要分为四层:表现层、应用层、领域层和基础结构层:
现在让我们来对比一下Microsoft NLayerApp的架构分层方式。在Microsoft NLayerApp的Guide Book中提供了下面的分层架构图,其分层方式在大体上与上文所述基本相同,同时,下图还对各个层的内部做了细化描述,便于读者能够更清楚地了解到每个层所包含的组件及其之间的协作方式。
了解整个项目的整体架构对于理解整个系统的运作方式有着很大的帮助。下面,我将对上述架构中的每个层进行介绍,让我们看看这些层都包含了哪些组件,以及这些组件是如何协作的。
- 表现层(Presentation):该层的主要职责是通过用户界面向用户展示必要的数据信息,同时接收用户的反馈。该层中的组件主要实现了与图形界面、用户操作捕获、数据转发等用户界面功能。建议根据项目的实际情况,选用相关的模式(比如MVC、MVP或者MVVM等)将这些组件细分到更小的层中。
- 分布式服务层(Distributed Service Layer):当应用程序以服务提供商(Service Provider)的方式向其它远程应用程序提供业务功能时,或者应用程序的客户端本身是被部署在另一个远程位置时,其业务逻辑就必须通过分布式服务层向外界发布。分布式服务层(通常被实现为Web Service)可以根据可配置的通信通道与数据消息格式,为应用程序提供远程访问的功能。需要注意的是,分布式服务层中不应该包含任何业务逻辑的实现。
- 应用层(Application Layer):应用层用于协调领域模型与其它应用组件的工作,以完成一个特定的、明确的系统任务。这种协调可以包括:事务调度、UoW(Unit Of Work,PoEAA)的执行,以及调用一些系统必须的处理任务等。应用层同时还可以包括应用程序的优化、数据的转发和格式转换等工作,当然,我们将这些工作统称为“任务调度”,至于每个任务的核心部分,应用层都会将其转发到下层去处理。应用层通常会被看做是一种“业务层外观(Business Facade)”,但它却不仅仅是转发领域模型层的处理请求/反馈那么简单。它通常可以包含下面这些内容:
- 通过仓储契约(Repository Contract)来访问持久层机制,以读取或保存领域对象。注意这里访问的是仓储契约,而并非仓储的具体实现。仓储的具体实现是基础结构层的内容
- 对来自于不同领域对象的数据进行组织和整理,以便能够让分布式服务层更有效地传递这些数据。通常,我们会将数据整理在数据传输对象(Data Transfer Object,PoEAA)中,例如WCF的Data Contracts
- 管理和维护应用程序的状态(而不是领域模型中领域对象的状态)
- 协调领域对象之间、领域模型与基础结构层组件之间的协作关系。比如在银行转账系统中,资金从一个账户转移到另一个账户,首先需要通过仓储读取“账户”领域对象,然后在领域对象上进行转账操作(可以是“账户”本身的行为,也可以是,按Evans的举例,使用领域服务(Domain Service))。或许在完成转账后,无论成功与否,都需要向外发送电子邮件,这就需要基础结构层的电子邮件组件协作完成
- 应用服务(Application Services):首先需要注意,DDD中提到的服务与平时所说的Web Service等并不是一个概念,它可以存在于应用层、领域模型层甚至基础结构层。DDD中Service所表述的概念,其实是“无法归结到任何一个对象”的一系列操作的集合,因此,Service通常是在协调不同对象之间的工作。应用服务也是如此,它会对其它下层组件(比如领域模型层与基础结构层)进行协调
- 业务工作流(Business Workflow):业务工作流并非必须的,对于某些由特定步骤组成的业务过程,引入业务工作流会使问题变得简单
- 领域模型层(Domain Model Layer):该层的主要职责是展现业务/领域逻辑、业务处理状态,以及实现业务规则,它同时也包含了领域对象的状态信息。这一层是整个应用程序的核心部分,它可以包含下面这些概念和内容:
- 实体(Entities)
- 值对象(Value Objects)
- 领域服务(Domain Services)
- 仓储契约/接口(Repository Contracts/Interfaces)
- 聚合及其工厂(Aggregates and Factories)
- 聚合根(Aggregate Roots)
- 规约对象(Specifications)
- 基础结构层(数据持久化部分)(Data Persistence Infrastructure Layer):该层为应用程序的数据存取提供服务,它可以是应用程序本身的持久化机制,也可以是外部系统提供的数据访问的Web Service等。根据分层架构的设计原则,该层应该以“低耦合”的方式向上层提供数据持久化服务。因此,该层可以包含如下这些内容:
- 仓储的具体实现:从概念上看,“仓储”意味着对一组相同类型对象的集中管理,就好像是存取同一类型对象的仓库。然而在实践中,仓储主要用来在特定的持久化机制/技术上执行对象的读取和保存操作。这些持久化机制/技术可以是Entity Framework、NHibernate或者是针对某一数据库引擎的ADO.NET组件。为了简单起见,我们将数据访问操作集中到仓储中,并针对不同的持久化机制/技术开发一个仓储的具体实现,这将会对应用程序的维护和部署带来便捷。在设计仓储时,通常的做法是,首先对领域模型划分聚合并区分聚合根,然后针对每一个聚合设计一个仓储,仓储通过聚合根对聚合进行管理。在领域模型层中,各组件是通过仓储契约(接口)来实现对仓储的访问的,这样做就使得领域模型层无需了解任何仓储的具体实现和持久化细节(Persistence Ignorance),读者可以参考我前面写的《EntityFramework之领域驱动设计实践(八)》一文。此外,我们通常所讲的“数据访问对象(Data Access Object)”并不是仓储,首先,仓储通过聚合根,负责整个聚合的读取和存储,它是一个领域概念,而数据访问对象则是对单个对象(更确切地说应该是单个数据结构)直接进行数据库操作;其次,操作方式也不同,仓储会在提交前先对内存中的对象进行标记,最后的一次提交过程(Unit Of Work,PoEAA)则是在上层组件(比如在应用层)中完成的
- 层超类型(Layer Supertype,PoEAA):通常,在实现某层的特定功能时,我们会将一系列对象的公共逻辑提取出来,然后将这些逻辑置于一个抽象类型中,同时使得其它类型都继承于该抽象类型以避免逻辑重复。这样的抽象类型被称为层超类型。大多数数据访问任务可以使用层超类型以简化开发,减少代码维护成本。例如,在实现面向ADO.NET的数据库访问组件时,我们可以在层超类型中使用DbConnection、DbCommand等对象实现公用逻辑,然后在子类中继承这些逻辑并提供具体的SqlConnection、SqlCommand或者OleDbConnection、OleDbCommand实例
- 数据模型(Data Model):如果使用ORM来实现仓储,那么通常情况下ORM都会使用一个数据模型(比如Entity Framework)来实现需要的功能,这样的数据模型有点像实体模型,但它与数据传输对象一样,跟领域模型层的实体模型是完全不同的。数据模型甚至是一种可视化的图形描述,由专门的可视化设计工具负责维护
- 远程/外部服务代理:当采用外部系统来实现数据持久化机制时,远程/外部服务代理负责连接外部系统并转发数据操作请求及响应信息
- 基础结构层(Cross-Cutting):该层提供了能被其它各层访问的通用技术框架,比如异常捕获与处理、日志、认证、授权、验证、跟踪、监视、缓存等等。这些操作通常会横向散布在应用程序的各个层面,我们平时讨论的面向方面编程(AOP)关注的就是如何在不影响对象本身处理逻辑的基础上来实现这些横切的却又必不可少的功能点。在实践中,通过使用一些流行的Interception框架(例如Microsoft Unity、Castle DynamicProxy等)可以帮助我们方便地实现AOP
总结
本文以文字描述为主,结合Microsoft NLayerApp项目,更详细地对DDD及其分层架构做了介绍,文中也引入了不少来自于Martin Fowler《企业应用架构模式(PoEAA)》一书中所介绍的概念与模式名称,帮助读者朋友更好地理解DDD分层架构中各层的主要职责。与《Microsoft NLayerApp案例理论与实践 - 多层架构与应用系统设计原则》一文一起,通过这两篇文章的学习,我们已经对应用程序的设计与架构,以及DDD风格架构及其分层有了一定的了解。从下章节开始,我们会把本文所描述的分层架构与Microsoft NLayerApp项目结合起来,进一步学习Microsoft NLayerApp项目的具体实现。
摘要: .NET直接提供对MSMQ的访问支持,只需要添加System.Messaging程序集引用即可方便地操作MSMQ。MSMQ支持两种事务处理模式:内部事务处理以及基于MS-DTC的分布式事务处理。MSMQ的内部事务处理MSMQ的内部事务处理是指,仅采用MSMQ本身提供的事务处理机制完成事务处理。比如,假设有一系列的消息需要发布到MSMQ,那么,就可以启动一个内部事务,确保这些消息的发布过程是一个原子操作。要使用MSMQ的内部事务处理机制,在创建消息队列的时候,就需要勾选“事务性”选项,如下图所示:首先,需要创建一个MessageQueueTransaction的对象,并使用Begin调用以启动M
阅读全文
摘要: 在继续实现Apworks框架的过程中,发现一个必须解决的问题,就是分布式事务处理。它要求两个原本相对独立的工作能够在同一个事务上下文中完成处理。如果处理成功,则两者同时提交,否则,两者同时回滚。Apworks框架需要依赖分布式事务来解决二次提交(2PC)的问题,这个在我之前的博客文章中也提到过,简单地说,就是领域事件的存储和发布必须是一个原子操作。在此,我打算使用2-3篇文章的篇幅对.NET下分布式事务处理的实现做个简单的总结,其中并不会涉及到有关分布式事务的原理/理论方面的内容,仅仅是对其实现方式做个记录。首先需要了解到,虽然.NET分布式事务在一定程度上能够解决Apworks框架中2PC的
阅读全文
摘要: 在对NLayerApp实际项目进行讨论之前,让我们首先学习一下(或者应该说重温一下)分层/多层架构与应用系统设计原则。很多朋友会认为这些都是老掉牙的内容,只要是软件从业人员,都会对这些内容非常熟悉。然而,果真如此吗?我在这里整理这部分内容,一方面是为介绍NLayerApp打下基础,而另一方面,则是希望借此机会将这些理论性的东西做个归纳,也希望读者朋友能够认真阅读,毕竟温故知新嘛。 需要说明的是,从本章节开始,大多数理论性的东西都源自Microsoft Spain团队针对NLayerApp所编写的《Architecture Guide Book》,事实上这本Guideline的英文版至今也还没有
阅读全文
摘要: 项目简介Microsoft – Spain团队有一个很不错的“面向领域多层分布式项目”案例:Microsoft – Domain Oriented N-Layered .NET 4.0 App Sample(在本系列文章中,我使用NLayerApp作为该项目的名称进行介绍),在codeplex上的地址是:http://microsoftnlayerapp.codeplex.com/。它是学习领域驱动设计(DDD)的一个非常不错的案例项目。该项目采用的是经典的DDD架构,而不是CQRS架构,但我觉得整个案例做的非常不错,基本上包含了基于DDD的架构实践的各个方面。因此,应不少社区朋友的要求,我打
阅读全文