领域驱动设计系列文章3 —— 有选择性的使用领域驱动设计

领域驱动设计系列文章

通过现实例子显示领域驱动设计的威力
浅析VO、DTO、DO、PO的概念、区别和用处
有选择性的使用领域驱动设计


前言

本系列的第一篇博文抛砖引玉,大谈领域驱动设计的优势,这里笔者还是希望以客观的态度,谈谈领域驱动设计的缺点及其不适合使用的场景,以让读者可以有选择性的使用领域驱动设计。

影响选择的因素

我们知道,没有最好,只有最合适,设计也是一样。因此,所谓设计,就是以你和你的团队的知识、经验和智慧,全面充分的考虑各种内外因素后,在你们的设计方案中作出合理的选择的过程。而这些影响你们选择的因素主要有:

  • 技术框架的特征和约束(如果你的项目决定使用C语言进行开发,那么首先在设计方法上,就需要使用面向过程而非面向对象的设计方法)。

  • 时间的压力和约束(你永远不可能告诉你的老板,给我10年时间,我和我的团队将为你设计出世界上最优秀的软件)。

  • 你的团队的能力、经验、性格、价值观等因素的约束(你不能期望一个长期从事遗留系统维护项目或大部分成员是缺乏经验的高校毕业生的团队能很好的按照你的设计意图去实现你的高度抽象的优秀设计,同时你也别指望一帮家里经济条件不错,本着过来熬时间的家伙会乐意与你一起刻苦钻研、精益求精)。

  • 你的系统的特征(如果你想把一个足够简单而且在可以预计的将来都不存在很大规模的需求变更的系统设计得很复杂,很精妙,具有很好的扩展性,但要为此付出巨大的时间、人力成本,这显然是一种不理智的过度设计(Over design))。

  • 其他外在因素的约束(你的项目需要参与投标,你必须压缩人力、时间等以让你的项目成本成为巨大的竞争资本)。

当然,上述的考虑因素站在比较高的角度,通常是项目经理、架构师需要考虑的问题,但这当中你应该会得到一些启发。回到我们的主题,我们首先看看,领域驱动设计相对于传统的面向过程式的设计,有什么缺点:

领域驱动设计的缺点

  • 复杂化:面向过程思维之所以一直很受欢迎,是因为它很直观,非常符合大部分人的思维习惯,大部分人拿到一个问题,通常都是会很直观的想第一步做什么、第二步做什么,如果怎样,应该怎样,否则怎样……,可以说,任何水平的程序员,都能很好的使用面向过程的方法进行设计和开发。同时,由于我们教育水平的落后和整体IT环境的制约,可以这样说,真正掌握面向对象思维和设计方法的程序员的比例非常低(虽然绝大部分都使用面向对象的语言和工具),而本身面向对象思维要求人有很好的抽象思维能力,因为你需要把一个复杂的系统一层层的抽象为简单的部分,需要把现实世界的事物(有些是可见的,但有些是不可见)合理的抽象为计算机世界的不同元素。这些都不是一些很容易做的事情,要做得好,就更难。

  • 团队的抗拒:如果你的团队(很大可能)大部分人都习惯于用很直观的面向过程的方式进行设计和开发,你需要推动你的团队转换思维来采用面向对象的方式进行领域驱动设计,通常会遭到多数人的抗拒。因为人都是有惰性的,他们习惯安于现状,而改变是痛苦的,他们要付出额外的努力,需要进行学习,但以笔者的经验,相当一部分程序员,特别是有一定工作年限的程序员,他们从事IT工作都只是为了获得一份不错的报酬,因此他们的学习动力非常有限,而且,他们都相当自负,被说服的难度比较大。

  • 管理、开发和维护的成本高:复杂度更高,意味着你需要花更多的时间进行设计,同时需要花出额外的时间进行必要的培训,而且需要有更完善的文档(设计文档,API文档,培训文档等)。领域驱动设计的抽象程度比较高,因此必需有良好的文档,否则,随着项目的不断迭代、升级和维护,它很容易因为后来者的误解而慢慢回归面向过程的设计,甚至会变得“四不象”,领域驱动设计的成本优势是随着时间的推移慢慢体现的(见下图),如果出现这种情况,所有前面付出的努力都会付诸东流。

在这里插入图片描述

系统的初始阶段,领域驱动设计需要付出更大的成本,但随着时间的推移,领域驱动设计的成本效益优势会逐步显现
  • 性能比较低:使用纯面向对象的方式进行领域驱动设计的程序,其系统开销通常要比面向过程设计的程序高,从而性能相对较低(关于具体的例子,后续的博文会提及)。

选择使用领域驱动设计的导性原则

那么,假设我们在时间、团队能力及各种资源都允许的情况下,是否就可以麻木的全盘使用领域驱动设计呢?正如本博文的标题一样,答案是否定的,我们需要有选择性的使用。让我们来看看一些指导性原则:

  • 使用领域驱动设计,并不代表整个系统的方方面面都必须遵从领域驱动设计的原则,需要根据实际情况,让适合的部分使用领域驱动设计,让不适合的部分使用面向过程的设计。

  • 对于那些业务规则非常简单,通常只有增、删、改、查的简单操作,而且也不大可能发生大规模需求变更的模块,可以让业务实体成为一个“贫血模型”,例如一些基础数据:系统参数、商品类型、国家、地址信息(注:对于这点,本人持保留态度,因为这些业务虽然非常简单,但既然选择了领取驱动设计,即使把这些业务实体设计为“充血模型”,即把极其简单的业务逻辑也封装在业务实体中,也并不比使用“贫血模型”成本高,而它却带来了统一设计风格的好处)。

  • 对于查询操作,特别是复杂的查询操作,出于性能的考虑,可以用结构化查询逻辑一次性完成,并把这些逻辑封装在Repository(即技术上的DAO)中(这方面的具体例子,后面关于“查询通道”和“领域对象仓库”的博文会更具体的阐述)。我们可以看到,对于一些业务视图,以及报表模块,很明显不适合使用面向对象的方式设计,因为这些“视图”和“报表”,本质上就不是业务实体。

  • 同样出于性能的考虑,在业务实体的实现逻辑中,某些操作不适合过度偏执的使用面向对象方式。例如,在“订单”的“新增订单明细”(order.addOrderItem(orderItem))中,如果业务逻辑规定一张订单中包含优惠商品的明细数目不能超过20条,使用面向对象的方式,就需要把订单中的所有订单明细全部加载,然后逐个明细判断其对应的商品是否优惠商品,再统计出优惠商品的数目,这样很明显是低效率和高开销的,这里只需要使用Repository提供的一个统计方法,用一个结构化查询逻辑返回统计结果即可,而这就是非面向对象的方式。

本博文给有志于领域驱动设计的读者泼了一下冷水,提出一些“反模式”(Bitter),是为了让读者冷静一下,在领域驱动设计过程中作出更灵活和更合理的选择。关于这方面的论述,笔者在这里浅尝则止,限于水平、经验和表达能力,不敢胡乱卖弄,建议读者可以参考阅读Martin Fowler的《Patterns of Enterprise Application Architecture》一书的相关观点。


原文标题:领域驱动设计系列文章(3)——有选择性的使用领域驱动设计
作者:Johnny.Liang
出处:http://www.blogjava.net/johnnylzb/archive/2010/06/26/324563.html

一共两个压缩分卷,这是第二个分卷 第ⅰ部分 让领域模型发挥作用. 第1章 消化知识 5 1.1 有效建模的因素 9 1.2 知识消化 10 1.3 持续学习 11 1.4 知识丰富的设计 12 1.5 深层模型 15 第2章 交流及语言的使用 17 2.1 通用语言 17 2.2 利用对话改进模型 22 2.3 一个团队,一种语言 24 2.4 文档和图 25 2.4.1 书面的设计文档 27 2.4.2 执行的基础 29 2.5 说明性模型 29 第3章 将模型和实现绑定 32 3.1 模型驱动设计 33 3.2 建模范型和工具支持 36 3.3 突出主旨:为什么模型对用户很关键 41 3.4 实践型建模人员 43 .第ⅱ部分 模型驱动设计的构建块 第4章 分离领域 47 4.1 分层架构 47 4.1.1 层间的联系 51 4.1.2 架构框架 51 4.2 模型属于领域层 52 4.3 其他种类的隔离 55 第5章 软件中的模型描述 56 5.1 关联 57 5.2 实体(又称引用对象) 62 5.2.1 实体建模 65 5.2.2 设计标识操作 66 5.3 值对象 68 5.3.1 设计值对象 71 5.3.2 设计包含值对象的关联 73 5.4 服务 74 5.4.1 服务和分隔的领域层 75 5.4.2 粒度 77 5.4.3 访问服务 77 5.5 模块(包) 77 5.5.1 敏捷的模块 79 5.5.2 基础结构驱动打包的缺陷 80 5.6 建模范式 82 5.6.1 对象范式的优势 82 5.6.2 对象世界中的非对象 84 5.6.3 在混合范式中使用模型驱动设计 85 第6章 领域对象的生命周期 87 6.1 聚合 88 6.2 工厂 96 6.2.1 工厂及其应用场所的选择99 6.2.2 只需构造函数的情况 101 6.2.3 接口的设计 102 6.2.4 如何放置不变量的逻辑 103 6.2.5 实体工厂与值对象工厂 103 6.2.6 存储对象的重建 103 6.3 仓储 105 6.3.1 查询仓储 109 6.3.2 了解仓储实现的必要性 111 6.3.3 实现仓储 111 6.3.4 在框架内工作 113 6.3.5 与工厂的关系 113 6.4 为关系数据库设计对象 115 第7章 使用语言:扩展示例 117 7.1 货物运输系统概述 117 7.2 隔离领域:系统简介 119 7.3 区分实体和值对象 120 7.4 运输领域中的关联设计 121 7.5 聚合的边界 123 7.6 选择仓储 124 7.7 场景概述 125 7.7.1 应用特性示例:改变一件货物的目的地126 7.7.2 应用特性示例:重复业务126 7.8 对象的创建 126 7.8.1 cargo的工厂和构造函数 126 7.8.2 添加一个handling event127 7.9 停下来重构:cargo聚合的另一种设计 129 7.10 运输模型中的模块 131 7.11 引入新特性:配额检查 133 7.11.1 连接两个系统 134 7.11.2 改进模型:划分业务 135 7.11.3 性能调整 137 7.12 小结 137 第ⅲ部分 面向更深层解的重构 第8章 突破 143 8.1 关于突破的故事 144 8.1.1 中看不中用的模型 144 8.1.2 突破 146 8.1.3 更深层的模型 148 8.1.4 冷静的决定 149 8.1.5 成效 150 8.2 时机 150 8.3 着眼于根本 151 8.4 尾声:一连串的新理解 151 第9章 隐含概念转变为显式概念 153 9.1 概念挖掘 153 9.1.1 倾听表达用语 154 9.1.2 检查不协调之处 157 9.1.3 研究矛盾之处 162 9.1.4 查阅书籍 162 9.1.5 尝试,再尝试 164 9.2 如何建模不太明显的概念 164.. 9.2.1 显式的约束 165 9.2.2 作为领域对象的流程 167 9.2.3 规格 168 9.2.4 规格的应用和实现 171 第10章 柔性设计 184 10.1 释意接口 186 10.2 无副作用函数 190 10.3 断言 194 10.4 概念轮廓 197 10.5 孤立类 201 10.6 操作封闭 203 10.7 声明性设计 205 10.8 一个声明性风格的设计 207 10.9 攻击角度 215 10.9.1 切分子领域
【实例简介】 项目采用经典DDD架构(用沃恩.弗农大神的话,其实这是DDD-Lite)思想进行开发,简洁而不简单,实用至上,并且所写每一行代码都经过深思熟虑,符合SOLID规则! ####当前版本 3.0 alpha版(2017-2-7) 采用全新工作流,实现自定义表单处理; 2.0版(2016-10-31) 支持多流程模板; 增加Ace admin界面支持 秀外 输入图片说明 输入图片说明 输入图片说明 慧中 教科书级的分层思想,哪怕苛刻的你阅读的是大神级精典大作(如:《企业应用架构模式》《重构与模式》《ASP.NET设计模式》等),你也可以参考本项目。不信?有图为证,Resharper自动生成的项目引用关系,毫无PS痕迹! 输入图片说明 实用 符合国情的RBAC(基于角色的访问控制),可以直接应用到你的系统。 权限资源 菜单权限 经理和业务员登陆系统拥有的功能菜单是不一样的 按钮权限 经理能够审批,而业务员不可以 数据权限 A业务员看不到B业务员的单据 字段权限 某些人查询客户信息时看不到客户的手机号或其它字段 用户应用系统的具体操作者,我这里设计用户是可以直接给用户分配菜单/按钮,也可以通过角色分配权限。 角色为了对许多拥有相似权限的用户进行分类管理,定义了角色的概念,以上所有的权限资源都可以分配给角色,角色和用户N:N的关系。 机构树形的公司部门结构,国内公司用的比较多,它实际上就是一个用户组,机构和用户设计成N:N的关系,也就是说有时候一个用户可以从属于两个部门,这种情况在我们客户需求中的确都出现过。 ####系统工程结构: OpenAuth.Domain 系统领域层 OpenAuth.Repository 系统仓储层,用于数据库操作 OpenAuth.App 应用层,为界面提供接口 OpenAuth.Mvc 采用基于jquery与bootstrap的B-JUI界面 OpenAuth.UnitTest 单元测试 Infrastructure 通用工具集合 ####使用 管理员可直接在登录界面点击基于精典DDD的权限管理 - 点击以开发者账号登录登录; 普通应用账号使用:test(密码:test)登录; ####后续 更多狂野的功能,正在玩命加载中,敬请期待... 更多文档正在整理中.... 当然,如果你想学习完整的DDD框架,可以参考我的另一个项目(BestQ&A--开源中国推荐项目/集CQRS AES等DDD高级特性于一体的问答系统) 【实例截图】
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值