仓库源文

.. Kenneth Lee 版权所有 2022

:Authors: Kenneth Lee :Version: 0.1 :Date: 2022-09-19 :Status: Draft

在语义上评价逻辑


这本来是单位内部的一个具体设计的单位公开范围讨论,但里面的很多要素都值得作为一 个通用的例子来讨论,我根据记忆把其中比较重要的要点记录下来,作为我的架构案例库 的一个例子。

这个例子来自一个设计规模很小的设计,作者要做一个网络应用的数据库切换方案,把整 个Web应用的数据库方案从A切换到B,核心原因是A数据库需要两个开源软件组合起来完成 功能,而B数据库的方案只要一个数据库就可以了。

这是我现在给你的总结,但其实我第一次看他的文档的时候,我不太能看出他的目标是这 样的。这是我要在本文中想强调的第一个要点:

我们很多时候学习架构设计方法,听讲述者说某种设计理念,你觉得这个判断很容易,也 觉得自己学会了这个方法。但你在现实中操作,你发现你根本做不出一个水平的设计。这 背后的原因是:构架设计真正的困难不是根据描述好的东西决定一个选择。而是你怎么去 “总结”那件事情,能总结出来,选择反而是一件容易的事情了。

很多人看了很多架构设计的书和教程,学了半天只学会了怎么画UML图,DFD图,等自己拿 这些图去解决问题的时候,只能用那些图去还原代码,根本起不到对问题实际进行“总结” 的作用。背后就是这个原理。

实际上,这个设计的作者,很多“抽象”层次的设计理念跟我是很接近的。但他这个设计我 觉得完全是走偏的。他怎么走偏的我也没法给你形容,因为走偏的东西不见得是有规律的, 他总结的需求就是一些散乱的点,比如双写,数据库中有Proxy,使用的开源软件的版本, 这些描述都“对”,但那些散乱的信息没法让你作出决策。

所以,大部分时候,我们不能从抽象的逻辑上看到一个设计的好坏,我们要谈那个具体问 题的逻辑本身。我们讨论“这顿饭里面蚝油菠菜盐放多了”,而不是讨论“调味料需要有一个 度”。调味料有度我们都知道,而且我们不知道这个度在什么地方,那个我们没有结论。但 你现在这个蚝油菠菜确实盐放多了。这个我们能讨论,我说不定还能告诉你,这个菜你不 要放盐,其实蚝油已经够咸了。

这个设计中我看到的第二个问题是是没有“筹划”,比如它会这样描述:“业务之前有从XX模 式割接到XX模式的经验,应该是原来业务割接机制定的。业务也没有大量的人力去之后次 改动代码去支持配套我们的割接方案。所以整个割接方案需要我们自己出。”

我个人倒是很喜欢这种直白的描述问题的风格。但这不够,因为设计是一种筹划,筹划是 把筹码摆上来的选择。比如“去北京有船,车,飞机”三条路,好坏分别如下:巴拉巴拉…… 所以我们选择了飞机。这东西值得设计,因为不做这个选择,我们都不知道应该怎么开始。 这种事情没有了设计,操作就会错,就会缺乏“筹划”。但前面这种报告式的文字,我们又 不是来听故事的,作为设计的主体,就没有设计的作用了。

关键是,在这个后面,作者就开始画Use Case图了,而且这个Use Case是在描述割接的方 案。如果我们是做筹划,Use Case应该画的是原来整个Web应用的Use Case,我们筹划我们 割接了以后是否还能保证原来的所有能力。这个Use Case图起到一个“逻辑闭包”的作用, 所有功能都在一个视图中呈现,我们可以知道这些功能是否互相冲突,我们可以进行取舍。 你单独建模一个“割接”过程,这有什么需要“筹划”的呢?

这是一个直接的问题,知道了可以修改,但我其实更关心的是设计中一个更重要的,思路 上的误区:我们写下的每个字,画出来的每副图,都是为了制造约束。是我们把条件排上 去,发现我不满足这个条件,我们目标就无法无法达成,所以我推理出这些约束,是为了 连续传递给下一层逻辑的,你前面描述这个“割接”过程,是后面设计的“约束”吗?是为了 保证你一定要“割接”吗?“割接”是代码的实施过程,不是编码什么的指导啊。用这种方法 画Use Case说明作者只是因为要画Use Case所以画了Use Case,不是因为作者有什么下一 级有什么设计问题需要决定,才画了这个Use Case。这才是大部分设计文档最大的问题: 设计不是解决编码的问题,而是为了完成某个仪式,那么这个设计就没有价值了。

再说第三个问题,这个设计其实有一个很明显的“逻辑闭包”空间:他有n个实际的现网版本, 他也列出这些版本了,我本来预期他会列出他要做多少个版本的,结果他只有区区两个“原 则”,比如,开发需要兼容这些网上版本,出现这种情况很多时候是为了不用承诺。

我们考虑一下这个过程:我们有10个现网版本,为此我们用一个源代码编译出3个二进制版 本,替换网上的10个版本。但这个事情不容易的,开发一套源代码,但出三个二进制版本, 说起来只是编译,其实是三份的测试工作量,而替换网上的版本,更加不是开发者自己可 以决定的,不但要冒市场风险,也要相应的工作量,更总要的是,还需要客户答应,你不 一定能做到。把目标写下来,会有很多现实的问题要解决:

  1. 提前承诺搞定这10个版本,事后复盘的时候怎么面对管理层的批评?
  2. 面对问题更加复杂了,本来以为只有3个版本了,结果有5个替换不掉,现在可能有8个 版本。
  3. 部分版本推不上去,有一个版本开发出来完全没有用,白做了

这些都是执行上的“不好看”,很多人不愿意暴露出来,所以,你因为你只是“懒得写”这么 多,其实本质上你不想面对“问题”,而设计,我们就是强迫我们“理智”地面对问题。

第四个问题,其实这个文档,如果最后写一个总结,总结一下这个文档“说了写啥”,就能 看出全文逻辑是不连贯的,所以,其实如果我们能够在最后都写一个Summary,就文字把原 文的全部内容和逻辑总结一下,可能效果会好得多。

最后一个问题,这个设计无条件引入了一个交“双写”的设计。大概的意思是在切换期间, 同时配置新老两个数据库,数据同时写入两个数据库中,应该是怕新的数据库有问题,到 时新的业务数据全都丢了。

这个问题我希望强调的问题是:我们很多人都很不在乎“需求分析”,人们觉得需求是“显而 易见”的,但其实如果我们把需求向“收益”这个角度去靠,就会发现,需求并非那么“显而 易见”的。

双写这个需求,说起来好像都知道什么意思,就是两个数据库都写而已。但为什么要双写 呢?双写是个形式而已,并不产生利益,产生利益的是“数据丢失的时候可以恢复”。如果 是这样,我就应该尽量让原来的流程完全保持原来的流程,新数据的流程应该是正交的。 因为只有这样,数据丢失的机会才是最小的。但实际上,他的设计是让两个数据访问接口 互相同步。那你告诉我,如果新数据库提供了错误的反馈,是不是老数据库的数据也丢失 了?你还能得到原来的目标吗?

你看,这样针对目标的一个细化,就能发现逻辑上的问题,这种东西才是设计,才是需求 分析。