关于"业务逻辑拆分模式:file/dir/repository"的几点讨论 @taowen组织了一个微信群"业务逻辑拆分模式撰写组",在讨论中他提出一个观点

业务逻辑最终都是要分解成文件,文件夹,git仓库的。那是否应该从这个角度入手:什么东西适合用文件,什么东西适合用文件夹分,什么东西适合用git仓库分呢?

软件工程的本质是否就体现在这种分解过程中?是否存在一般性的可操作的规则来指导我们进行正确的业务逻辑拆分?在本文中,我想结合可逆计算理论谈谈自己的看法。

一. 树形结构:长程关联

我们可以从信息认知的角度来理解树形结构的作用。当信息匮乏时,我们只能认知到存在的一。当信息逐渐增加时,我们会识别出差异,认识到一分裂为多。如果认知的复杂度进一步增加,我们会识别出差异中的同,经过分组汇聚之后实际上形成一个嵌套结构。

Tree=List+Nested Tree = List + Nested

所以,树形结构是一种非常自然的认知框架。这个框架的一个令人瞩目的衍生特性在于:树可以有效的表达一种受控的长程关联。也就是说,当在父节点上施加某种控制的时候,我们会在所有子节点以及孙子节点上产生相应的影响,比如在根目录上控制访问权限。同时,节点对自己的父节点以及祖父节点存在确定且唯一的影响途径,比如DOM消息冒泡处理。

树形结构表达了整体和部分的构成关系,它的一个特例是父节点和子节点具有类似的结构,比如目录由子目录和文件构成。当存在这种整体和部分的自相似性时,我们只要掌握少数核心结构即可通过推演来理解系统整体结构。比如,在程序语言理论中,通过递归应用有限的语法规则,即可生成无限多合法的程序语句。这种现象在自然界中是广泛存在的,它被称之为分形(Fractals)。

树形结构的每个节点具有局部可区分的名称,比如文件名,同时它在整个树形结构中存在唯一的路径,这是在全局结构中可用于定位的坐标。比如,对于二叉树,我们可以通过二进制来为每一个节点赋予唯一的编号,例如0表示左分支,1表示右分支,1011表示沿着路径右左右右所到达的节点。

当我们认知世界并对世界施加控制的时候,我们需要一个有效的坐标系统,同时我们也需要一种有效的控制传递手段,所以树形结构就经常成为某种必然的选择。

二. 如果评价拆分方式的优劣?

首先我们需要认识到在给定的情况下,往往是存在多种可选的坐标系的。比如,在平面中我们可以选择无限多的X-Y正交坐标系。从一团乱麻中挣脱出来,我们需要的是一个认知上的切入点,具体从哪里切入不一定那么重要。比如,在不同的X-Y坐标系中,我们都可以建立类似的解析几何方程,并采用统一的代数方法去求解。

对于特定的问题,可能存在着某种最优的表达方式。例如圆这个结构在笛卡尔坐标系下是二维结构,当圆心为坐标中心时,我们可以很明确的识别出上下左右结构的对称性。但是,圆的本质是一维的,只有在极坐标系下我们才可以实现最简的表达。所以,最有效的表达是降维的。当我们进行拆分的时候,我们当然希望是沿着不变的边界进行分离, 例如坐标r保持为常量。但是,往往只有在演化的过程中,我们才能发现变化的脉络。而一个不巧的事实是,只有在时刻t我们才能观测到时刻t-1的演化结果,所以为了实现最有效的拆分,终极的一种需求是**把未来的知识以某种方式输运到当下*。

对于所有问题所构成的集合,显然不存在什么最优表示方案。在多个树形结构中进行选择时,决策树机制提供了一种评价标准:选择特征使得信息增益最大化(信息不确定性减少的程度最大),简单的说就是切分后减少混杂。但是出于不同的使用目的,我们的关注重点可能是变化的。比如某些情况下我们看重特征B,而另外一些情况下我们看重特征C等。受限制于人的认知能力、历史习惯、环境限制等,往往我们习惯于选择唯一的一种主切分方式,它只能是一种综合妥协的结果。

理想中的结构抽象应该是简单明了的反映领域中的本质关系。但是因为任何一个复杂结构事实上都不是直接构造出来的,而是逐渐生长出来的。生长依附于它所处的生态,并不会凭空的发生,而万物生长的过程又会反作用于生态,导致情况的进一步复杂化。例如,作为一个无神论者,你可能认为宗教是彻头彻尾的谎言,完全无用的痴言妄语。但是,人类社会围绕着宗教建立了庞大的经济、文化、政治体系,大量人类文明的珍宝依托在宗教这个看起来并不靠谱的概念体系之上,而且大部分时候社会运转良好。

到底什么东西适用于文件?什么东西适用于文件夹?什么东西适用于git仓库呢?从完全抽象的角度上说,我们所需要的可能只是一种贯通一致的、在各个层面统一的通用管理手段。但现实是,想要拿来就用的功能只在某些层面存在。比如,我们可能希望权限管控的基本单位是任意目录,但是git不支持啊,当然可以自己搞,但有挺多工作量,还有外围一堆配套工具的问题,还有使用者培训的问题,那还是算了吧。

文件存储是一种静态的表达,或者说是信息的一种序列化形式,它并不是我们知识的全部。比如,分子生物学发现生物的遗传信息本质上由ATGC等少数几个抽象符号构造的DNA序列来保存和传递。但是如果要真正理解这些信息如何起作用,为什么要这样组织,最终我们还是要参考它的运行时结构,发生在不同时空位置的转录、剪切、折叠等等知识。

三. 可逆计算对于拆分的新见解

可逆计算理论提出了一个新的软件构造范式:

	App = Biz x-extends Generator<DSL>

本质上,它对应于 Y = F(X) + Delta 这样一种分解模式。

首先,这是一种生成式系统。我们所表达的DSL信息并不是被直接使用的,而是事后可以通过转换器/生成器进行再解释的。当情况发生变化时,我们可以通过简单的将变化信息通过DSL模型输入到系统中,从而自动传播到系统各处。 大范围的信息传导机制便于实现拆分的局域化。

第二,DSL是对信息的一种结构化表述。在可逆计算的具体实现中,我们不仅仅需要引入结构化表达,同时需要引入对这种结构化表达的一系列处理手段,比如转换、合并、生成等。git在文件级别管理差量更新,而可逆计算需要把这个粒度推进到文件级别以下,同时将整个应用看作是一个完整的树形结构表达,来进行整体结构管控。

第三,Delta差量和自动化的差量合并机制是可逆计算的核心。差量是可逆性的自然结果

A=B+C==>C=A−B=A+(−B) A = B + C ==> C = A - B = A + (-B)

可以进行差量分解实际上是我们这个世界之所以能够被理解的一个本质原因。在数学上,任何一个解析函数都可以分解为Talyer级数,牛顿力学本质上对应于一阶线性项,而在物理中,我们总可以通过不断的追加二阶项、三阶项来逐步深化对系统的理解。

根据可逆计算的构想,为了有效的控制熵增,我们采用如下两个策略:

  1. 尽力保持系统的可逆性
  2. 通过Delta差量把不可控的混乱部分分离出主体

可逆性表现为加入系统中的信息需要能够很容易的分离并删除,比如很容易的屏蔽或删除某个特性。在开发阶段它可能表现为版本管理,在部署运维阶段则体现为自动回滚、灰度发布等。可逆性可以成为关注点分离的一种评判标准,同时它也能成为一种系统化的结构构造手段。

大型系统的腐化源于系统结构不断接受各类偶然性需求的冲击,根据热力学第二定律,一个自然发展的系统必然是不断熵增的(除非是不断新陈代谢的耗散系统,比如说不断重写)。但是不能控制熵增,我们仍然可以控制熵增的地点。基于差量机制,我们可以把偶然性需求隔离在某个差量Delta中。在可逆计算的具体实践中,所有逻辑都是可以进行差量定制的,因此在开发完一个产品之后,我们可以在完全不修改主版本代码的情况下,通过存放的差量描述,对主版本逻辑进行细粒度的定制。

在可逆计算的视角下,软件构造不再是从部分到整体的一种逐步组装的部分-整体构成关系,而是一种基于结构运算的转化关系。

      App1 = A + B + C
	  App2 = A + B + D = App1 - C + D = App1 + Delta

在完全不拆解App1的情况下,我们可以通过单纯的**"追加"**操作实现App2的构造。从生产模式上说,可逆计算将传统的"生产即组装"模式改变为更适合抽象逻辑结构构造的“运算即生产”模式。

所以基于可逆计算,我们对于业务逻辑拆分可以多问下面三个问题:可逆否?差量乎?今天你DSL了吗?