DDD与TDD比较之——DDD
最近承诺要写一篇TDD和DDD区别的文章,在比较之前,我这里会先分别给出一个DDD的开发实例和TDD的开发实例。这篇文章主要讲解DDD。
几年前,曾接手了别人写了一半的一个项目,新加一些功能,然而模型不是很清晰,虽然反映了外部的业务逻辑,但是为了一些新的功能或者特殊案例,我们需要加一些特殊处理。由于模型不够清晰,原本1天能做的的东西,往往需要4-7天才能做完。
我们需要简单清晰的模型,这样往往能够清晰的表达业务,可能聪明的程序员往往不在乎这个,总能够想出解决特殊的逻辑处理这些问题,然而随着业务的日渐复杂,特殊逻辑的往往不能完全的表达业务,特殊逻辑越来越多,特殊逻辑之间也产生了很多矛盾,为了解决一个很简单的需求,我们要绞尽脑汁,花费大量的精力和资源,大家忙忙碌碌,甚至非常充实,但是鲜有人能够体会到编程的乐趣。
如何建立简单有效的模型呢?简单的模型不是简单的思考,简单的拍拍大脑就做决定,因为,“Simplicity is ultimate sophistication”。简单清晰的模型,需要一个智者“慢慢”摸索才能完成(汲取和消化领域知识,挖掘隐含概念),哪怕你是MF或者是Eric。
我给出一个真实的故事,详细讲述实践Domain-Driven Design过程的所发生的事情。
几年前,我们承接了一个项目,需要做两个系统的集成。刚开始,做该集成开发的有两个人员,过去了一个月多月,后来其中一位离开了公司,但是两个月过去了,仍然没有进展。于是经理派我去着手该事情,其实,我只了解其中一个系统,对另一系统,除名字之外我别无了解。我首先向之前的开发人员索要需求,由于没有正式的需求文档,只有一封潦草的邮件,其中关于此集成的只有一句话:系统A和系统B的集成。看完之后,我一头雾水,由于提供需求的专家暂时不在,为此,我就去咨询那位一直致力于该工具的软件开发人员,想从他那里了解更多信息。
我们首先大致进行了如下对话:
开发人员: 我们就需要做个简简单单的映射,根据需求专家的意思。
我: 映射?
开发人员: 根据系统A的发过来的事故(Incident)的信息,在系统B生成一个新的事故(Incident)的记录。
我: 这是在系统B创建一条记录,是这样吗?
开发人员: 是的, 现在的问题是我们系统B的事故(Incident)添加了新的属性,这些属性是必填项,而发送的过来的信息不全,导致一些新的必填信息为空。
我: 如果B系统记录的属性发生了变化,A系统需要集成,应该在A系统修改和添加这些属性。
……
经过接近半个小时讨论,我得出了以下结论:现在是要发布新版本的系统B,B系统新加了一些特性,导致一些属性发生了变化,包括增加,删除和修改,客户要求发布的版本能够和系统A能够集成,但前提是不能修改系统A代码。
现在的问题也就是如何决定这些必填项信息的内容,而并非映射的问题。几分钟后,我们继续了我们的讨论:
开发人员: 事故(Incident)记录里还有3个属性是需要特殊处理的,Assignment属性,Urgent属性和Priority属性要根据事故(Incident)所关联的另外一条记录资源(CI)的属性来判断。
我: 怎样决定这三个属性值确定了吗?
开发人员: 关于Incident里有3个属性是需要特殊处理的,Assignment属性和Urgent属性和Priority属性,要根据所关联的出现问题的资源(CI)来判断,打个比方:这个资源是Adobe Reader,那么就应该交由Applications的团队管理来处理这个事故,于是Assignment属性就是Applications;并且Urgent属性是Critical,Priority属性就是high。
我: 也就是只有这三个属性需要一些逻辑判断来赋值,其他的如果没有值的话,就是使用配置的默认值了。
开发人员: 这3个字段可以由关联的资源(CI)的任何3个属性值来决定。
打个比方:如果Adobe Reader出了事故,那么这个资源就是Adobe Reader,而Adobe Reader是由Applications的团队管理来处理,于是Assignment就是Applications;而Adobe Reader的Urgent值是Critical,Priority值就是high,那么该事故(Incident)的Urgent值是Critical,Priority值就是high。
我: CI的3个属性的值如何决定Incident这3个字段。
开发人员: 这个还要继续和需求专家讨论。
我: 只是用3个CI属性够用吗?
开发人员: 目前就是这样要求的.
目前看来,我能从开发人员那里得到的信息差不多就是这些了,然而问题还没解决,似乎这位开发人员对于某些需求细节还不太清楚,而他已经动手写完了一部分代码,这部分代码并未反映这些需求的细节。
为了对需求有更好全面的认识,我和需求专家预定好了时间,展开了讨论。
我: 新生成的Incident有三个字段需要特殊处理,对吗?
专家: 可以是多个。
我发现前面对需求认识开始出现问题了,我开始继续往下问。
我: 能不能给我一个场景?
专家: 如果打印机坏了,A会生成一条信息发送给B,B会生成一条Incident记录,这条记录的CI就是Printer,如果不知道应该交由谁处理,即Assignment是空,那么此时,我们就需要找个默认值赋给它。
我: 那么我们需要CI的三个属性来做什么逻辑?
专家: 目前我们最多提供3个条件来判断是否使用这些默认值,如果不符合条件,我们就不使用这些默认值。我们还能提供一个如果不匹配时使用另外一些默认值?
我: 当然可以。
专家: 集成时,不仅可以创建一条Incident记录,还可以更新或者删除(这里指使该条数据状态为Closed,是一种特殊的更新)一条Incident记录。
我们进一步讨论的3个条件逻辑:目前使用最简单的相等来判断。我也了解到这个版本目前只是实验性质的,并且这个集成方案由于种种原因,是B软件单方面的做出的,这个方案最终在以后版本遭否决的程度非常大。了解这些对该功能的开发非常重要。至此,我还没有画出来一张模型图出来和他讨论,我承认我在这里犯了一个严重错误,由于未及时画出模型图来,也差点导致我接下来的工作重做。
此时虽然我没有画出来一张模型图出来,但是我头脑已经有一副,如下所示:
Incident和Configuration之间的关系是一对多:可以创建,更新和删除时使用这个配置来。
Confiuration与CriterionMapping和DefaultMapping之间的关系是一对一的:一条配置信息
正当我准备编写代码之际,我意识到,模型图还未来得及和领域专家进一步深究,于是我又找到他做最终的探讨:
我把我之前脑中的模型图首先展示给了他,和别的领域专家一样,一开始他并不了解这些符号的意思,我用2分钟简单地讲给他多对多的关系和关联关系,他已经明白这张图的意思了。
然后,他迅速指出其中一个细节来:
专家: Confiuration与CriterionMapping关系不是一对一的,是多对一的。
我: 这个……,为什么?
看来理解是有出入的。
专家: 因为我们可以根据发送过来的事故(Incident)的具体属性,选取不同的Mapping,来填充这些新的属性。
我: 恩,确实不错,更加灵活了。
于是我立即修改了模型图:
别看这个小细节的修改,这可关系到用户的体验度和满意度。我们都知道盲人摸象的故事,有的盲人认为它是萝卜,有的却认为它是柱子……,每个盲人对大象的认识是不同的,片面的,要对大象有个全面的认识,需要摸清大象的每个部位。我们认识领域问题要一步一步地深入挖掘,否则会管中窥豹,时见一斑。
记得前不久,遇到几位著名公司的“资深”的开发人员,自认为对软件开发了解独到,甚至对“面向切面的编程”及其不满。我们讨论到一个实际问题:
他们问道:我这里有一系统,使用到了workflow,需要通知,当一条记录发生变化时,需要什么模式?
就这么几句,我努力尝试更深一步的交流,然而都是徒劳地,他们认为,你懂得设计模式,那么就不需要了解需求,或者给你及其模糊甚至错误的需求,都应该立马给出令人惊奇的设计,并结合令人惊奇的模式。
我真无法得到需要什么模式,因为,我不清楚具体的需求到底是什么,而且,模式并非只与模型有关,模式和现有代码(比如,三种wrapper模式,详见我的书籍)等有关。而且模式的粒度要小于需求问题的粒度。
我们在上世纪60年代就提出了,软件必须重视需求,80年代末期,我们意识到,软件问题已经和开发所使用的语言关系并不紧密了,而要解决了实际问题,我们应该关注与问题域(Problem Domain),于是为这些领域建模逐渐兴起。
虽然这几位“资深”人士,有的甚至有10年的开发经验,然而并未了解软件的核心,虽然,我不怀疑他是否做过软件设计,但是至少,我想,他们没有真正设计出OO系统,和让用户非常满意的系统。
末了,上述系统从需求到设计,以及最终的代码交付,我花了4天时间,而之前,已经过去2个多月仍未有结果,因为他有兴趣的是“两个系统集成的技术”。
我希望大家能够明白,软件的核心是模型,不是算法,也不是数据结构,也不是技术。尽管这三个方面及其重要,但它们都称不上是软件的核心。我们需要智慧的程序开发人员,需要脚踏实际,一步一个脚印地去做每一件事,而不是空想自己10分钟可以做别人10年的工作。
作者简介:
我是一个Agile和DDD(Domain-Driven Design)的爱好者,关于这两方面的文章书籍非常丰富:
我非常推荐Eric Evans的Domain-Driven Design: Tackling Complexity in the Heart of Software一书,其中探讨了非常全面的关于领域建模艺术的技术,很多关于DDD的文章和书籍都是基于此书展开的。这本书让我对软件设计有了新的认识,很值得细细回味其中某些重要章节。
Eric Evans的关于Folding together DDD into Agile讲述了如何把他们结合起来,读了之后,我获益甚多。
本人著有书籍《漫谈设计模式》
分享到:
相关推荐
下面将详细阐述其中涉及的关键技术点——DDD(领域驱动设计)、TDD(测试驱动开发)、代码优先策略、异步任务处理、KISS原则、DRY原则、CLEAN CODE以及SOLID原则。 1. 领域驱动设计(DDD):DDD是一种软件开发方法...
最后,书中还会讨论如何将DDD与持续集成、测试驱动开发(TDD)以及敏捷开发方法结合,以实现更高效、更灵活的开发流程。 《实现领域驱动设计》这本书是学习和实践DDD的宝贵资源,它通过实例和深入的解释,帮助...
这包括遵循最佳实践,如SOLID原则、DDD(领域驱动设计)和TDD。 6. **精益思想**:ThoughtWorks将精益理念应用于软件开发,减少浪费,增加价值流的流动。例如,通过最小化等待时间、消除过度设计和优化工作流程,以...
### 领域驱动设计(DDD):精简业务模型与实现匹配 #### 一、领域驱动设计概览 领域驱动设计(Domain-Driven Design,简称DDD)是一种面向复杂业务领域的软件开发方法论,旨在通过深入理解业务逻辑来构建高质量的...
为了更好地理解上述设计思想和技术的实际应用,我们可以考虑一个具体的案例——中大型B/S结构的应用系统开发。 1. **领域建模**:在这个案例中,可以将业务划分为多个模块,比如订单管理、库存管理等,每个模块都有...
TDD(Test-Driven Development)、UDD(Usage-Driven Design)、RDD(Refactoring-Driven Development)、DDD(Domain-Driven Design)等方法论提供了具体的实施策略。 ### 实践与工具 重构、结对编程、静态代码...
DDD - 领域驱动设计 开发人员 - 开发人员 HTML - 超文本标记语言 AI——智能人工(人工智能) GCP - 谷歌云平台 JS - JavaScript LESS - 更精简的样式表 Lib - 图书馆(图书馆) OOP - 面向对象的编程 PHP - 超文本...
而且在理论与实践之间架起了一座桥梁,使读者能够更好地理解和应用领域驱动设计(Domain-Driven Design, DDD)及测试驱动开发(Test-Driven Development, TDD)等最佳实践。 #### 二、主要内容概述 1. **核心概念...
通过这个项目,学习者可以了解如何在Dart项目中实施清洁架构,提高代码的可读性和可维护性,并掌握如何在实际项目中运用DDD和测试驱动开发(TDD)方法。同时,这也为其他编程语言的开发者提供了借鉴,让他们了解到...
1. **全面的IT教育基础**:屈小勇毕业于211工程大学——西南大学的计算机科学与技术专业,具有扎实的理论基础。 2. **深厚的行业经验**:他拥有11年的银行和互联网金融信贷系统实践经验,涵盖了咨询、需求分析、...
这通常会涉及到软件工程中的最佳实践,如TDD(测试驱动开发)和DDD(领域驱动设计)。 标签“软件工程”和“案例”表明这些资料将深入探讨实际项目中的问题和解决方案,使学习者能够更好地理解和应对可能出现的各种...
同时,领域驱动设计并不排斥其他分析技术,如分析模式或测试驱动开发(TDD),它们可以辅助我们构建和验证领域模型。 领域建模在Rational Unified Process(RUP)中也占有重要地位,采用用例驱动的方法,通过用例来...
在领域驱动设计(Domain-Driven Design,简称DDD)中,聚合根是业务逻辑的核心组件,它封装了一组相关的实体和值对象,维护其内部一致性。下面我们将深入探讨Java编程语言中如何实现和控制这类聚合体。 首先,让...
为了保证代码质量,项目可能还采用了单元测试和持续集成(CI/CD)策略,通过Visual Studio或其他IDE进行开发,利用TDD(测试驱动开发)和DDD(领域驱动设计)方法来确保代码的健壮性和可维护性。 总结来说,...
这是一个域元素,但必须根据项目需求来实现——通过 API 调用或数据库查询。固定价格一旦我们将产品添加到购物车中,价格可能是固定的。 如果是项目用例,请查看。如何组装实际应用我们必须通过我们的基础设施实现...