「Project」人事平台花名册档案模块重构设计与实现

由于历史原因和前期的产品快速迭代,人事业务中最核心的人事档案模块,代码开始变得越来越腐化,逐渐暴露出各种问题:1. 新人使用、接手、迭代的成本越来越高;2. 代码架构不合理,难以支撑更复杂的产品逻辑。这是这个人事档案重构项目开启的一个背景。

1. 重构背景及目标

背景:

  1. 档案存储的异构:人事的档案随着历史发展,存储一直是异构的,底层有多张表,还有一部分数据是从外部服务读取。这些数据一起构成了一个员工的档案;上层服务一直在理解这些存储,获取档案时,还需要理解哪个字段在哪个表,开发复杂度、新人理解成本都非常高;
  2. 模块封装太差:在系统的架构分层中,没有统一的档案域的读写入口,其他模块在执行档案的读写甚至会直接操作某个表进行读写。
  3. 新人接手成本高,影响业务迭代速度:读写入口40+,且多个入口含有不同的特殊逻辑。;

重构目标:

  1. 屏蔽异构的存储层,让上层不感知各个档案字段存储在哪里,直接使用统一接口更新即可:
    1. 人事档案对外提供的档案查询和写入,都只是key-value的结构,不感知底层存储;
    2. 实际底层存储是异构的,存储在不同的表中、不同的服务中;
  2. 易理解、易接入、易维护:新的重构代码需要做到这几点。这几点都是工程上的事情,如领域模型合理,代码逻辑清晰,注释清楚等等;
  3. 流量切换要保证业务正确40+的流量入口切换到新代码,如何保证逻辑的正确性,线上业务无影响?或做到影响可控,随时切换回旧逻辑;
    1. 历史遗留原因,档案的写入口很多,且不同入口含有一些特殊逻辑;
    2. 历史代码包袱很重,动不动就一个方法200+行代码,很难不有遗漏的逻辑;
    3. 因此要有一套黑盒机制,能够做到数据对账;即使完全不懂原来的代码逻辑,即使黑盒重构,也要能通过对账来发现问题。

2. 模块分层设计

  1. 统一档案域的数据模型,所有档案的更新入口模型只有一个
  2. 隔离底层存储,增加数据路由,配置化的方式,将不同的档案字段,路由到其所在的真实存储,执行读写。

3. 异步实时对账机制对账逻辑设计

3.1 为什么需要对账

本次重构,对账逻辑是要优先设计出来,因为业务不正确,重构没有意义。

要先设计一套,无论怎么重构,都能保证线上不出问题,保证每一个链路的新代码和旧代码的业务结果不一致,都能准确发现

如果是从代码内部逻辑来确认链路的正确性,是完全没有说服力的,也是不可行的,因为没有人能保证一个复杂大型应用的重构的每一个细节都关注并熟悉到,甚至与还有一些团内内都无人知晓的特殊逻辑存在。

因为代码重构,尽量保证业务逻辑的正确是基本的,但是不是可信的。对账应该把原来的代码当成黑盒,即使不知道哪部逻辑,也要能识别出新旧逻辑在最终写入档案的正确性

3.2 如何对账

为什么需要这种对账:对于图中的「旧逻辑」,已经无法(或者说成本很高)去梳理内部的业务逻辑了,包含了太多的特殊逻辑,以及某些逻辑是否还需要都无法确定,因此干脆直接将「旧逻辑」当成一个黑盒,我只关心对应走到「旧逻辑」的流量最终执行完成的数据是什么即可;

4. 档案模型设计

明确内部的档案领域模型;无论是查询还是写入,上层业务只对接同一个模型;

这里采用松散的设计,不再使用一个大的内存对象来定义每个档案字段;而是:企业元数据 + 员工档案;

  1. 对每个企业维护一套档案元数据;企业可以新增自定义字段,只需要在元数据中标识字段来源即可;
  2. 对用户的档案只是简单的key-value结构;

将员工数据,填充进企业的档案元数据,构建出员工的档案模型:

至此,档案的读写模型已经可以确定:

  • 读模型:企业元数据 + 员工档案,构建出带有字段元数据的档案模型;
  • 写模型:只需要写入key-value格式的数据;

5. 异构存储的路由设计

首先明确的是路由的是什么:路由的是档案的每一个字段;比如(职位、手机号、学历等等),他们都可能在不同的表、甚至不同的服务;

设计的目标:

  1. 在上层业务的视角中,不需要理解异构的存储,只有要查询、更新的字段;比如要查询某个字段,不需要关系从哪个表中取哪个字段;
  2. 路由层能够准确查询和更新到对应存储;
  3. 后续的场景扩展,需要做到轻量化,不改变整体代码逻辑,只需要增加字段,配置化达到效果;

字段路由的2种基本路由结构:

  • 针对固定的系统字段,维护在内存中的一个大的枚举中,并约束其所在的真实存储;
  • 针对企业的自定义字段,固定路由到自定义字段表;

6. 交付节奏

  1. 首先要将新逻辑重构完成;
  2. 将部分接口接入新逻辑,同时保留旧逻辑(双跑)
  3. 开发「对账逻辑」;
  4. 先发布一版上线,通过对账,来验证「重构逻辑」的正确性;
  5. 错误修复:针对线上对账出现的问题,执行修复,直到没有错误。
  6. 分批执行流量切换:将部分简单的逻辑,先切换到新逻辑。逐渐灰度放量;
  7. 继续分批流量切换:直到所有流量切换到新的逻辑上。
  8. 稳定运行期:流量切换完成后,运行1个月后,才可以说是稳定了。
  9. 旧代码下线:将整个重构过程中的特殊切换逻辑、对账逻辑、旧代码,逐步下线。
  10. 重构整体完成。

7. 技术栈

Spring、MySQL、Redis、RocketMQ