阅读目录:
- 1.原则
- 1.1.精简聚合
- 1.2.分离用例与接口功能(设计模式的用武之地)
- 2.工具、框架、组件
- 3.过程
1】原则
原则对于任何一项技术实现来说都是至关重要的,在设计某一个系统功能的时候我们讲究的是设计原则:
【单一职责原则Single Responsibility Principle、里氏替换原则Liskov Substitution Principle、依赖倒置原则Dependence Inversion Principle、接口隔离原则Interface Segregation Principle、迪米特法则Law Of Demeter、开闭原则Open Close Principle】。
在架构设计的时候我们也讲究架构原则:
【分层原则、避免循环依赖】。
不仅仅在技术领域在做人做事都要讲究原则,违背原则那么等待你的将是无情的惩罚。
对于DDD的设计我们也有相应的原则需要遵守,当然如果不遵守在前期看不出什么区别,但是到开发阶段问题就会暴露出来。
我们来看两个基本的设计原则问题。
【精简聚合】
精简聚合的设计原则无疑是最重要的。一些软件工程方法论书籍经常指导我们进行UML业务建模,"在这个阶段不需要考虑任何技术实现问题”,我按照这样的指导原则进行了UML的设计然后顺利的创建出ER关系图,结果发现那样的数据库结构根本不能作为最终的项目开发数据库。哪里出问题了?我反复查询指导书籍后来在专业的DDD书籍上看到了一句大概这样的话:
-
【“不以技术实现为前提的设计都是纸上谈兵”】。
我想这句话很真实的描述了方法论与企业应用之间的鸿沟,很多技术思想或者理论确实很好,但是要想用起来需要解决很多问题。DDD也避免不了这个问题,怎么避免在设计UML模型的时候不会导致设计过度的问题,这里我们只需要遵守【精简聚合】原则就不会导致设计过度问题。
在前面的例子当中我们设计一个完整的UML领域模型,但是我们并没有对它进行【精简聚合】重构,所以它存在的问题就是无法进行项目开发。
1.1图
我们构建出来的领域模型初步版本应该是上图这样的,实体与实体之间是有强联系的,聚合之间的关联太大,导致牵一发而动全身。如果按照这种关系创建数据库那么数据库之间的主外键肯定很多,对数据库的设计造成了影响。这样的关系如果在程序中使用也会存在很多问题,我们无法进行少数聚合的使用,当我们使用某一个聚合的时候它会接二连三的把相关联的聚合都给拖出来,不仅在查询的时候妨碍而且在Factory创建聚合的时候也会存在无法构造的问题,不管在对聚合Repository进行任何操作的时候都会影响程序逻辑,所以我们需要对一个复杂的庞大的关系进行拆分。
将红线的部分全部断开,聚合之间通过Id进行关联,这样就会变的很清晰。因为很少程序中会在某一个业务逻辑点上需要所有的业务模型参与,这样既方便了程序的开发也方便了数据库的设计,更方便了ORM的使用。ORM的延迟加载其实就是为了聚合之间的依赖,可以在需要的时候在去查询需要的模型。但是这样虽然程序可以说的过去,那么数据库的设计就说不过去了。对于不同的ORM框架的映射原理不同,在构造模型的时候是需要稍微的调整的,比如在EntityFramework中,它能支持的映射方案你保证你的模型能顺利的映射过去,这里就不扯了后面有一个详细的项目做全面实践,到时候在具体问题具体分析。
最后我们看一下分解后的类图:
1.2图
这样一来一块一块很清晰,都能直接使用相关的核心领域模型,也不需要担心ORM框架的延迟加载的问题。
【分离用例与功能接口(设计模式的使用之地)】
分离用例与功能接口其实也是初次接触DDD的朋友都会犯的职业病,因为我们都熟悉面向对象设计。在进行UML建模的时候我们都非常喜欢抽象,会很清楚的把具有泛化关系的用继承来表示,比如【用户类型】,不同的用户具有不同的行为权限,在初步设计的时候我们一般都会建立关于用户的一个继承关系来表达泛化的业务模型。但是在编码阶段会发现很明显的问题就是我们把关于Repository的行为包含到了发起用例的用户聚合当中去了,这样说可能有点抽象。我们还是用例子来分析;
1.3图
上图中我将【Admin】和【配送】用例分开了,想表达是不能将关于配送的行为放在【Admin】中。在我们对有关权限进行建模的时候经常会潜意识的将各自的行为放在了各自的角色当中,如果后期存在多角色共享行为的就将写在抽象的类中使用虚方法向下传递。问题就出在关于角色行为里,我们知道如果有行为那么就有可能在该行为里面执行有关其他聚合的IRepository操作,这样一来将会把领域模型搞的很乱,无法垂直分析。
1.4图
DDD讲究领域驱动,在我们看来【Dispatching】、【CheckOrders】都是继承管理员角色,管理员属于后台管理人员,意味着企业的员工。对消费者来说他们就是管理人员。同样消费者也会存在相同的情况,消费者可能存在很多种类型,有VIP系列的(VIP1VIP2VIP3…),有钻石会员之类的。如果这样设计的话并不能说是错的,这也完全符合DDD的思想要求,但是实际情况下却是不理想的。
这里就用到了我们长期使用的设计模式了,我们可以通过设计模式中的很多中模式来将用户与行为分离开来,再将使用的规则条件抽象出来就完全独立了用户,用户在使用的时候不会存在直接的行为归属,但是事实上他们确实是有行为。
1.5图
用专业的DDD术语讲“规约模式”,将业务规则抽取出来对象化,甚至到最后都可以进行规则的配置化。最让我们兴奋的是,我们苦心学习的设计模式终于可以在系统设计中大面积的使用了,难道不是一件很惊喜的事情吗!
2】工具、框架、组件
任何一种架构都是需要框架、工具的支撑才能变的完美。
当我们在某种架构下进行开发的时候,我们必须需要很多工具、框架的支撑才能让开发工作变的很便捷,这也和【敏捷开发】的思想一样。在传统的三层架构下开发我们都需要 "对象映射"、"AOPIOC” 等等类似的辅助框架,目的是为了架构前行的可能性。在DDD中我们也需要很多目前还没有出现的很多工具、框架,在.NET平台中目前来看只有EntityFramework框架算是为了DDD做了很多工作,如果我们的领域模型无法与数据库进行映射,那么领域模型开发所要付出的代价将是很大。
在设计阶段我们缺乏一个面向特定领域的建模工具,这种工具与UML不同,UML太技术化通用化。DDD中经常会提起【领域专家】一角,他是最具有权威性的领域领头人,我们所创建出来的UML他们未必能看得懂,通过技术人员技术化之后形成UML其实已经变味,【领域专家】是懂非懂的无法做到肯定的保证。如果能把领域模型语言化,那么这个将是一大成就。【领域专家】对领域中的任何事物、人物、环节都很熟悉,但是他无法表达清楚自己的想法,如果能有一个工具辅助他的设计,该工具能将设计后的模型进行平滑等价的技术化变成代码模型或者数据库模型,这一条鸿沟如果能跨越那么对行业来说具有很大影响力。
1.8图
如果我们能等价的将上图中的真实模型进行技术化,那么真的每个人都会喜欢需求分析、分析设计。
既然是模型驱动设计,我们在给用户分析类似这样一套系统的时候,前提是我们已经对里面的所有细节进行了抽象封装,每一个过程都是可以拆分的,最后能合并在一起形成一个整体的业务模型。当然这里只是一种技术展望,也是我们奋斗和理想的目标。
推荐一本最新Martin Fowler的书:《领域特定语言》
3】过程
DDD不是一种纯技术实现,而是一整套开发思想,它贯穿软件开发的所有生命周期。从我们开发接触领域,对领域知识进行深入的消化,这些都是DDD所强调的。那么在我们日常开发过程中,我们该如何处理这些过程,需求不会再像以前那样是一份杂乱无章的草稿,而是一个内容丰富的领域模型草图。这样的要求对团队对部门甚至对公司来说都是一个提升,要想做到完全的DDD过程其实很难。
公司领导如何看待这样的开发方式,我们多数人都是在一些非专业研发类的公司工作,领导希望能尽早的看到东西,这很矛盾,需要好的东西但是不按照好的东西做法来做。如果有幸能有一个面向DDD、敏捷、XP的研发团队工作,那么可以视项目为一件终身的艺术品。
这两篇文章主要是一些本人对DDD的感悟,分享给大家。
后面一篇文章将会详细的使用一个DDD架构的小系统作为案例给大家分享,里面将包括从需求的分析建模、设计模式的使用、数据库映射、EntityFramework的使用等等,可以作为真实项目开发的依据。