04.懒惰与隔阂
复杂性的来源
复杂性不是凭空而来,很多时候也不是刻意为之,这也就意味着复杂性的增加往往不会以我们的主观意志为转移。就像房间里的大象,我们无法逃避,也不能视而不见,本节即来讨论复杂性的主要来源点可能有哪些。
交互且无扩展性设计
当吸积效应导致的大规模系统,结合了交互这个特性,会使技术系统更加复杂。一个技术系统除了作用于自身,还会与其它大量系统产生交互。比如下单购买一件商品,那么订单系统,商品系统,支付系统,物流系统,卡券系统就会交互协作。这样吸积的复杂性,由于交互特性的出现,会呈现几何级数上升。交互带来两种特性:第一是互操作性。在搜索引擎上查询去某地怎么走,搜索引擎会告诉你打车需要多长时间,并提供一个打车按钮。第二是相互依赖性。比如下订单时需要使用优惠券,那么当卡券系统出现问题时就会影响订单系统。
对于只有一个业务的简单场景,并不需要扩展,问题也不突出,这也是为什么这个点经常被忽略的原因,因为我们大部分的系统都是从单一业务开始的。但是随着支持的业务越来越多,代码里面开始出现大量的 if-else 逻辑,这个时候代码开始有坏味道,没闻到的同学就这么继续往上堆,闻到的同学会重构一下,但因为系统没有统一的可扩展架构,重构的技法也各不相同,这种代码的不一致性也是一种理解上的复杂度。久而久之,系统就变得复杂难维护。像典型的 CRM 应用,有 N 个业务方,每个业务方又有 N 个租户,如果都要用 if-else 判断业务差异,那简直就是惨绝人寰。其实这种扩展点(Extension Point),或者叫插件(Plug-in)的设计在架构设计中是非常普遍的。比较成功的案例有 eclipse 的 plug-in 机制,星环系统等。还有一个扩展性需求就是字段扩展,这一点对 SaaS 应用尤为重要,因为有很多客户定制化需求,但是我们很多系统也没有统一的字段扩展方案。
不合理的业务封装
不合理的业务封装是一个相对宽泛的概念,其具体的表现譬如面向过程而不是对象、分层不合理等。面向对象不仅是一个语言,更是一种思维方式,但是很多时候我们更会用事务脚本的方式,面向过程地来编写代码。譬如 DDD 最大的好处是将业务语义显现化,把原先晦涩难懂的业务算法逻辑,通过领域对象(Domain Object),统一语言(Ubiquitous Language)将领域概念清晰的显性化表达出来。
另一个不合理的业务封装,就是不合理的分层;俗话说,计算机科学领域的任何问题都可以通过增加一个间接的中间层来解决。分层最大的好处就是分离关注点,让每一层只解决该层关注的问题,从而将复杂的问题简化,起到分而治之的作用。但是,并非分层越多越好,过多的层次不仅不能带来好处,反而会增加系统的复杂性和降低系统性能。分层太多和没有分层都会导致系统复杂度的上升,因此我们的原则是不可以没有分层,但是只分有必要的层。
缺乏统一语言
典型的敏捷开发的结构,流水线上的各个角色往往会专注于自己负责的环节,精细化的分工也限制了每个角色的全局视角;虽然我们经常提倡所谓的主人翁意识,但是在落地时又很难去推进。
Model‐Driven Design is the process of binding an analysis model to a code implementation model, ensuring that both stay in sync and are useful during evolution.Model‐Driven Design differs from DDD in that it is focused on implementation and any constraints that may require changes to an initial model, whereas DDD focuses on language, collaboration, and domain knowledge.
Historically, the capturing of requirements for software systems was seen as an activity that could occur long before coding was due to start. Business experts would talk to business analysts, who in turn would talk to architects, who would produce an analysis model based on all the information from the problem domain. This analysis model would then be handed over to the developers, along with wireframes and work flow diagrams, so they could build the system.
As developers start to implement the analysis model in code, they often find a mismatch between the high‐level artifacts produced by architects and the reality of building the system. However, at this stage there is often no feedback loop for developers to talk to the business and architects, so the analysis model can be updated and their input enacted. Instead, the developers diverge from the analysis model, and their implementation often overlooks important and descriptive domain terms and concepts that would have provided deeper insight and understanding of the domain.
As the development team further evolves away from the analysis model, it becomes less and less useful. Crucial insight into the model is lost as the development team focuses on abstracting technical concerns instead of business concepts. In the end the job gets done, but the code bears no reflection to the original analysis model. The business still believes the original analysis models are correct and is unaware of the alterations within the code model.
事先设计(Upfront Design)的弊端在于,随着业务的变化,因为领域专家与技术人员有不同的模型理解,代码库会变得失去控制:
缺乏约束与规范
在团队协作开发的背景下,缺少规范和约束会严重损害架构的一致性(Consistency),代码的可维护性将急剧下降。可能规范在实现层面就是命名、分包等不影响代码运行的小问题,但是千里之堤,溃于蚁穴,正是这些微末的不注意导致了整体复杂性的雪崩。
最好的约束就是在架构层面,通过 Lint 或者运行时检测的方式针对不符合规范的代码报错或者抛出异常。在架构的约束之外,我们还需要靠 Code Review 来随时发现不合适或者 Bad Smell 的代码。