2011年3月份还在华为夜以继日的时候,买过一本《领域驱动设计:软件核心复杂性应对之道》,虽然努力的看过一次,没看懂,觉得都距离我很遥远。2014年4月,在ThoughtWorks还不到一年,买了一本腾老板的《实现领域驱动设计》,看了一遍,似乎理解了一些,但还是有些摸不着头脑。
做IT的一方面白天要鼓足干劲干好那8小时,回家每天抽时间陪小孩玩耍一两个小时,另一方面又要在9点半之后尽快的吸取最新的营养,实乃不容易。回想起老婆回娘家的那段时间以及自己出差的一个月,真是非常难得,可以抓紧时间读书、试验新的技术。ThougtWorks的读书文化我很欣赏,别个同事每年读个50本书,我却用Training Budget买了很多书回来,就放在那里了。这并不是说我费钱,主要是中国的出版业很奇葩,你不买可能过段时间就成绝版了,想买也买不到了。我每过一段时间就要清理了一下,感叹一下书非借不能读也,真想静下心来重新读书。
读书,别开电脑,离开你的手机,摒弃一切的,拿上一支记号笔,开始吧。
重新读《领域驱动设计:软件核心复杂性应对之道》正是由于做到了项目的后期,觉得整个项目已经开始凌乱。大家对业务知识不了解,也没问,只求赶紧将东西搞出来;对技术只见树木不见森林,虽然有很精巧的重构,但无法反映出真实的业务场景;有多个的Micro Service但有些职责显得混乱不堪。团队在一个混沌的情况下越来越晕,我真是想找个时间重新审视一下这个系统,如何在新的一年有更好的发展?
对于一个良好的系统而言,还是需要一个靠谱的模型。然而团队并没有给出这个模型,最早的时候都是按照View Model来建模,映射Field,然后对其进行处理。再引入了Micro Service后,情况有所转变,因为Share的原因必须要让开发者思考Domain Model,然后对页面进行去耦合。这是一个好事情,但尚处于一个模糊的阶段。
领域模型也迫使开发人员学习重要的业务原理,而不是机械的进行功能的开发。
这正是我们目前开发人员很缺的一块儿。作为一个常见敏捷开发团队,一般配置BA(业务分析人员)、DEV(开发人员)、QA(测试人员)。BA和业务人员谈论需求,并且分解为Story卡,DEV可能会参与其中的技术讨论,但对业务知识也只是一知半解,甚至都没有对业务知识的追求。短时间内,这个也没有太大的问题,BA充当了业务人员和开发人员之间的一个翻译,但往往业务人员和开发人员之间存在了语言的障碍。
在之前的项目中,我也仅仅只作为DEV来实现一些需求。对于背后的业务逻辑,BA告诉我即可,别多想了。长此以往,DEV最后就真正的变成了Coding Monkey,而现实的情况是Coding Monkey越来越便宜。所以开发人员必须要逐渐的懂业务,从业务上思考问题,才能跳出Coding的圈子。
好在我们项目,由于BA的能力欠佳,我们不得不跳出常见的开发团队,让多个经验丰富的开发人员尝试更多的时间跳过BA直接和业务人员打交道。一来是意思传达的更准确,二来是增进了开发人员和业务人员的交流。虽然这样通常会让开发人员多出20%-30%的时间用于更多的沟通,对于开发的效率有所影响,但总体而言物有所值。甚至Iteration Manager Fabio(我只能说和这样的牛人一起工作实乃幸运)也鼓励开发人员这么做。我想如果今年团队能够突破英语的障碍,直接和业务人员沟通,BA这个职位都可以淡淡的弱化了,人人当BA。当然还得感谢ThoughtWorks能够有比较小的敏捷的团队。否则类似于华为、中兴这样的大企业,开发人员几乎没有和业务人员沟通的机会,造成做了半天都不知道具体的业务场景是啥,写出来的代码难免会错误百出。
Ubiquitous Language,通用语言。
这种语言可以用于团队的沟通,也可以用于描述软件本身。于领域专家谈话的时候也需要使用这种语言,能够消除彼此之间的隔阂,让开发人员更容易的理解业务。而开发人员也应该使用这种语言来阐述需求,消除模糊的语言。
然而我们没有,甚至各种名词彼此混淆冲突。举一个现实的例子是ANZSIC和Occupation。ANZSIC是指The Australian and New Zealand Standard Industrial Classification,而Occupation是指在某业务部分对ANZSIC的细分用于更精确的定价及投保。
我刚到项目组的时候,就发现代码中混淆着ANZSIC和Occupation这样的类、字段和变量,问了两三个人也没解释清楚,大家的理解这两个东西好像是一回事。后续和业务人员沟通的时候,我们也会将ANZSIC说成Occupation,造成很多的混乱。甚至在做设计的时候,也会混用这两个词。我快要晕了。
我决定停止这样的混乱,找业务人员问清楚。原来ANZSIC和Occupation比较类似,ANZSIC是4位编码(例如3232)。而Occupation是对应的ANZSIC的扩展,例如3232对应的ANZSIC还不足以区分投保,所以Occupation再3232的基础上再加2到4位,变成323201或者32320101。这样就解决了ANZSIC细分粒度的问题。
搞清楚这个事情以后,写到了一个Terms的文档中,并且在整个团队及相关的团队中都共享这个知识,每次有人错误提及的时候我都会纠正一遍。这样好歹基本上大家都搞清楚了。但这个混乱的成本也很大。代码中的混乱,已很难改变。很多JSON对象中也是乱套的用,多个Micro Service的API也不好改,甚至有些本该是ANZSIC的写成了Occupation,对类的重命名会造成类定义的混乱。这是个鲜活的例子,开发人员应该搞清楚每个名词的真正含义并不断修正之。
新加入团队的成员很是不理解为什么代码里面几乎没有什么注释。作为一个自解释的代码,保持代码短小,命名清晰,可以保证大部分时候不需要什么注释。但千万别走极端,代码无法解释的内容,例如一些比较难理解的设计用途,或者一些不通常的实现方法,都需要有一些注释作解释。
那么设计文档呢?可能有时候开发人员过于自信自己的水平,认为可执行的测试就表明了一切,就不需要文档。但他们可能没有遵循TDD,造成测试遗漏;或者没有能力管理好成千上万的测试;或者测试命名、意图描述的不准确。这些因素都可能造成一些歧义。让一个新来的人员直接看这些测试,直接就会晕死在各种细节中。
仅仅使用代码这一种文档形式与过度地使用UML图具有同样的基本问题。文档不应该重复表示代码已经明确表达出的内容。
我们项目每个开发人员会负责一个特性,在每个迭代的分析阶段会有一些文档性的东西支撑。但我基本不会将代码中实现的东西在文档中又说一遍,例如类图这种东西,一旦我重命名就完全不一致了。甚至API的设计,我只会说基本原则,并参考API测试,那里有你所需要的。最后,我会讲解整个特性的基本实现原理及一些约束,这种代码之外的补充文档才是充分有用的。