碎片数据收集利器:结构化动态表单设计思路
本文基于面向基本公共卫生的业务系统设计经验,抽象出一套适合大型ERP系统的表单业务数据模型,目标是最大限度保留系统弹性的同时,尽可能降低系统复杂度和开发成本。enjoy~
背景
填写表单应该是所有业务线条中最避免不了的环节,例如我所经历的医疗项目:
以上面两个例图作为示例,可以看到姓名、性别、出生日期、血型等字段是完全重复的,由于业务场景的差异,表单被定义了不同的样式和字段结构,此时将遭遇以下几种问题:
- 同一用户经历了两个不同场景时,不得不重复填写相同的字段;
- 如果相同的字段在两个表格中的值不同,基本无法判断哪个为正确值,例如同一个人在
居民健康档案
中血型填写为A型,而在居民健康档案信息卡
中填写为B型; - 某些字段会重复出现在不同的表单中,随着业务需要,将其串连起来查看其趋势,如身高、体重、血压、心率等等,以帮助医生确诊疾病,然而这些字段保存在各自的表单中,由于开发人员的变更、文档的遗漏和产品的迭代,无法穷举出所有的这些字段数据来源,即便能够回溯所有的来源,本身也是一件十分消耗精力的事情;
- 因为政策或业务需要,要在原有的表单上做调整,新的标准导致表单字段产生变化,此时原有系统为保证其运行的稳定,难以从数据表和底层代码中迭代,只能新增数据表做开发,当表单需求频繁变化时,加剧数据碎片化的问题;
- 新增业务表单时,开发需要订排期,用户需要等待发版后才能使用,新增大量表单时影响原有开发计划的同时,业务部门也难以快速开展系统业务。
- 做数据统计和分析时,由迭代造成的数据字段遗失或变更,无法统计出完整而准确的数据,做出的报告难以反映出真实的情况 ….
传统的区域化基本公共卫生系统正在经历这样的剧痛,当然其他行业比如金融的部分业务同样面临相同问题(本人只经历过这两个行业,见谅),如何在纷繁复杂的业务环节中抽离出四两拨千斤的数据模型,除了满足日益频繁的业务调整外,还能将数据完整的、标准的存储并利用起来,是后端产品经理的安身立命之本。
作为一个不务正业的产品经理,这次就从数据库表结构设计上,介绍一套解决方案:结构化动态表单。
场景和需求:
- 可覆盖绝大部分表单业务场景;
- 表单样式和字段可灵活调整,不影响历史积累数据,不会造成数据库和代码层面的频繁变更;
- 数据统计时能够快捷、准确、全面地获取到想要的字段数据,不过度依赖文档和程序员老员工;
模块介绍
属性库
所有表单中所有的字段都在属性库中定义,相当于表单字段的字典。定义的核心包含属性的唯一标识、属性名、属性值取值规则和约束等信息。
因为我认为所有的字段都是围绕某个业务进行的,把这个业务抽象成对象,那么这些表单的字段就是这个对象的属性,所以命名为属性库。
如果用关系型数据库表达属性库,根据以往的经验可以总结出如下两个基础表:
属性分类,主要根据使用者需求对属性进行分类,方便查找和后期的批量数据统计,比如健康管理把心率、瞳孔大小、脉搏等属性规划到生命体征类,把身高、体重等属性规划到基本体征类等等,因此仅需要定义唯一识别码、名称和分类说明即可。
属性,这个表非常重要,是数据标准化的基础表。唯一标识、名称、说明,这是一个属性最基础的说明,不用解释。
- 分类ID字段可支持多个ID,表示一个属性可划分到多个分类下,这个可根据实际需求定义,我所经历的场景是有这种情况的,比如心率,既可以是生命体征类属性,也可以是临床诊断类属性,很难绝对界定。当然属性和属性分类也可以通过单独建关系表来定义对应关系,方法有很多,各有优劣,看技术leader自行选型吧。
- 属性类型,根据个人的经验,总结出图中的几种类型,相信大家都认识,不用展开,其中单选框、多选框两种类型因为还依赖对应的取值字典,因此还需要到属性值字典中定义取值选项。另外
值单位
这个字段,方便做数据转换和终端数据展示用,比如时长的值60,单位是分钟,通过算法即可转换该单位的值为1小时。
属性值字典,主要用于配合属性类型为单选框或多选框的取值,也是数据标准化的一部分。
例如定义一个属性叫性别的属性规划到基础信息分类中,此时会属性库的三个表中分别插入以下数据: 属性分类表:ID=‘1’,分类名称=‘基础信息’,分类说明=‘用户基本信息’ 属性表:ID=2,分类ID=‘’,属性名称=‘性别’,属性说明=‘用户的性别’,属性类型=‘单选下拉框’,值单位=空 属性值字典:[ID=3,属性ID=‘2’,字典值=‘男’],[ID=4,属性ID=‘2’,字典值=‘女’],[ID=5,属性ID=‘2’,字典值=‘未知’]
模版库
所有的动态表单都是以模版的方式保存在数据库中,表单模版中定义表单中填写的字段、字段的默认值和表单样式。
由于表单样式的不可预见性,因此可以准备一套符合自家产品风格的视觉设计语言,限定表单视觉样式的框架,包含前面提到的属性类型呈现样式,和细化到UI在手写、PC端、移动端的字体大小、线条风格、交互方式、间距、缩进、比例、布局方式等参数,当然本篇由于篇幅限制不展开和视觉风格相关的讨论,读者可自行脑补。
既然是模版,肯定少不了控件,模版由控件组成,在这里把控件分为两类:属性组件和容器组件。
表单模版,是表单的字典表,用于定义表单的基础信息如名称、用途说明等,如果与业务衔接,还可以添加关联的业务、填写对象、触发填写的时间等,这部分信息由具体的业务场景决定,可根据实际需求设置字段。
容器组件,负责定义外观样式的组件,决定了属性组件在表单中的呈现样式,可根据不同布局需求细分更多容器组件,这里不展开细讲。
- 顺序号,在同级下的显示排序,从左至右,从上至下的原则进行排列。
- 容器名称,即表单中某方框的名称,可不填
- 在终端显示表单时,需要充分考虑各个组件在页面上的默认布局参数和可变参数。通常前面提到的设计语言中会定义标准的内边距外边距、线粗和线色等视觉样式,这些就是默认布局参数,但组件在表单中的显示顺序、嵌套关系和组件内的组件排列方式等参数多数时候是需要配置的,依据实际需求添加参数即可。
- 容器组件可嵌套,当遭遇多级层级关系时,用容器组件实现嵌套关系再好不过了,不建议属性组件也支持嵌套,因为会提高属性值的取值复杂度,除了开发和数据存储逻辑复杂度高外,后期数据分析时也会进入逻辑黑洞,应尽量避免
- 是否支持累加数据,此字段用于控制组件内的元素,是否可以按照定义的字段多组生成,例如如:
在容器组件
主要用药情况
中,属性组件药物名称
、用法
、用量
、用药时间
、服药依从性
的值可以添加多次。 - 还可以添加跟多字段或子表,描述容器更多的视觉布局样式,比如支持PC端、移动端、打印手写的样式定义。
属性组件,来自于属性库中的属性,决定了表单中填写的字段信息。
- 容器ID,当前属性组件放置在某个容器组件内,若值为null,表示直接放置在表单中
- 属性别名,为适应部分个性化的需求,可以为属性定义别名,比如身高,对婴儿通常叫身长,对青少年或成年人叫身高。别名定义到模版中而不是在属性库的意义在于,用户的个性化称呼通常只会在自己所处的场景内使用,对于其他场景下的其他用户并不一定通用。
- 属性默认值,很好理解,没用把这个字段放到属性库的理由和别名一样,场景不同,默认值不一定通用。
- 是否必填,表单提交前判断必填项的依据。
- 页面区域,用于判断当前组件出现在其父组件的位置,枚举类型。
属性组件还可以有更多可扩展特性,后面会提到一些。
业务数据库
有了前面的属性库和表单模版库的配置,即可配置出各式各样的表单,而实际使用这些表单保存下来的数据格式是怎样的呢?
表单主表,作为表单的索引表,主要是提供表单的填写来源、时间戳和与业务相关的标记。
通常实际业务有很多附加的信息,图中给出的是本人面临的业务场景常见的字段。
容器明细表,这里保存了表单内负责样式的容器数据,因为表单模版可能会变更,因此需要将其视觉样式数据保存下来,以记录当时呈现的样式,避免因为模版变更而造成的布局样式丢失。
属性明细表,保存了所有表单的所有字段的值。
- 为了配合容器组件记录其布局样式,还添加了容器ID、顺序号、组号、页面区域,用于记录保存表单时属性在表单中的位置。
- 组号,当遇到属性组件放置在允许累加数据的容器组件中时,标记出属性值所在的组。
- 别名,若属性在模版中配置了别名,则保存在这里,如果值为空,则显示原属性名。
- 修改时间,表单可能会遇到修改部分数据,因此标记字段的最后修改时间。如果有属性值的操作日志,可以不要。
样例
弊端
动态表单的存在,在一定程度上可以缓解产品迭代因业务变更带来的压力,但其开发复杂度较高,尤其表单模版,解析模版数据呈现到终端时,依赖遍历算法,对程序员的要求不低,若整套产品的应用规模不大,不建议使用动态表单,或者根据需求开发简配版。
由于表单依赖属性库和表单模版的配置,属性和表单模版的维护质量决定了表单的数据质量,因此需要有高度责任心和专业能力的人员来进行属性库的维护,提高了使用门槛,但反过来讲,罗马不是一天建成的,如果有野心建立行业标准,本身也需要大力投入数据质控。
设计思想和基本原则
- 产品开发和业务运行尽可能的解耦。业务人员不必完全依赖产品业务功能的情况下才能运行相关业务(这个问题单独靠动态表单无法完全解决,还需要依赖工作流,不过没有动态表单时也可以勉强适应部分场景做业务试运行);
- 产品永远做功能迭代,尽量避免数据迭代。常见的C端产品往往会有很多的营销推广广告页,这些广告页常常会频繁变化,而且为了抓热点往往需要即时响应跟进,如果按照每周发版1-2次的节奏,等发出来商业机会已经凉了,因此往往会做一个后台配置广告页的功能,使运营人员可以自行配置广告页面,包含页面元素、入口布局、外链引导、渠道埋点配置等等,这就是功能迭代。如果运营改一次广告,产品即发一次版,这就是数据层迭代。每一次变更都将累积相应的数据,产品是生成这些数据的工具,产生数据是业务人员做的事情,产品和业务是冰淇淋机和卖冰淇淋的小姐姐的关系。
- 用户永远只能看到功能,只关心产品是否满足其需求,而产品经理永远要从高低远近多维视角看待和解构需求,不断的整合、重组、拆分、归纳,穷举各种场景下的业务形态,在业务耦合和模块化处理上达到平衡,本质上是优化效率,创造利润,如果达不到这两个目的,这个产品不能叫产品,只叫艺术品。
- 提前预判功能模块的发展趋势,在设计初期预留充分的扩展性和迭代方向,避免高频率的推倒重来,当然如果是敏捷开发,请无视这条。
意义
- 属性库即数据规范,如果数据量在行业中足够大,适用面足够广的情况下,具备发展为行业规范的潜力。
- 表单模版即数据接口标准,当多个系统需要进行数据对接时,最头疼的往往是梳理数据对接标准,将需要对接的数据模版通过接口规范的方式导出给对接方,数据字段和取值规范一目了然,新老系统导数据也会用到。
- 数据利用更加便捷方便,需要查询某项具体的属性数据时,只需到属性明细表中即可找到,无需遍历其他业务表单
- 用户可通过配置表单明确新需求,表单模版一定程度上提高了用户需求和功能落地的沟通效率,一定程度上提高了产品的业务可扩展性
扩展性
以下是随意举例的可能的功能扩展方向,仅作为扩展阅读。
属性组件功能扩展
多属性之间的逻辑约束和默认值:
- 性别为男时,诊断中不可出现妇科疾病;
- 年龄在18-21岁之间,职业默认值为大学生;
- 填写了身份证号码即可解析出籍贯、性别和出生日期。
单属性复用:
姓名性别等字段用户一旦填写后,以后再次填写有这些字段的表单即可自动填写。
属性值字典 诊断、症状字典数据可能依赖外部接口调取,而非本地属性值字典库。
容器组件扩展
- 可配置容器背景图,视觉优化;
- 内部元素排列方式支持左对齐、右对齐、垂直排列、水平排列等;
- 可套用整体视觉设计的皮肤。
模版库扩展
- 模版插入公用参数字段,如表单中的制表机构、填表人、所在科室等等;
- 表单中的字段可作为工作流业务环节控制字段,比如当支付方式为现金时,无需弹出支付二维码。
操作日志
- 记录所有用户在业务表单上的操作记录,包含操作人、时间戳、修改前属性值;
- 记录所有用户在属性库和表单模版库上的操作记录。
本文由@鱼丸 原创发布于人人都是产品经理。未经许可,禁止转载
题图来自Unsplash,基于CC0协议
看了你的第一条评论回复。你说提升要用机器学习和量化决策。那是数据分析和处理上的是吧?跟报表本身又是另一个层面的事情。
这个是个简单共通的结构化动态表单思路实际应用更复杂,我做医疗信息化的产品经理,今年做的一个临床科研数据采集系统的项目就是这样,,要求更高划分模块更突出:数据集管理、表单设计配置工具、逻辑规则设计器、表单模板管理、数据源管理…….
临床类的项目复杂度比公共卫生更高,划分的模块更细,不同的学科知识库数据结构差异非常大,光靠这些可配置的数据采集器其实对业务本身提升并不大,个人认为可能需要往机器学习、量化策略等方向发展才可能有质的提升,但是这样一来,门槛又更高了,但也变得更加有趣了
总结的很到位,动态表单的思路很清晰。
就像文中提到,罗马不是一天建成的,如果有建罗马的野心,就不要在在战略层面偷懒。
另外,文中提到了敏捷开发,其实个人认为,敏捷开发与动态表单的设计理念并不矛盾。
就像作者提到,动态表单主要立足于建立统一的数据表结构规范,实现数据表层的动态管理,以适应碎片化的业务数据,避免因为业务表单变更新增而增加过多的数据表以及带来的一系列问题;而敏捷开发更多的强调的是对业务需求的快速响应与快速实现与迭代,两者的目标是一致的。
赞同,不过有时候敏捷开发在运营探路的过程中可能会发现其他商业机会,这种机会也可能会影响到战略上的定位,动态表单在一些轻快的项目中太重了,三大模块一个都不能少才能运行,有可能东西还没做出来,因为试探不成功项目就搁置了,所以产品经理真的需要仔细权衡,到底是为了快速实现战略目标抢占高地,还是为了用动态表单去做业务,杀鸡焉用牛刀。
从大局来说动态表单确实可以覆盖很多业务,在大战略上可以当做整套平台的基础设施进行建设,以充分发挥其效能,所以个人认为这种较重的模块适合在战略目标长期明确的情况下瀑布式推进。