从三层架构迈向领域驱动设计

标签:前端

本文读者基本要求:从事信息管理系统开发,略懂GOF设计模式及SOLID设计原则,对三层面向过程机械编码厌倦,而且不知道出路在何方,若是还掌握代码坏味和重构手法,那是极好的。程序员

1. 三层架构

理论介绍-->实际经验-->总结反思数据库


技术分享1.1 简单介绍三层架构编程

严格分层架构模式的特色是上层只能访问相邻的下层,其余层次间的调用都不容许。三层架构就是一种严格分层模式,它把职责划分为界面展现、业务逻辑、数据访问三层,还有一个业务实体,前面三层都要依赖它,因此它并不构成一个层。结构如图1。设计模式

三层架构的特色是一种面向过程的编程思想,特色以下:缓存

a. 业务实体类中基本上只有属性没有方法。安全

b. 业务逻辑层的类基本上只有方法没有属性。性能优化

c. 将数据表结构映射为业务实体类是一个惯用作法,以致于有人将其称之为“传统”。这样的好处是只须要理解一套模型,可以经过自动化工具从数据表直接生成业务实体,还可以天然而然的经过自动化机制存储与检索业务实体。但对于复杂点的业务,这样作就是绝大部分问题的根源。数据结构

技术分享 d. 当业务膨胀起来,须要划分模块的时候,咱们有个经常使用的变形:提取一个服务层出来,用来组合模块间的交互,还为业务逻辑层提供了一个防腐层,能够把记录日志、验证权限、处理异常等职责分配给服务层。结构如图2。架构

上面介绍的就是咱们常说的三层架构,因为采用了严格分层模式,用户界面层是绝对不能跨过业务逻辑层调用数据访问层的,同理服务层也是不能调用数据访问层。可是图2都有四层了,那它仍是三层吗?这就须要见仁见智了,通常来讲仍是的。其实三层架构还有个更准确的名字----分层贫血领域模型架构,这样就无论多少层都能归纳了,可是这名字没有三层那么朗朗上口。前面名字中的领域模型指的是业务实体,贫血意思业务实体中没有或不多方法。


1.2 实际应用中的三层架构

 

1.2.1 PetShop。

三层架构比较著名的例子就是PetShop,其技术内容比较丰富,MemberShip,ProfileProvider,各类接口,各类Factory, 可替换的数据访问层,缓存机制,消息队列等技术,看起来很牛的样子。但它的领域模型却比较简单,连模块都没有划分,只能算个玩具。不少年前,见识还比较少 的时候,微软就是我心中的神,只有微软提供的技术才是最正宗的,微软说的都是对的,没有用上微软提供的技术,内心就各类不对劲。后来学习设计模式,知道有 个模式痴迷症(若是在编码中没有用上模式,内心就各类不对劲),才明白原来本身以前患上了微软痴迷症。在个人实际工做经历中,微软痴迷症患者不在少数。就PetShop而言,微软只是展现一下他的技术而已,初学者们不明觉厉,盲目地奉它经典,以致于固步自封,不明白软件开发的路还很长很长,每种技术都有它的适用场景,选择权永远都在本身的手上(或在团队领导的手上)。

 

1.2.2 本人实际工做经历。

参加工做的前几年,都在小公司,经历的都是一我的开发的小项目,有时有个Web前端或美工配合一下,信息系统嘛,主要功能固然就是存储、检索、展现数据,业务操做就是CRUD了,业务逻辑最多就是验证一下业务实体填写的正确性,数据表的数量也很是有限。那时的我,在三层架构的统治下,作着一个快(枯)乐(燥)的数据搬运工(从数据库搬到界面或从界面搬到数据库)。


长期重复枯燥的工做会让人感受前途无亮,穷则思变,结果就是人员的流动。对于短时间小项目来讲,人员流动根本不是个事,可是对于长期项目来讲,可能就 没那么简单了,某些关键人物的离职,可能会引发很长期的震荡,接手的人加班、发脾气绝对是正常的(这视乎接手人员的技术水平),甚至可能使项目停滞不前。

 

终于,我顺从心里的呼唤,离开那个简单的环境,加入了一个比较复杂的长期项目。和之前同样,采用三层架构,信息系统项目嘛,CRUD就是基石,项目中绝大部分的工做就是搬运数据。不一样的是,开发人员有10个以上(人生中第一个团队开发);数据表超过200个 (这是之前不敢想象的)。更加不一样的是,有些功能,它可能牵涉到了全部的数据表,作这样一个功能,首先要把全部关联到的数据表都找出来,理解清楚表与表之 间的关系,比较痛苦的是别人写的代码你基本用不上,都要本身从头搞。对于那些平时只是埋头搞本身一块功能的人来讲,简直是要命。还有更要命的,不知道什么 时候需求又变动了,而处理这个功能的人离职了。开始,接手的新人两眼泪汪汪求多给点时间,而领导不理解,之前谁谁不是很快就搞定了吗?那些站着说话不腰疼 的同事说,这功能简单,这个、这个、还有这个表,那个、那个、还有那个表,这样再那样就解决了。我把这种协做方式成为嘴炮协做,真正的协做应该是代码层面 的。新人无法辩驳通常最后会选择默默地加班。人员继续流动,这样的情形发生屡次以后,领导开始意识到,状况没那么容易了。特别是若是几个关键开发者都离职 了,那境况更是难上加难。就算没有人离职,状况同样是愈来愈难,这我在后面的一个项目中有比较深入的体会,可是并非全部人都会把这种难归结于架构不对或 团队开发水平还有待提高,他们会认为这是需求的问题或者其余同事的问题,还有人会认为这是正常的必然的无解的事情。若是一段时间复杂的功能变动多起来,加 班就会成为常态,若是加班可以搞定,状况还不算最糟糕。“水很深,水太深,水不知道有多深”开始成为了团队的口头禅。比较幸运的是当时那个项目这样的功能 不算太多。最后有人总结出一句名言:“谁遇到谁倒霉 ,忍忍就过去了”。说到这里,不少人可能会说,让全部开发人员的熟读全部数据表不就好了?实际状况是,除了关键的几我的,其余开发人员每每没有那个愿意把 有限的生命浪费在阅读这些下个项目就毫无用处了的数据表上,并且表的数量可能会增加到没有任何一我的可以总体掌控的程度。并且一个牵涉多个模块的复杂功 能,不能调动相关模块来协助,反而都得直接与数据库打交道,其余模块的逻辑也须要本身处理,这是单打独斗的套路,体现不了团队的协做与效率;再有就是不能 复用那些久经考验的代码,致使重复代码慢慢积累,系统缺陷也比较多。并且由多人接手后,一旦有人理解出了误差,需求变动致使致命缺陷的概率会很高。

 

虽然项目组的生活氛围不错,时不时有经费给同事们出去吃完玩乐一下,可是不能预料何时需求就会提出一些“险恶”的功能,对人的精神也是一种考验。为了应付随时可能出现的各类险恶状况,我学习掌握了一些重构手法,学会用了UML来分析问题,还在项目中成功地应用了两个设计模式来解决一个棘手问题。可是在三层架构的大环境下,可以发挥的余地也是很是有限,有种有技术没地方用的感受,常有新建一个类不知道该往哪一个层放的状况。


后来,我还在另一个采用三层架构的长期项目里呆过,状况与上一个项目基本同样,除了人员比较上一个项目稳定不少。就三层架构实际遇到的问题,我咨询了一些项目经理朋友,他们的意见以下:

经理A:代码是不可能写成书上介绍那样规范好维护的。

经理B:需求是不可能稳定下来的,代码也是不可能稳定下来的。

经理C:项目到了后期代码乱?难道不都是这样吗?


1.3 三层架构的反思

Linus Torvalds :Bad programmers worry about the code. Good programmers worry about data structures and their relationships. (差的程序员纠结代码怎么写,好的程序员纠结数据结构和结构间的关系。)

三层架构的最大问题在于:实际应用中人们喜欢把内存模型和数据库模型保持一致。三层架构的大部分问题都是从这里衍生出来的。

数据库模型的粒度若是很小,那么大量的表链接很快就会让数据库跑不动了。

若是数据库模型的粒度若是很大(这是大部分项目的选择),代码的质量(重用性、稳定性、扩展性)就不好。因为没有从业务的角度去仔细定义每个对象,每一个人会根据本身的须要创建各类QueryModel或ViewModel,慢慢地类会多到想哭。或若是不创建各类Model,强行重用DataModel的话,那么接口提供的内容每每绝大部分都不是你想要的。

内存模型与数据库模型保持一致并不是天生的,这是有不少缘由形成的:

它建模的简单性让初学者没法拒绝,因为经验主义,以致于多年之后已经没有勇气去摆脱了;

没有专门论述三层与建模的书籍;

ORM工具误导,与数据表结构一致内存结构方便创建映射关系;

示范代码的误导,错误把示范代码当成产品代码;

等等......

种种误导致使不少人工做不少年后依然未能找到正确的路,忽略了一个重要的核心(业务建模)环节(业务模型要与代码的数据结构保持一致),可是他却坐 上了项目领导的职位,这就是更大的误导了。以致于让人产生不管你去到那个公司都是同样的错觉(在不少人的经历中,这的确是事实,包括我本身),但事实不该 该是这样的。

用户界面,领域模型,数据库它们应该具备同等的重要位置,领域模型在不少公司都是被忽略的角色。

咱们应该相信:优秀的软件必定是由优秀的开发者制做的,团队的协做方式应该在代码层面,代码复用能够下降缺陷和提高效率。而这些都指向你应该离开“传统”三层架构。

还有一些三层开发人员最终患上了数据库痴迷症,他坚信程序就应该作个搬运工,其余的事情都应该交给数据库来完成,业务逻辑也应该写进存储过程里面去。

2. 三层分层到DDD分层的转变过程

优化层次关系-->重构到面向对象设计-->使用DDD相关模式深刻重构

上面讲到,三层是“分层贫血领域模型架构”,那么DDD则可称为“分层充血领域模型架构”。从名字上看,它们就有亲戚关系,有亲戚好办事,嘿!

三层到DDD的 过程大致是这样的:首先推翻严格分层的理念,采用松散分层来从新定义服务层(松散分层的意思是上层能够访问下层,而不仅是相邻的下层),把调用数据访问层 的职责交给服务层,接着把业务逻辑层移动到与业务实体在一块儿,再接着融合业务逻辑与业务实体,使之成为面向对象的设计,而后利用DDD的模式进行更深刻的重构。在DDD技术掌握的还不是那么扎实的时候,三层技术基本仍然可以继续使用。


技术分享2.1 优化三层结构&重构到面向对象的设计

因为目前的服务层职责是很是轻的,甚至有不少空壳的调用,因此平衡一下职责,把调用数据访问层的职责从业务逻辑层提高到服务层,须要的数据经过参数传递给业务逻辑层。这样,对于那些简单到无业务逻辑的CRUD就不须要去访问业务层了,直接调用数据访问层。

结构如图3,咱们看到业务逻辑与数据访问层已经没有依赖关系了。

而后咱们就能够把业务逻辑与业务实体移到一块。

而后把属于业务实体的逻辑迁移到实体类中。

技术分享上 面简单单几句话和两张图就把问题搞定了,可是实际迁移过程当中,风险是很是大的,若是没有充分掌握重构知识,建议不要在正式产品代码上尝试。有些逻辑没法归 类到任何一个业务实体上的,就让它留在原地,成为一个领域服务。有些逻辑连领域服务都没法归类的,就让它留在服务层,在坚持大方向的前提下,细节灵活处 理,由于每次重构都可以让你对将来的路看到更清楚,极可能下一次重构后你就会为这些流浪逻辑找到合适的家了。全部的建议都只是建议,最终决定权在你的手 上,受苦的人始终都是你本身。

一个事实是:掌握的编程思想越多,那么搬迁起来就越容易,假若编程功底太差,可能根本没法搬动,不建议初学者进行这些危险的试验,由于你必定会把事情搞砸的,若是你把事情搞砸了,请自我检讨一下是否是编程思想不太够用。

上面的过程并不是要你一步到位,你能够把代码重构到任何一个时刻,等待后面知识跟上了,再进行下一步的重构。

到目前为止,代码仍是那些代码,只是位置变了而已,因此若是严格按照重构手法进行,编译、运行应该都是没有问题的。就这样,在现有生活没有任何影响的状况下,你已经为你的职业生涯打开了另一扇门,不再怕编程技术拼不过那些年轻人了。

实际上,DDD只是让你从新回归到面向对象编程,没有什么更加神奇的魔法,固然DDD超越面向对象的地方在于对类的设计提出了更多的指导方法。


2.2 使用DDD相关模式进行深刻重构

技术分享正式介绍一下DDD的分层架构,固然DDD的架构并不限于分层,如今还有六边形架构、CQRS、EventSourcing等技术能够选用,可是不要好高骛远,先把分层架构掌握好。原版分层结构如图5,改进版结构如图4,图4基本上就是图3的各个层换了名字,而且UI能够访问基础设施层。而图4与图5的区别在于,图4是基础设施依赖领域层,图5是领域层依赖基础设施层,这两层的关系倒置了。这样的倒置会更凸显领域层的核心地位,也有人认为这样已经算是六边形架构了。


DDD各层次职责解析:

用户界面层:

原版----负责向用户展示信息以及解释用户命令。

补充---- MVC中V和C都属于UI层,V展示信息,C解析用户命令UI像地图同样把各个控制器关联了起来


应用层:

原版----很薄的一层,用来协调应用的活动。它不包含业务逻辑。它不保留业务对象的状态,但它保有应用任务的进度状态。

补充----协调应用的活动这句话太抽象了,我充实一下它:从数据访问中获取领域对象,调用领域对象的方法完成任务,而后再调用数据访问代码把领域对象的改变持久化。事务、权限检查、记录日志、处理异常的职责也归它管。这点和前面三层的服务层的职责实际上是同样的。


领域层:

原版----本层包含关于领域的信息。这是业务软件的核心所在。在这里保留业务对象的状态,对业务对象和它们状态的持久化被委托给了基础设施层。

补充----业务对象的持久化工做咱们已经提高到应用层了,通常状况下,这层最好不要涉及资源库的调用,可是并不绝对。资源库的抽象要么在领域层中,要么提高到了“应用程序框架”,领域层是不会依赖基础设施的。


基础设施层:

原版----本层做为其余层的支撑库存在。它提供了层间的通讯,实现对业务对象的持久化,包含对用户界面层的支撑库等做用。

补充----这层的职责包含了三层的数据访问,可是并不表明UI层就能够调用数据访问的代码,并且职责范围扩充了,有人可能会把它看成存放公用代码的地方,可是建议这里只存放本项目公用的东西,若是可以跨项目公用的代码应该放在一个叫作“应用程序框架”的项目来完成,每一个公司都应该有本身的应用程序框架。


对比一下三层分层与DDD分层:

a. UI层技术基本同样,一些比较智能的绑定可能没法进行了。

b. 服务层基本同样。

d. 业务实体+业务逻辑 = 领域层

e. 若是三层架构不采用业务实体与数据表一致的作法,这层也是同样。因为内存结构与数据表结构之间存在阻抗失配,存取领域对象没那么简单。


DDD模式: 聚合,实体,值对象,工厂,资源库,模块。若是彻底没有DDD经验,建议首先阅读《领域驱动设计精简版》一书,可快速掌握DDD的理念。

聚合:这是个难点,通常建议是要设计小聚合,聚合与聚合之间用ID关联,不要直接引用。聚合包含实体,值对象。表达聚合的对象叫作聚合根。聚合内部全部对象的变动必须经过聚合根。聚合根的本质是一个实体。若是聚合要传递给其余模块(系统),通常不要直接传递聚合根,新建一个粗粒度的值对象来进行传递,即DTO对象,DTO也可由接收方创建,由接收方决定须要什么数据,这样就解耦了模块间的关联,至于数据库中是否须要把这个DTO冗余存储,则看实际状况。聚合的数据表设计原则:大表小类。即数据表采用粗粒度,聚合根内部使用细粒度对象,有可能的话,尽可能每一条数据表记录就是一个聚合。

实体:特色是必需要有一个ID来标识本身,可包含值对象和其余实体。

值对象:特色是个只读对象,没有ID标识。

工厂:因为聚合的建立多是个很是麻烦的事情,用工厂来封装这个复杂麻烦的过程。

资源库:资源库就是持久化聚合的地方,就是说数据存储的最小粒度是聚合。可是数据查询的需求可能很是灵活,实践中这条规则有点僵化,通常使用是读写分离方案,就是写的时候使用聚合对象,可是读的时候能够根据业务仔细创建一些查询模型(QueryModel)进行读取。至于数据库是否须要分红读写两个模型,仍是要看实际状况,在系统更复杂和须要更高性能的时候,数据库的模型也要分红两个,不过它们之间的同步就比较麻烦了。领域事件、CQRS、Event Sourcing等技术就是用来解决这个麻烦的。

领域服务:总有一些须要多个聚合进行合做才能完成的业务,它们不能简单地划归参与的其中一个聚合,要用一个领域服务来表达,注意领域服务不是应用层的服务。

模块:如何划分模块,通常有横向划分和纵向划分两种,横向划分例如:实体模块,工厂模块,资源库模块。纵向划分例如:商品模块,订单模块,支付模 块,每一个模块内部都会具有聚合,资源库,值对象等元素。通常的经验是横向划分对项目没有什么帮助,纵向划分能够减小系统的复杂度。模块间的交互在应用层进 行。


在重构的过程当中,用各类代码坏味做为切入点,DDD模式、设计模式等做为方向,利用成熟的重构手法掌控重构过程,而后用SOLID设计原则评估你的重构成果。进行改动架构的重构,必需要整个团队取得共识,而且须要掌握DDD的人增强代码走查,确保一直走在正确的道路上。

再次强调,重构,是不少人的口头禅,可是对整个系统架构进行重构,这是个高风险的大工程,必须在团队中取得共识,并有明确的目的地和到达目的地的安全路径。


至此,三层到DDD的转换完成了。这是三层的终结,也是DDD的开端。路,还长。

3. DDD技术支撑框架

comming soon...

4. DDD领域建模经验

comming...

5. 数据库设计与性能优化

comming...

6. DDD实际工做经验及例子

comming...

相关文章
相关标签/搜索