夫未战而庙算胜者,得算多也;未战而庙算不胜者,得算少也。
多算胜少算,而况于无算乎!
吾以此观之,胜负见矣。
写在前面:
作者对DDD了解的不深入,写这篇文章并没有参考行业报告,没有数据,仅仅是个人工作中得出的一些启示或者看法,如果有认识不正确的地方,请大家指教。
在InfoQ第二季度架构与设计图谱中,Domain-driven Design(以下简称DDD)已经成为 Late Majority,已进入成熟主流的范围。
首次接触DDD是在2017年,当时入职的公司架构师极力推崇DDD,在项目初期花费大量时间去组织研发高P讨论领域建模,向所有研发人员宣讲DDD在解决业务复杂性带来的好处。写到这里,如果对DDD有了解的同学应该发现,在我当时所在的团队,DDD只是在【研发团队内部】达成了一些共识,但并没有在整个业务部门推广,包括业务、产品、测试等,说句不好听的,就是 研发部门 找到了新大陆(新技术) 内部自High 而已。
项目开始后,进行实践的研发人员对DDD完全不了解,我自己也买了两本关于DDD的书研究,一本是DDD的提出者Eric Evans写的《领域驱动设计》,另一本Vaughn Vernon写的《实现领域驱动设计》,说实话,这两本书都没读完,先立个Flag,2020年内硬着头皮也要把两本看完。
看了一半就懵圈了,说的是啥?刚开始除了对【贫血模型和充血模型】有了启示,还有对【分层架构】的设计,特别是【六边形架构】的设计有了一定理解,也明白【实体、值对象 和 聚合】的概念,但是如果真的写代码,可真不知道该如何实践。
不过从【贫血模型和充血模型】中明白,在工作的前几年,自己确实用【面向对象】的开发语言写了【面向过程】的代码。
还是说说在实践过程中遇到的问题:
1、思想不统一
就像前面说的,DDD只是研发部门内部自High,业务和产品根本不吃这套;
原因很简单,业务和产品【只关注的是KPI和业绩】,【怎么实现我不管】,排期定下就要完成,完不成汇报给老板都是研发的锅;
再说说研发内部,如果DDD是架构设计,首先研发内部如果不统一,更别说让产品和业务都认同。
最近十年行业发展,【高并发大流量】,【大数据】,【人工智能】,【微服务】成为主流,出现的技术也越来越多,研发都有极客精神,这很好,但是难免产生一些思想:
什么技术流行用什么;大厂用什么就要用;我们要用最新的技术,否则就会被淘汰;
如果说产品关心业绩,那研发自然只关心技术,毕竟会的技术多了,出去面试找工作有资本(“将来面试好吹一波”,这是听到的原话,如果真有这种思想,本身就很危险);
作为技术,我从不排斥新技术或者新思想,因为科学技术的诞生都是为了解决发展问题,但是绝大多数研发还是技术思维,想用最流行的技术,想用最新的技术,而往往忽略本质问题,系统最大的挑战是什么?系统要解决真正问题是什么?有多少人能说清楚呢
反正用最新的技术准没错,消息队列、缓存、分库分表都用起来,DDD这种思想很火,业务似乎也很复杂,我们就要用;
2、没有和业务讨论
之前待的团队,有从事本身业务的技术前辈,也有从其他业务进入团队的技术专家,一开始大家讨论为业务进行建模,让人觉得带在这样的团队,有前途有发展,但是现在看来,初衷是美好的。
领域专家不仅要在研发团队内部去宣传这个思想,也要将这个思想带到整个业务中,包括产品,前期没有和产品、业务一起讨论,形成共同的认识,为后期需求优化和迭代埋下隐患,因为领域问题不仅仅是架构设计问题,还是业务问题,DDD除了指导架构设计外,也需要业务参与,如果脱离业务讨论领域,最终只是研发自己的事情,和业务沟通没有统一语言,甚至会出现概念上的分歧。
另外,领域专家不能只懂DDD,也不能只懂行业,必须要理解业务本质,并且能从长远角度去规划和思考业务;
领域专家不是岗位,而是能力;
领域专家要有责任心,要为业务负责,要对设计的领域模型负责,但是并不代表领域专家设计的模型就是不能变的;
一开始大家讨论DDD,建立了一套模型,但这个模型只是在整个业务中的一部分,或者说只是为一个较核心的系统建模,定义了模型实体,然后用实体放到业务流程中去验证,看着好像还行,于是得出最初的模型。
从现在看,除了模型之外,整个业务上下文和边界还是没有划分清楚,职责没有确认好,为后续的实践埋下坑。
3、没想清楚就动手
最后好好总结自己的问题。
首先,自己对DDD没有实践经验,长期从事面向过程开发,虽然在过去的开发中,利用设计模式去解决if...else...等带来的问题,但更多是利用 设计模式 中 行为型模型 去解决代码层面上问题,但是如何落地模型,将模型变为代码实现,成了自己的首要问题。
当时虽然在编码过程中,不断和Leader讨论模型落地该怎么写,但发现是自己一开始没想清楚,结果只是把Service的方法迁移到了对象中去,对象本身变得极其臃肿。代码越来越复杂,实体类成为一个新的“Service”,从表面上,虽然是用领域模型去完成业务功能,但只是将放在service中的事务脚本,迁移到了领域模型中,模型变得十分脆弱。
没有想好业务本质,也是自己的问题,把业务概念没有做好抽象和归纳统一,还是像过去用业务概念去建立模型的行为,导致后续模型扩展时,在其他模型中增加了不需要的Override方法,代码整体看起来很丑。
有时候,为了复用而定义的方法,反而成为【技术债】。
现在对【不要为了复用而继承】这个理念有了更深刻的认识,继承的类一定要是抽象的,不能是实现类,因为实现类很容易变,一旦变了对子类影响很大,封装性也被破坏,当时为了复用父类的方法和属性,把一些本该隐藏的内容,设计成protected,这也导致代码变得脆弱。
虽然项目顺利上线,但现在看,当时自己在用战术上的勤奋,弥补自己战略上的错误。
在后续的迭代中,发现当初自己建立的模型实现上有很多问题,扩展性不强,可维护性低。在一次团队分享,把自己的设计问题和一些同学进行沟通,并自己对当时的问题做新的设计,比较遗憾的是,后来的Leader觉得我能力不行,把这个系统给其他人负责了,没能将自己新的设计去优化老的模型。
人生,总是要有些遗憾的。
当时自己比较固执,没有多找些人沟通和交流,开放心态也很重要,学会接受别人的观点,或者说认证倾听他人的意见,在自己思考一下是不是有道理,而不是一开始带着冲突的态度,将和自己不一致的观点都很排斥,或许会少走很多弯路吧。
对DDD的总结:
1、DDD是思想,首先绝大部分人在思想上统一,包括 业务、产品、研发,不要将DDD发展为研发内部的自High;不要带着看源码或实现原理目的去看待DDD;
2、要有真正的领域专家,领域专家是战略家,也是战术家,能理解业务问题,能思考设计和研发中的痛点,领域专家需要全才,既要懂业务,又要懂技术,这对研发来说是优势;领域专家要有话语权;
3、一旦领域确认好,形成规范,任何新的业务形态和尝试,都不能破坏【领域边界】和损坏模型,建立模型和边界,就是为了保证业务更好更快发展,研发效率和质量提高,不能因为需求紧急就放弃底线;边界不清楚,功能随意堆积,将催生一个个新的【巨人】系统诞生;
4、需要大量尝试,不一定能从开始就建立完美的模型,如果可以当然最好,模型不是万年不变,而是随着业务发展进行优化;也不要从工作业务中开始落地DDD,可以自己先建个项目,去实验,然后再结合自己的业务问题去实践,不建议没有经验的工程师拿公司的系统去实践。
5、控制试错成本,虽然模型可以优化,那必须结合自身企业的试错成本,在业务迭代中稳中求胜,毕竟大的系统重建(不是重构)成本,在业务发展面前都要好好掂量;
6、【模型设计】需要结合【业务知识】与【程序设计】,不能脱离两者来设计模型,因为脱离【程序设计】的模型,最终成为【业务功能堆积】的载体,而失去模型抽象总结业务概念的本质;而脱离业务的模型,系统无法真实反映业务的本质,最终导致功能分散,无法实现软件设计中【高内聚低耦合】的思想;
7、【模型设计】不是堆积行业术语,大多数的行业术语,增加了对理解业务实际本质的成本,容易造成偏离轨道,而沉浸到讨论一些抽象名词的理解细节中。用易于理解的概念去抽象和总结模型,容易理解和实现才是【模型设计】的关键;