画外音:
一直都在收拾了别人的烂摊子,总结一下怎么做一个"人见人夸"的功能设计。
这个系列主要写给产品看,最佳实践不敢说,失败案例我有大把可以分享和吐槽的。
研发请随意,有不妥的地方欢迎指正。
前情提要:
当你不得不造一个轮子的时候,你需要先储备一些基础常识。内容很多,我们分期讲:
————————————————————————————————
二、组件设计的设计工程
当我们粗略定下来方案之后,就要开始规划功能了。
规划功能可不是上来就时序和类图做技术设计。
而是 拿出之前整理好的 干系人关注点 和 干系人阻力点。
详见: # 为什么你的轮子很难用(三):需要梳理方法和方案选型标准
开始思考:在功能中需要怎么融入日常管理 和 放大这个功能对各个环节的价值。
第一步:管理设计
为什么第一步强调管理思考呢?
因为 出了风险或者对线上有负面影响 就要被回滚啊。
如果公司有 事故责任认定的EP部门,还要扣工资,绩效和年终。
所以 麻烦大家先站在 老板的 角度想一想。
不然造一个轮子出来,把自己坑了那就实在得不偿失了。
就算你只是想 面向晋升和简历编程,最好也得掂量掂量。
作为老板,对于这个轮子考虑的是什么?
- 业务价值:有什么有用,在什么数据指标上能看到提升
- 成本控制: 联动全局上下游需要付出多少人力物力
- 落地风险:这个轮子会导致多少下游出问题,掉指标
- 精细管理:从这里能储备什么数据,对什么环节做控制
以上都是一个轮子需要考虑的东西。
所以在工作中,业务方/老板 问你要数据,
总是出现:这块没有埋,那块没日志,数据结构没设计这个字段.... 核心问题是什么:
是 早期在设计轮子的的时候,只顾着解决明面上提过来的需求交付功能。
而没有,基于多角色的需要,进行 功能流程,接口逻辑,数据结构的设计。
无论你后期时候需要 自动化或者智能化。都是建立在 早期的数据化搭建的。
别相信产品说只是试试,产品这个岗位就是为了改需求而生的。
真试错成功了,后面需求疯狂迭代起来,老板连重构的时间都不给你。
比如说:
能加上索引/唯一键/操作日志事件的都单独加上。有数值id就有加密id,提前哈希分表的就提前分表。
有添加就有删除,有查看就有导出,有单个就有批量,有存库就有hive,增删改查基本都别偷懒。
第二步:价值设计
价值设计,大白话讲: 这个轮子很好用,对业务/用户有帮助。
那 核心价值和核心场景是什么,外延价值和拓展场景是什么,要提前思考清楚。
考验的是你 能不能准确蒙中甲方未来的需求变化。
我们可以举几个例子:
1)比方说,要搞 一个优惠券发放。这时 产品跟你说 一个用户限制只能领取1次同个id的优惠券
真的傻到 按UID做幂等吗?这就扯淡了吧,那以后改多次怎么办,连累 上下游跟你一起换接口?
2)再比放说,记录一下作者的合同签约,先只做 添加签约和查询。
真的傻到 按UID做唯一键吗?一个作者一辈子只能签1次,这可能吗?那续约怎么办?
3)再或者说,上传一些卡号卡密的CDK。需要分多少种类型,能不能多次上传,上传要不要做去重?
真的傻到 按文件md5做哈希校验闭着眼存redis的list?稍微一改字符就能绕开,有必要做吗?
这跟脱裤子放屁有什么区别?
——
以上例子,都是我见过的真实案例。
太多人只考虑眼前,或者考虑自己当前搞起来快不快。
对外号称敏捷迭代,实际上给长期增加了大量的改造成本。
而这些本来稍微花几分钟枚举一下,就可以规避掉。
你自己造的屎山,大部分情况下都是连累你自己去维护的。
第三步:用例设计
这里考验的是每个人的知识储备,也就是你见过的情况多不多。
建议每个人都整理一下常见的用例,这对你后期CV一把梭乃至组件化都是有帮助的。
这里我们以一些业务后台admin最常见功能举例:
| 分类 | 注意要点 |
|---|---|
| 增 | 一共需要多少字段 |
| 选择什么字段做唯一键 | |
| 每个字段能否做校验 | |
| 配置选项之间是否有级联或互斥 | |
| 有单条新增怎么做多条批量 | |
| 多条数据间有重复是覆盖,去重还是打回 | |
| 删 | 能软删就不硬删除,先下架后删除 |
| 删除的前置校验条件是什么,时间/状态/被引用? | |
| 删除之后需要联动多少下游做变更处理 | |
| 改 | 什么类型创建后,是否不能修改 |
| 为了可以改,数据计算的策略是是么 | |
| 什么数据被修改可以回溯填补 | |
| 什么数据被修改可以回滚还原 | |
| 查 | 怎么存,存多久,怎么同步,怎么过期,要不要加缓存 |
| 怎么查,加什么字段做索引和查询 | |
| 有单条件,要不要加多条件 | |
| 有单个查,要不要加批量查 | |
| 返回内容怎么分页,怎么排序 | |
| 选择 | 能用树结构做分类就提前做 |
| 超过5个选项的内容就得加搜索 | |
| 除了单个,是否涉及多个选择,全局选择 | |
| 下拉选择没有,能不能直接在当前新增 | |
| 状态 | 能拆出多少状态:发布状态,时间状态... |
| 不同状态下,对于增删改查需要做什么校验处理 | |
| 时间 | 有单个起止,就有永久有效 |
| 有单个起止,是否需要多次循环 | |
| 有多次循环,就有无规律的多个起止 | |
| 日期和时间,是否需要拆分,进行特殊控制。 | |
| 同步 | 对方删除,没通知我,怎么做diff |
| 对方null,我怎么防御性编程,部份修改部分不改 | |
| 隔离 | 应用设计怎么跟业务分离 |
| 业务与业务之间怎么多租户隔离互不影响 | |
| 怎么实现针对某个业务的特殊逻辑 | |
| 接口 | 接口复用性怎么设计,通用字段和拓展字段是什么 |
| 使用有先后顺序的接口,能否合并提供功能 | |
| 怎么反爬,频控怎么做,限流怎么做 |
只有当你对绝大多数情况(用例)能快速枚举,了如指掌的情况下,才能抽象出一个合理的组件。
画外音:
很多人呢,面向部分场景,抽象了一个具备多态的模块,可配置输入得到对应输出。 就开始搞对外服务,说组件化了,然后业务方上来一瞧,怎么这也没有,那也没有,WTF 怎么用啊? 胆子大点的 连 租户服务隔离 都没做,上来就说自己是中台,让所有人中心依赖他。 有一个接入方QPS上去了,把系统打挂,就会连累所有接入方一起死。 比如最近520,腾讯的livelink中台因为斗鱼的调用QPS太高,连累上 B站/虎牙/快手等多家一起扑街。 其实很多事情的,是有硬性标准和行业规范的,只是大家都不愿意投入成本重视起来,最终囚徒博弈互相坑爹。
——————
当你了解了所有的要点,那就可以开始写用例了。
写用例,是一个人的基本功。
很多产品为什么PRD的功能逻辑,写不全,很大程度上就是没经历过 用例 的训练。
用例该怎么梳理呢?
一般通用格式是:
| 模块命名 | 前置条件 | 操作步骤 | 预期结果 |
|---|---|---|---|
| -- | -- | -- | -- |
这里我们以一个最简单的 列表状态管理 作为示意:
- 模块名称:列表页-列表-状态管理
用例1:
- 前置条件:进入页面,加载列表
- 操作步骤:无
- 预期结果:查询列表数据,展示每条数据的状态
1)上下架:以开关组件展示,绿色==上架,灰色=下架
2)状态标签:
若 当前数据为 上架状态
当 today < start time,状态标签==未开始
当 start time <= today < end time ,状态标签==进行中
当 today >= end time,状态标签==已结束
若 当前数据为 下架状态
状态标签=已下架
用例2:
- 前置条件:该条数据为上架状态
- 操作步骤:点击开关
- 预期结果:
校验当前用户是否具备操作权限,
若具备,
操作弹窗提示:"是否执行上架操作?"
点击确认按钮,开关切换为灰色,状态标签变更为已下架,页面toast提示"下架成功"
点击取消按钮,关闭弹窗。
若不具备,页面toast提示"您缺少操作权限"
用例3:
- 前置条件:该条数据为下架状态
- 操作步骤:点击开关
- 预期结果:
校验当前用户是否具备操作权限,
若 具备,
操作弹窗提示:"是否执行下架操作?"
点击确认按钮,开关切换为灰色,状态标签变更为已下架,页面toast提示"下架成功"
点击取消按钮,关闭弹窗。
若 不具备,
页面toast提示"您缺少操作权限"
看起来是不是和prd差不多?
其实 好的prd 可以直接当做测试用例。
那么现在,基于这个用例,我们就可以改写成prd,把这个模块大致写一遍。
- 模块名称:列表页-列表-状态管理
- 前端逻辑:
控件1:开关按钮
展示逻辑:绿色==上架,灰色=下架
操作交互:
1)下架操作:
校验当前用户是否具备操作权限,
若具备,
操作弹窗提示:"是否执行上架操作?"
点击确认按钮,开关切换为灰色,状态标签变更为已下架,页面toast提示"下架成功"
点击取消按钮,关闭弹窗。
若不具备,页面toast提示"您缺少操作权限"
2)上架操作:
校验当前用户是否具备操作权限,
若 具备,
操作弹窗提示:"是否执行下架操作?"
点击确认按钮,开关切换为灰色,状态标签变更为已下架,页面toast提示"下架成功"
点击取消按钮,关闭弹窗。
若 不具备,
页面toast提示"您缺少操作权限"
控件2:标签文案
展示逻辑:
若 当前数据为 上架状态
当 today < start time,状态标签==未开始
当 start time <= today < end time ,状态标签==进行中
当 today >= end time,状态标签==已结束
若 当前数据为 下架状态
状态标签=已下架
操作交互:无
控件3:页面toast
展示逻辑:
根据接口msg返回内容,展示 {报错码}:{报错文案}
在当前页面居中展示5秒后自动消失
操作交互:支持手动点击关闭
- 后端逻辑:
接口1:列表数据接口
入参:无
出参: {msg,1:{name,上下架状态,start_time,end_time}....}
依赖:略
实现:略
接口2:上下架操作接口
入参:id,操作值,uid,timestamp // 1-上架,2-下架
出参:{msg}
再基于这个模块,抽象出组件。
这个时候你就要考虑了:
- 前后端分离,业务逻辑放哪里:是做成纯前端组件,还是 前+后组件。这是不一样的选型。
- 后续遇到类似的拓展场景有什么,如何兼容:这决定了你的数据接口和组件设计。
- 基于现有功能,应该抽象什么出来字段实现配置化:这决定了你的组件被人用得爽不爽。
所谓 从需求到模块,是一个 复杂到简单的归纳过程。 但是 从模块到组件,是一个 从简单到复杂的演绎,再从复杂到简单的归纳。
从需求到模块的拆解,先到这里。 我们下一篇,讲一讲,如何从 模块 到 组件。