@windwolf
2017-04-19T05:07:22.000000Z
字数 3195
阅读 940
Sailing
作为一个IT服务型企业, 我们的ERP产品定位为可以进行二次开发的标准版本, 因此, 必然会在标准版本的基础上衍生出各种项目版本.
与此同时, 标准版本也在向前发展, 会产生多种升级版本.
此外, 一个ERP产品必定是由多个模块组成的, 而模块之间有必定存在依赖关系, 而且模块间的升级节奏往往也不一致.
因此, 版本控制实际上需要管理 产品与项目关系, 模块依赖, 版本升级 这三方面的各种关系.
在以往的实践中, 我们对代码有一定的版本控制策略, 对以上三个方面都有不同程度的覆盖.
我们试图一定程度上解决以上问题.
一般来说, 一个产品往往有多个项目同时在实施, 产品和项目中都有多个模块, 而无论是产品还是项目, 模块都可能会有版本演进, 而不同的版本的模块可能依赖其他产品或项目中的某个模块的某个版本.
因此, 产生了产品/项目--模块--版本这样三个层级关系, 并且在第三层上, 会有错综复杂的依赖关系.
下图描述了一个产品P, 同时有两个项目P1, P2在同时开展, 产品和每个项目中有A和B两个模块, 而这两个模块有各自的演进路线及依赖关系.
粗实线表示项目内的模块版本演进, 点划线表示产品到项目的演进, 而虚线表示模块依赖关系.
暂不考虑模块依赖, 那么产品/项目--模块--版本三者之间将表现出一种层级关系. 第一层是产品/项目, 第二层是模块, 第三层是版本, 任何变更都能通过[产品/项目].[模块].[版本]来定位.
下图的每个节点代表某个项目的某个模块的某个版本, 节点的名称表示了这个定位关系, 而连线表示了他们之间的演进关系. 颜色用来区分不同的项目.
上图中有两棵树, 分别代表了模块A和B的版本演进线路, 版本演进中有些分叉, (一般来说)这些分叉都表示了一个项目的开始.
上图中的树属于某个模块, 暗示了模块的概念也许比产品/项目更加具有实质性. 这是有道理的. 模块有其对应的现实实体, 他包括了代码, 数据库定义等, 而产品/项目仅仅是一系列模块的集合, 是个管理/营销概念, 因此, 模块, 产品/项目, 版本的概念可以归纳为以下几点:
这样一来, 整个版本控制模型可以表示为一个森林, 森林中的一棵树表示一个模块的演进过程, 每个树节点表示所属模块在某个项目或者产品中的某个版本, 树节点的分支表示了某次项目化.
因此, 可以明确地知道是否可以从A版本升级到B版本, 如何升级. 只要能从A版本出发向下能找到一条路径到达B版本, 那么就必定可以升级, 且这条路径就是升级的路径.
再考虑有模块依赖关系的情况.
![]()
图中的虚线表示了模块版本的依赖关系, 箭头方向表示[依赖于]关系.
如何回答是否可以从A升级到B, 如何升级这个问题.
此时可以把依赖关系理解为升级路径上的前提条件. 也就是说, 如果想要升级到版本A, 必须首先将其依赖模块升级到指定的版本. 那么能否从A升级到B的标准除了之前提到的有无直接通路以外, 还需检查通路上的所有节点的依赖模块是否可以从升级到指定版本.
只要模块间的依赖关系不会出现闭合, 也就是不存在循环依赖关系, 那么任何一个模块的版本需要升级, 最终都可以按照上面的思路递归的表示成一个升级方案.
而模块间不存在循环依赖几乎是任何模块化系统设计的基本准则. 因此, 一定存在一个升级方案, 且这个方案可以通过系统性的办法来得到.
再次回顾需要解决的问题:
经过对版本控制的分析, 解决方案的原则也就十分明显了:
但如何落实还有一个问题需要解决: 如何保持代码和数据结构的版本匹配.
再次考察版本演进树的一个重要特性:
知道了A模块的当前版本信息, 以及其他模块的版本信息, 就可以推算出A模板能否升级到其他版本, 如果可以, 升级方法如何.
因此, 只要数据结构的版本是可以升级到代码版本的, 那么总有办法将数据结构一步步升级到与代码一致. 当然, 有个前提条件, 就是代码和数据结构分别记录了当前版本.
至此, 解决方案就浮出水面了:
传统产品中, 只涉及代码, 业务数据结构, 和业务数据. 一般来说, 只需在代码和业务数据结构版本不一致时, 执行升级方案即可.
但Sailing的体系中还有一层元数据. 那么就变成了, 代码, 元数据结构, 元数据, 业务数据结构, 业务数据 这五者. 此时, 情况稍微更复杂了一点点. 代码和元数据结构是需要保持版本一致的, 并且,代码和元数据结构的版本变迁也可能会影响元数据, 业务数据结构和业务数据. 因此, 代码版本演进的迁移操作需要覆盖后四种数据.
元数据和业务数据结构之间也有版本完整性的约束, 元数据的版本演进有可能造成业务数据结构的版本失配. 但目前Sailing的设计恰巧避免了此类问题的出现, Sailing的业务数据结构是定义在代码层面的, 也就是说, 业务数据结构的定义和代码版本不存在失配的可能性, 因此只要在代码版本迁移中完成数据库业务数据结构的版本升级即可解决此问题.
另一方面, 元数据的版本演进可能会造成和业务数据之间的版本失配. 这个问题有其他方面的额外需求, 需要用另外的手段解决, 专题讨论.
除此之外, 元数据版本变更再没有其他情况了, 因此不必采用版本演进的做法.
版本前继的的切换
举个例子, 标准产品P中模块M的当前版本为P.M.1, 在项目P1中对M模块做了个性化修改, 版本陆续演进到了P1.M.3, 后续标准产品中模块M也陆续升级到了P.M.3, 项目甲方考察之后决定升级P.M.3, 但按版本演进规则, P.M.3不是P1.M.3的直接后继, 不能升级, 甲方表达了迫切希望在保持个性化的同时升级的意愿, 愿意另外支付升级改造费用. 如何处理?
此时, 甲方实际上是希望有个版本即包含了标准产品的各种新特性(P.M.3), 又包含了项目中各种个性化(P1.M.3). 而P.M.3中的特性是从项目分出去开始(P.M.2)到目前版本(P.M.3)之间的所有的版本演进, 而项目个性化则包含了项目分支创建伊始(P1.M.1)到目前版本(P1.M.3)的所有版本演进. 因此甲方需要的是P1.M.4, 他从P1.M.3演进而来, 但包含了P.M.3的所有新特性. 如下图所示:
这类操作需要非常谨慎. 不能直接使用P.M.2, P.M.3的版本迁移功能. 因为这个版本的迁移工具都是基于标准产品, 直接应用到项目版本会有意想不到的问题. 因此, 必须仔细分析从项目分支出来开始, 到目标产品版本之间的所有版本演进, 并给出升级方案.
因此, 并不建议做这种类型的版本升级, 而是尽量用元数据配置的方式来完成个性化.