领域驱动设计(DDD)的实践经验分享之分层架构(附源代码)
以前做了个简单的论坛,但是之前的版本都没有考虑过架构设计。所以想在第三个版本中应用分层架构+DDD+EDA架构重新设计一下我的论坛。经过半年的努力,终于整出了一个让自己比较满意的架构了,但是也仅仅是一个Demo,还不能真正使用,但对于说明架构设计已经足矣。源代码下载地址:http://files.cnblogs.com/netfocus/ProductName.rar
由于本人接触领域驱动设计的时间还很短,对于如何设计领域对象还没有丰富的经验。所以我希望大家看了我的源代码中的领域层中的领域对象后不要笑话我,呵呵。因为我本文主要想向大家展示的是我对分层架构的思考和自己的想法(我想大家自己的想法才是最重要的吧),我本人也很注重思想、思考,不要老是讨论别人的老外的架构有多好多好,我们要有自己的思想。所以大家在看我文章和源代码时不能过分专注于一点上,而应该从总体上来看待。
开发工具:Visual Studio 2010
项目结构图:
说明:
1)关于项目的命名规则。假设现在有一个公司要做一个项目,我觉得比较好的项目命名方式为:以CompanyName.ProductName作为前缀,基础类库命名为Common,产品中的某个子应用模块,则可以命名为CompanyName.ProductName.Modules.Forum,CompanyName.ProductName.Modules.Blog,等。然后每个模块还可以根据模块的分层设计分出不同的Project,比如论坛的应用层可以命名为:CompanyName.ProductName.Modules.Forum.ApplicationService,等。由于我做的只是一个展示架构的Demo,所以没有用具体的CompanyName,ProductName。我觉得在开发阶段我们可以不使用最后的名字,到了最后项目快完成时再做统一全局替换即可。
2)架构设计介绍。
一个经典的基于领域驱动设计(Domain Driven Design) 的应用,分为下面的层次:
我的项目也同样采用上面的分层架构,对于上面每一层的功能和职责我想大家应该都比较清楚了。我主要想和大家分享一下基于这样的分层架构下我的实现方式:
先来看一下各个层中包含的主要元素。
说明:
界面层(User Interface or Presentation Layer):没有什么特别的,因为我是采用ASP.NET MVC来最为界面层,所以会包含Page、Control、Controller、View Object(VO)。前三个大家都很熟了,至于View Object,相当于Page或Control的Model,负责提供数据给界面,以及保存界面录入的数据。View Object还有一个很重要的功能就是会对界面提交的数据做简单的数据有效性验证,但不会包含任何的业务逻辑验证。任何业务逻辑验证会由领域层来完成。界面层只会和应用层或基础层打交到,不会和领域层有任何关系。界面层的任何查询或行为都通过应用层实现,界面层通过构建应用层中的某个Request,然后发送到应用层,应用层完成相应操作或查询后返回一个Reply给界面层。如果是查询的操作,则Reply中会包含一个或多个DTO,界面层将DTO转换为VO,然后显示。
应用层(Application Layer):真正意义上的非常薄的一层,该层由一些应用服务来完成所有功能。为了性能方面的考虑,在学习了命令查询职责分离(Command Query Responsibility Separation,CQRS)的思想后,我觉得应该将查询和命令的实现分离,这样可以对这两部分更好的进行单独设计。因此,在我的实现中,对于查询的Request,会委托给一个专门负责查询的服务去完成;对于命令的Request,会委托给一个专门负责处理命令的领域服务去完成。另外,只要是有命令处理的请求,就会最终用事务的方式来提交所有的修改;
领域层(Domain Layer):我设计的最具特色的一层。大家知道,经典的领域驱动设计,领域层会包含领域服务、实体、值对象、聚合(根)、工厂,仓储这些概念。但是在我看了一些资料和自己独立思考后发现,用这样的方式来组织领域逻辑在我看来不是太好,因为1)整个模型没有体现出事物的相互协作的特性,也就是没有消息机制;2)用不太自然的方式来组织相互作用的各个实体,比如Aggregate的概念在我看来就是一个不该有的概念。在真实的世界中,我认为最好的软件就是我们自己,那就是“人类”。想想看人类是亿万年才进化出来的地球上最高级的生物,他的内部结构绝对是一个非常好的”软件“。大家都知道人有大脑、皮肤、细胞、神经元。如果把人和一个领域模型想比较,那么人的皮肤相当于领域模型暴露给外面的Domain Service,人的细胞相当于领域模型中的各个Domain Entity,人的大脑相当于领域模型的一个”中央事件处理器“(Event Bus),人的神经元相当于领域模型中的各种消息(Domain Event);也就是说,一个真正的符合客观实际的领域模型应该包含:Domain Service+Domain Entity+Domain Event+Event Bus四个元素。不需要其他任何概念。
需要特别指出的是:
1)所有的Entity是独立的,相互平等的,没有什么聚合(Aggregate)的概念,也就没有Aggregate Root的概念。所有的Entity之间的交互应该总是通过Event来完成。而不应该通过对象引用的方式来达到相互协作的目的。以前大家都以为面向对象编程就一定要让对象之间相互引用。大家想想为什么我们要让对象之间相互引用?目的很简单就是能让对象相互影响,相互协作。那难道就没有其他方法可以达到此目的吗?当然不是,我们可以用事件,也就是用通知和响应的方式。我认为对象引用,或者说调用其他对象的方法是造成对象之间耦合的根本原因。而用发送事件和响应事件的方式才是真正体现事物交互的本质联系,比如我们人类,如果我狠狠的掐你一下,你可能会疼的跳起来,可能嘴巴还会撅起来。真个过程可以描述为:皮肤接受到外界刺激,传递到大脑,大脑作出反应(当然有时可能是条件反射),通知肌体其他部位作出相应反应(跳起来,撅嘴巴)。另外,任何一个Entity都具有发送事件和处理其关心的事件的能力,所有的业务逻辑验证都应该在Entity中实现,如果一个Entity不能实现的验证则会通过发送消息让多个Entity协作完成验证。2)领域服务应该非常的薄,它只需要做一件事情,那就是发送事件给事件处理器,仅此而已,它相当于一个圆球的表明,而里面包含的各个相互作用的实体。就如同人类的皮肤一样,人的皮肤也很薄。这点和经典的领域驱动设计中的领域服务也完全不相同。经典的领域驱动设计中的领域服务的职责是处理那些需要多个实体合起来才能完成的事情,比如银行转账。
3)中央事件处理器,其功能应该和人类的大脑一样,应该很聪明,它应该知道任何一个事件该怎么处理。也就是说它会知道该通知哪些实体去完成所有的响应操作。这个功能类似于事件驱动架构(Event Driven Architecture,EDA)中的Event Bus。
4)整个领域层应该是持久化透明的,也就是说不需要关心Entity的修改是如何被持久化到数据库的。所有的对象只需要关心自己的感兴趣的事件并处理之,修改自己的状态。仅此而已。
基础层(Infrastructure Layer): 和我们平时常说的基础层类似,包含一些抽象的接口或底层的服务,但往往设计和实现很有技术含量。这里就不做过多介绍了,具体可以查看我的源代码实现。
最后还没有提到的就是持久化透明和具体的持久化操作实现了。要讲清楚这点比较复杂,如果大家有兴趣可以看一下我写的前一篇文章,文章中提到的有些设计可能和我发布的最新源代码有一些出入,比如Repository在我最新的源代码中已经没有了。
好了,主要思想就写到这里了。我想没有什么能比你直接看源代码更直接的了。各位如果有兴趣就下载下来看看吧。这可是我用了很多时间写出来的东西。
发表评论
不错啊 楼主辛苦了