ABP学习实践(十六)--领域驱动设计(DDD)回顾

ABP框架并没有实现领域驱动设计(DDD)的所有思想,但是并不妨碍用领域驱动的思想去理解ABP库框架。

1.领域驱动设计(DDD)与微服务(MicroService)的关系?

领域驱动设计(DDD)是一种设计方法,一种理解业务、分析问题的思维方式。
微服务(Microservice)是一种软件架构设计风格。
当遇到规模庞大、复杂性高的问题或项目时,采用领域驱动设计能更好的理解问题的内在逻辑,并进行拆解分治。在拆解分治的前提下,构建多个独立运行的服务,就是一种具备微服务风格的实施方案。具体到单个服务的开发工具上,ABP框架是在.NET开发环境下的一种开发模板工具。
使用领域驱动设计方法分析项目需求后并不是只能用微服务风格的软件架构体系才能实施,实施微服务风格的软件架构也不是只能以领域驱动设计为前提,ABP框架也并不是唯一能体现领域驱动设计思想和微服务风格的开发模板。
一切的一切只是因为领域驱动设计与微服务风格能更好的配合,ABP框架则是.NET开发环境下比较适合领域驱动设计和微服务风格的一款工具。

2.DDD分层架构与三层架构的关系?

2.1为什么要分层?

在生活中包括在计算机科学、软件开发中,分层思想随处可见,企业管理中有分层组织架构,计算机网络中OSI网络分层结构,软件开发框架中也有各种逻辑分层。分层的目的是职责分离,也就是高内聚、低耦合。分层的特点也是层与层之间按照约定交互,但不相互影响层的内部,原则上也不允许跨层交互。如果允许跨层交互,最终就会演变曾网状结构,也就是一团乱麻,所以按照约定的职责利用分层结构也是很有必要的。

2.2DDD分层架构是怎样的?

关于DDD分层架构,常见的说法是:从上至下为用户接口层、应用层、领域层、基础层。
在这里插入图片描述

用户接口层:面向前端提供服务适配,面向资源层提供资源适配。这一层聚集了接口适配相关的功能。
应用层:实现服务组合和编排,适应业务流程快速变化的需求。这一层聚集了应用服务和事件相关的功能。
领域层:实现领域的核心业务逻辑。这一层聚集了领域模型的聚合、聚合根、实体、值对象、领域服务和事件等领域对象,以及它们组合所形成的业务能力。
基础层:贯穿所有层,为各层提供基础资源服务。这一层聚集了各种底层资源相关的服务和能力,包括常见的数据库存储、事件总线、缓存、API网关、任务调度等基础功能。

2.3传统三层架构与DDD分层架构是怎么对应的?

在这里插入图片描述
由于都是分层架构,所以DDD分层架构相比较经典三层架构而言存在一定的相似性、继承性和发展性。三层架构向 DDD 分层架构演进,主要发生在业务逻辑层和数据访问层。DDD 分层架构在用户接口层引入了 DTO,给前端提供了更多的可使用数据和更高的展示灵活性。DDD 分层架构对三层架构的业务逻辑层进行了更清晰的划分,改善了三层架构核心业务逻辑混乱,代码改动相互影响大的情况。DDD 分层架构将业务逻辑层的服务拆分到了应用层和领域层。应用层快速响应前端的变化,领域层实现领域模型的能力。在数据访问层和基础层之间:三层架构数据访问采用 DAO 方式;DDD 分层架构的数据库等基础资源访问,采用了仓储(Repository)设计模式,通过依赖倒置实现各层对基础资源的解耦。仓储又分为两部分:仓储接口和仓储实现。仓储接口放在领域层中,仓储实现放在基础层。原来三层架构通用的第三方工具包、驱动、Common、Utility、Config 等通用的公共的资源类统一放到了基础层。

3.Application应用层与Domain领域层的关系?

3.1ABP框架的分层架构是怎样的?

在官方文档的描述中,ABP框架的分层结构从上至下依次是:展现层、分布式服务层、应用层、领域层和基础设施层。其中展现层是直接面向最终用户的,用来展示数据或提供交互界面的,分布式服务层则对外提供API数据服务。展现层和分布式服务层也就是对应DDD分层架构中的用户接口层,叫法不同,职能实则是一样的。剩余应用层、领域层、基础层的划分与DDD分层架构是一致的。
在这里插入图片描述
在ABP框架的具体实现上并没有严格遵循DDD分层架构,主要是在应用层(Application)把数据传输对象(DTO)纳入了进来。这是因为ABP框架在具体实现上做了很多规则约定和封装。在分布式服务层也就是API接口层,ABP框架提供了很好用API接口动态生成方法(只要按照预定的规则编写应用服务,就可以自动生成API接口),因为太好用,以致于很多初用框架的开发人员都忽略了分布式服务层的存在。在基础层则是囊括了日志、多租户、依赖注入、验证授权等通用的功能组件。

3.2应用层与领域层的职责有什么不同?

领域驱动设计(DDD)的主要特点就是从业务领域出发,划分边界,把相关的功能在同一个领域范围内实现,所以说领域层是业务的核心,领域模型的核心逻辑代码一定要在领域层实现。而应用层代码主要完成服务组合和编排,以及聚合之间的协作,它只是承上启下的作用,不应该有核心领域逻辑代码。如果将核心领域逻辑代码放到应用层,基于 DDD 分层架构模型的微服务实际上就会慢慢变成传统的三层架构模型了,那样的话代码逻辑混乱的情况早晚会出现,也就失去了使用DDD分层架构的初衷。

4.项目实践中的常见问题有哪些?

4.1按照数据驱动设计的思路理解领域驱动设计

数据驱动设计的做法一般是分析业务需求、创建数据库表、编码上层服务、交互前端应用,由下至上的拆解功能需求,由于理解简单操作方便,能很好的与三层架构搭配,也被很多开发人员所熟悉。领域驱动设计则是接触到需求第一步就是考虑领域模型,再从上至下划分领域、切割服务。而不是将其切割成数据和行为,然后数据用数据库实现,行为使用服务实现,最后造成需求的首肢分离。领域驱动设计考虑的是业务语言,而不是数据。这就导致很多数据驱动设计的高手尤其是数据库设计的高手在使用领域驱动设计的时候特别不自在,不先设计数据库表而先设计领域模型的做法让他们抓狂,感觉没有数据库表设计所有工作就推进不下去。如果依然以数据驱动设计的思路理解领域驱动设计,对于复杂问题的分析仍然难以与实际需求相符。

4.2按照三层架构的方式使用DDD分层架构

就算勉强接受了先分析领域模型而不是先设计数据库表的思考模式,仍然有可能在具体实践中陷入按照三层架构的方式使用DDD分层架构的处境中。
把DDD分层架构中的实体对象当作三层架构中数据库访问对象使用,不跟数据库表一一对应就难受,关联的业务领域模型被硬生生拆成多个关系型数据表。
把DDD分层架构中的应用层当作三层架构中的业务逻辑层使用,把所有业务逻辑代码一股脑的写进应用层,应用服务之间相互调用乱作一团,完全忽略领域服务的存在,更不要说什么聚合和领域事件了。

4.3按照功能模块的思路实践微服务

如果说前两个问题是开发人员的问题,那么这个问题就是技术管理人员或者项目管理人员的问题了。这里的技术管理人员、项目管理人员主要指负责方案设计、软件开发管理的人员,有些中小型公司往往没有专职的架构师或技术经理,几个开发人员中的相对技术水平较高的那个就被选为负责人。这类人员要负责功能框架的设计、给项目组成员分配任务、确保项目进度,还少不了亲自上阵写代码,名称头衔诸如研发负责人、开发经理、技术经理之类的。对于最终用户来讲,很多人不会理解什么是数据库、什么是接口,他们只关注我的需求能不能帮我实现。开发经理拿到用户需求后,单纯按照用户的字面意思进行理解,需求清单上有几个模块或子系统就划几个服务,功能做得差不多了兴冲冲去跟客户汇报,客户讲这里不对那里错了。开发人员扎耳挠腮一通改,发现原来相同的业务领域被硬划到了几个不同的服务之间,改动量之大可想而知,即使顺利交付也要付出很大代价。
开发经理有时也会按照功能模块的思路往下派发开发任务,开发人员各做各的,做完才发现这有一部分不是重合的吗?拆又拆不掉,合又合不动,费劲心思打补丁,最终成了补丁摞补丁,代码屎山一座,无人想碰。

管理人员应该意识到,在面对需求复杂、规模庞大的项目时,不仅是在技术上要进行设计模式、解决方案的调整,而且要伴随着进行管理模式、开发流程的调整,重点在于思维方式的改变带动行为方式的改变。


5.举个例子

你需要将一盒水果从城南的家里带到城北的公司,一盒水果单手就轻松拎走,城南到城北也是通地铁的,你决定自己坐地铁带过去。你从家里出发,步行到家附近的地铁站,带水果进站乘车然后到公司附近的地铁站下车,下车后你又骑共享单车到公司楼下,然后坐电梯上楼,一盒水果带到了。这个过程中地铁不管你从哪个地方以什么形式到达地铁站,它只负责将你运送到指定站点,你下车后去哪里它不会过问;公司大楼的电梯不管你从哪里乘坐什么交通工具到达大楼,它只负责将你送到指定楼层,你出电梯后去干嘛它也不会问。这就是分层,单一职责,存在连接但不相互影响,更不会跨层连接。你为了解决将一盒水果从城南家里带到城北公司这个问题,选用了步行+地铁+共享单车的出行方式,这就是你为了解决具体问题而选用的软件架构模式。而你选择坐地铁1号线还是3号线,下地铁后骑美团单车还是滴滴单车则是你选则的具体开发工具框架。
再进一步,需求变了,你需要将两大箱水果从城南家里送到城东郊的岳父家。两大箱水果几十公斤你感觉拎不动,城东郊还没通地铁,原来步行+地铁+共享单车的出行方式行不通了,你决定开车过去,于是水果搬到车上,开车送到岳父家,然后搬下水果,岳父高兴的拉着你喝两杯。这就是需求发生了变化,原有的软件架构模式不能解决问题了,就换一种软件架构模式去解决。这其中你开你爸的SUV还是开你老婆的轿车送水果就是你在当前软件架构模式下选择的具体开发工具框架。
再后来,你觉得你老婆的轿车开着挺方便,同时也发现可以开车把一盒水果从城南家里带到城北公司,你就开心,就搞了一辆每天从城南家里带水果到城北公司。这就是解决稍复杂问题的软件架构模式也能解决稍简单的问题,但不见得原有解决简单问题的软件架构模式在遇到类似的简单问题就不能用了。
后来你觉得开车带东西很方便,就宣称开家用轿车送水果是最好的解决方案。这就成了唯工具论了。
后来你岳父觉得上次送的水果很好,跟老家亲戚夸你,结果老家全村都找你订购水果,品种还不一样,一共好几吨。你找到不同的批发商,订购后又联系物流公司,送到老家村里,又找村里的发小王哥做你的代理,替你把水果分到各家。这时需求规模增加了、问题复杂度升高了,你之前认为最完美的解决方案–自己开车送水果不合适了,你再次升级软件架构模式:找不同批发商–服务分治了,联系物流公司–服务托管/资源租赁了,找发小王哥–服务代理了…
当然你也许会坚持开自己家车送水果是最好的方式,决定自己多跑几趟把水果送到,自己开车跑了十几趟终于送完了,自己累的半死,结果老家亲戚跟你岳父讲“你这女婿是不是有点傻,赶紧了让闺女离了吧”。你很委屈:“这么复杂的问题不是解决了吗?”问题是解决了,可成本呢?一种软件架构方式或许可以解决相对复杂或规模较大的问题,但它是最优选择吗?选择它解决复杂问题所带的成本或者后果你能承受吗?
或许你也意识到开自家车送水果不能解决所有问题,但是开的顺手了也不想轻易放弃。后来公司同事听说了也从你那里订水果,又是几吨。还是从不同的批发商那里订货,但是物流送货你不习惯,你想想还是自己开车送货,又是累的半死,公司同事觉得你有点憨,走路都绕着你。你觉得更委屈了:“我结合了两种模式啊”。还是那句话,你的成本呢?当你习惯了一种软件架构模式,换做另一种软件架构模式肯定会有适应过程,甚至有可能是痛苦的。但是如果你还是用原有的模式在现有模式中解决问题,是一种最优的解决方案吗?或者说你是在创造一种新的模式吗?不,你只是在创造问题,而非解决问题。
看到这里你想笑吗?你也许会说“我怎么可能那么蠢,面对不同的需求还坚持用同一套模式解决?”
不是吗?回头看看自己在软件项目开发中的实践。你真的意识到需求规模和问题复杂度的变化了吗?你是否还在坚持使用毕业时学习的那套架构模式而不肯接受同事推荐的新的架构模式?你是否觉得新的软件架构模式不符合自己以往的思维习惯而放弃学习了呢?你又是否勉强接受了新的软件架构模式但是还是忍不住在里面套用自己以前习惯的那一套?

《人月神话》中讲“没有银弹”,软件开发过程中也没有完美的架构模式或解决方案,一切不考虑实际需求而单纯对比各种架构模式的评价都是耍流氓。深入理解实际需求的规模性和复杂性,认真分析后选用合适的技术方案(包括架构模式、解决方案、工具模板),基于较低的成本输出较高的成果,才是根本的解决之道。这其中最核心的是思维模式的转化,跳出自身思维的舒适区,摆脱思维惯性,才能达到问题的本质。

古人云“哀莫大于心死”,对于技术人员来讲最可怕的莫过于思维僵化、固步自封。


参考资料

领域驱动实践总结(基本理论总结与分析V+架构分析与代码设计+具体应用设计分析)

领域驱动实践总结(基本理论总结与分析+架构分析与代码设计V+具体应用设计分析)

领域驱动实践总结(基本理论总结与分析+架构分析与代码设计+具体应用设计分析V)

何时使用领域驱动设计

领域驱动设计之领域的定义

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值