本文共 4520 字,大约阅读时间需要 15 分钟。
补充:现在再回过头来看这篇文章,感觉当初自己偏激了,呵呵。不过没有以前的我,怎么会有现在的我和现在的enode框架呢?发现自己进步了真好!
从去年10月份开始,学了几个月的领域驱动设计(Domain Driven Design,简称DDD)。主要是学习领域驱动设计之父Eric Evans的名著:《Domain-driven design:领域驱动设计:软件核心复杂性应对之道》,以及另外一本Martin Flower的《企业应用架构模式》,学习到了不少关于如何组织业务逻辑方面的知识。另外,在这个过程中也接触到了一些开源的架构和一些很好的思想。如:命令查询职责分离(Command Query Responsibility Segregation,简称CQRS),事件驱动架构(Event Driven Architecture,简称EDA),以及四色原型和DCI架构,等等。前面这些知识对我来说是非常宝贵的财富,可以说我能进淘宝,很大程度上也是因为我学习了前面这些知识的原因。
在介绍我设计的框架之前,我想先探讨一下以往我们都是如何思考设计OO的系统的。大家都知道,真正的对象应该是不仅有属性,而且有行为的。并且大家也有另外一个共识,那就是为了完成某个任务,各个对象应该会相互协作共同完成这个任务。之前我们在设计一个系统时,往往会先设计好各个对象,明确他们的职责,在这个过程中,还会考虑如何建立对象之间的关系(依赖、关联、聚合、组合),在这些关系的影响下,我们会认为对象之间应该有主从关系、依赖关系,等等。然后我们所做的这些设计最终的目的是为了能让对象之间能够通过相互协作来共同完成某个任务。这种方式最核心的设计特色是,我们会通过”对象引用“的方式来实现对象之间的各种关系。这种方式很好,并且我们也已经完全习惯了从对象的职责以及它们之间的关系的角度去设计对象。但这仅仅体现了一个哲学思想,那就是“物体之间通过直接作用完成某个任务”。
我觉得任何两个对象之间的交互有两种形式:1)直接作用,即对象A引用一个对象B,然后A调用B提供的某个方法,以此来完成两个对象之间的协作;2)间接作用,对象A不引用对象B,仅仅包含了一个对象B的唯一标识,当它要和对象B协作时,会发送一个消息给对象B,然后对象B收到该消息后做出响应,从而实现两个对象之间的协作;不管是哪种方式,他们最终的效果是一致的,都可以实现两个对象之间的交互并最终完成某个任务。那么这两种方式各自的优缺点在哪里呢?我个人觉得对于对象引用的方式,其好处就是简单、直观、容易理解,很符合我们平时的设计习惯。但坏处是什么呢?我个人觉得这种方式是形成对象耦合的根本原因,对象A对对象B存在了紧密的耦合,也许你会说,在间接作用的方式下,对象A不也会保留一个对象B的唯一标识吗?没错,但要知道保留引用和保留唯一标识的耦合强度是不一样的。前者的耦合强度更大,因为持有另外一个对象的引用就意味着可以直接操作该对象,而仅仅持有另外一个对象的唯一标识则不行,必须先根据该唯一标识获取另外一个对象,然后再操作它。而对于发送接受消息的方式,它的好处和坏处是什么呢?其实正好和前者相反,即不简单、不直观、不容易理解,容易让大家觉得有过度设计的嫌疑,而好处则是能够将两个对象之间的耦合度降到最低。
好了,有了前面这些介绍之后,我想可以引出我所设计的这个框架的设计思想了。
既然在对象直接作用的思路下设计软件的各种原则、模式,以及各种最佳实践已经很多了,如SOLID五大设计原则、GRASP九大OO设计原则、Gof的23种设计模式、各种更大的模式如MVC、MVP、MVVM,等等。所以,我也就不用去费功夫去研究了,直接利用前辈的研究成果就行了。但我发现在对象间接作用的思路下设计软件的各种原则或框架似乎还不够多。当然也有很多大家都很熟悉了,比如Observer设计模式,按照这个设计模式设计出来的.NET框架中的事件和委托的机制,还有比如一些第三方的开源框架如事件总线,事件驱动架构,等等。
思考到这里,再结合自己最近不断学习DDD的背景下,我脑子里有了一个奇特的想法!那就是:是否可以搞一个事件驱动的领域模型实现框架从而可以让我们从消息和行为的角度去设计对象呢?
有了这个框架,我们可以:1)通过消息实现领域模型中各个领域对象之间的交互,或者说是通信及协作;2)通过消息实现领域模型和外界的交互,如领域模型的使用者和领域模型之间的交互,一般这个使用者是应用层;还有比如领域模型和数据持久层的交互。带着这个问题,我试图去寻找目前已有的框架来实现我的想法,但遗憾的是,我找不到,所以只能自己开发。想到这里,我其实挺担心的,因为我很有可能已经走火入魔了,因为我要走的设计道路很可能是个死胡同或不归路,或者说不是一条真正能很好的解决软件设计的路,不然我怎么会找不到这样的框架呢?但不管怎样,还是先试试再说吧!反正我的大脑放在那里不用也是浪费。就这样,带着这样的目标和思路,我开始一步步设计我的框架了。经过了三个月的设计、编码、测试、分享、讨论、重构的循环过程。到目前为止,总算初步实现了自己当初的目标,现在唯一差的就是在真正的实际项目中使用了。但幸好已经写了两个不同层次的Demo用来验证我的框架了。
该Demo包含了框架的源代码和Demo文件,基于VS2010开发,因为需要用到.NET4.0中的一些特性,如逆变和协变。源代码打开后,EventBasedDDDExample.PresentationLayer是启动项目,直接F5就可以运行。该Demo为了重点突出领域模型的设计,特意采用内存作为数据持久层,去掉了应用层,并且用控制台应用程序作为UI层,这样就方便大家运行Demo。该项目包含了四个演示的例子,前面两个例子演示了如何利用我的框架实现特定的业务场景(一个是银行转账的例子,另一个是论坛中发帖发回复删除回复的例子)。具体功能参见源代码。
该Demo是一个比较真实的项目,也是用VS2010开发。前身是我之前开发过的一个蜘蛛侠论坛,现在用我最新的框架来实现这个论坛。但由于时间有限,UI层还没开发好,但应用层、领域层、持久层已经开发好。因此大家在查看源代码时,不要去看UI层的设计,因为我还没开发好。而应该去看其他几层的设计!大家从我这个Demo中,可以看到如何将经典的领域驱动四层分层架构和我的框架集成。相信这对大家非常具有实用价值。然后关于项目的命名空间,我也要解释下。假设现在有一个公司要做一个项目,我觉得比较好的项目命名方式为:以CompanyName.ProductName作为前缀,基础类库命名为Common,产品中的某个子应用模块,则可以命名为CompanyName.ProductName.Modules.Forum,CompanyName.ProductName.Modules.Blog,等。然后每个模块还可以根据模块的分层设计分出不同的Project,比如论坛的应用层可以命名为:CompanyName.ProductName.Modules.Forum.ApplicationService,等。由于我做的只是一个展示架构的Demo,所以没有用具体的CompanyName,ProductName。我觉得在开发阶段我们可以不使用最后的名字,到了最后项目快完成时再做统一全局替换即可。
下面介绍一下我的框架的设计思想:
领域模型的组成元素:领域服务(Domain Service)+领域对象(Domain Object)+领域事件(Domain Event)+中央事件处理器(Event Processer)。
另外,领域模型与外界如何交互呢?还是通过上面所提到的事件,当外界需要领域模型做什么事情时,就发送一个在领域模型中已经定义好的事件,然后领域模型或其他人(比如持久层)就会响应该事件了。当领域模型发生了什么或想要外界提供什么数据时,也是通过发送事件,然后外界就会响应,从而为领域模型提供必要的支持,如持久化支持。通过上面的分析,似乎可以看出我们已经找到银弹了,即找到了一种单一的模式可以用来解决所有的对象交互与协作的问题了?应该不是这样,但我自己没发现不知道这种设计的问题出在哪里,所以非常期望大家能多给我些意见。还是那句话,我希望我们每个中国人都是一个不盲目相信权威并敢于怀疑权威并能积极去思考和将自己的思考转化为生产力的人,而不只是一个仅仅会使用外国人写出来的框架的人。
这篇文章说的全部是思想或思考心得,接下来我会具体分析我上面提到的两个Demo的具体设计。但我真的很希望大家能重视思想,重视自己的思考过程,并且要敢于去将自己的思想转化为具体的成果,如框架。我们来这个地球上走一趟,如果仅仅只是会用别人写出来的东西,那不是很可惜?但如果你根据自己的思想写出了几个能让别人用的东西出来,那不是非常好吗?那才是很有意义和价值的事情。
转载地址:http://eazxx.baihongyu.com/