系统防御性设计思维

82 阅读4分钟

文章首发公众号【风象南】

我们常听一句话,唯一不变的就是变化,那么既然变化无法避免,那么我们能做的就是适应变化。

简单来说就是设计或编码阶段预判了可能发生的业务层面或技术层面的变化,而提前预留了一定的变化适应性,在变化发生时可以通过少量修改或不修改的情况下完成功能交付,降低了后续的修改维护成本。各种设计模式可以认为是应对变化的一种解决方案。

但是,要解决问题我们都先知道问题在哪里,也就是你要解决变化的问题那就得先知道哪里可能会发生变化。

识别变化

如何识别变化,那么先看一个系统都包含哪些部分,一个相对简单的系统主要包含三个部分

1.业务
业务流程与业务规则

2.技术
技术框架、组件库各种技术中间件的访问如缓存、消息队列等

3.数据
核心为数据库中存储的数据

那么上面说的变化就在这三个维度中产生,下面分别看如何识别这几个维度中可能存在的变化点及如何在设计或代码实现时保留一定的变化适应性。

1. 业务

业务又可以划分为两部分,业务流程与业务规则。

业务流程指完成一件事所需要经历的过程

比如医院看病的流程

挂号 -> 医生问诊 -> 开单检查 -> 诊断开药

针对流程部分的变化一般可采用工作流或状态机类似的模式进行处理。

业务规则指具体执行时存在的约束条件和控制条件,可采用策略、模板等模式进行处理。

2. 技术

技术部分的变化比较纯粹相对容易识别,比如写代码的时候就需要考虑可能后期会更换缓存组件、消息组件、短信通道等,这种处理也相对容易,定义统一的访问接口然后按需提供多种不同实现即可。

技术部分应该养成一种习惯,设计或编码时顺手保留这种扩展结构。

很多时候就是懒。

3.数据部分

数据部分主要体现在表结构设计上,举两种场景

● 用户包含姓名、年龄、邮箱等基本信息,但是针对不同场景,需要扩展用户属性,而且差异较大且不通用,如何进行支持

○ 方案一:在原表t_user上进行扩展或者建立单独的扩展表
○ 方案二:原t_user表不做任何修改,增加 t_user_attr(user_id,prop_id,prop_value)属性扩展表

针对方案一不论扩展属性如何添加都存在代码改动,都需要修改前后端代码

针对方案二动态化处理后如果之前实现保留了动态属性处理逻辑,则增加属性仅需要在字典表增加属性定义代码无需进行修改

但是方案二也不是适用任何场景 ,比如扩展属性要作为查询条件但是prop_value因为存储了大字段等原因可能并不适合作为索引,此处只是提供一种思路来说明如何通过设计来应对变化,保持变化可能出现的敏感度。

● 员工入职从公司领取了电脑、手机,如何进行记录

○ 方案一:
t_user_phone(id,user_idphone_id),
t_user_computer(id,user_id,computer_id)
○ 方案二:
t_user_res(id,res_type,res_id),
其中res_type 代表资源类型电脑、手机等,res_id代表资源对应的ID
针对方案一如果现在新增了一种资源铅笔,
那么需要新增
t_user_pencil(id,user_id,pencli_id)
并增加相关代码

针对方案二如果有字典表维护了res_type资源类型那么理论上不需要修改代码即可实现资源的任意扩充
数据部分因为考虑数据库的性能因素,具体实现方式仍需要根据实际情况而定,技术要活学活用,没有什么是一成不变的。

总结

虽说上面说了要识别变化预留一定的变化适应性,但也不能走极端,所有地方都想保证灵活,好的解决方案不是过度设计,而是建立合理的扩展边界,让系统在应对变化时游刃有余,在成本与灵活之间找到相对合适的平衡点。