画外音:
一直都在收拾了别人的烂摊子,总结一下怎么做一个"人见人夸"的功能设计。
这个系列主要写给产品看,最佳实践不敢说,失败案例我有大把可以分享和吐槽的。
研发请随意,有不妥的地方欢迎指正。
前情提要:
当你不得不造一个轮子的时候,你需要先储备一些基础常识。内容很多,我们分期讲:
三、组件化
第一节:设计思想
在讲组件化之前,我们需要 梳理几组概念:
1. 可维护性/可复用性/可拓展性:
1)可维护性:指软件能够被理解、修正、适应及扩展的难易程度。
2)可复用性:指软件能够被重复使用的难易程度。
3)可拓展性:指软件适应未来需求变化的改造成本。
也就是说:可扩展性是可维护性的一部分,修正重修,扩展重增。
大部分轮子的难用,无非就这么几种情况:
1)可维护性不强:
出问题不知道怎么查,故障定位不好定。
知道问题不好改,牵一发动全身,隔离没做好,引发各个子功能出问题。
2)可复用性不强:
无法多次重复创建或利用这个对象。或者 有个相同的需求,接入成本特别高。
得多出来一堆与接入方无强相关的东西,各种依赖/调试/配置步骤一大堆。
(注意:这是技术层面的复用性,不是业务层面的复用性。)
3)可拓展性不强:
功能架构/数据结构/接口封装 等等设计不合理,加个功能得到处改,甚至于大范围重构。
2.面向过程/面向对象/面向切面:
1)面向过程 POP:
描述的是功能逻辑执行步骤是如何组织的。
也就是:对象之间是怎么进行处理与计算的,以及 对象内部从输入到输出是怎么处理计算的。
比如
步骤1:A 打 B ,
步骤2:A 打 C 。
2)面向对象 OOP:
描述的是 功能逻辑中 所涉及的 对象及其行为。
也就是:我是谁,别人输入什么,我将输出什么。
同上例:
对象1:A,行为 打(人),
对象2:B,行为 被(人)打,
对象3:C,行为 被(人)打。
3) 面向切面 AOP:
描述的是 对共同行为的抽象封装,实现调用方与被调用方解耦 乃至 实现功能逻辑的分层治理。
比如 区分业务逻辑与公共逻辑,核心逻辑与支撑逻辑,将可重用的部分剥离出来固化。
同上例:
调用方:A ,
切面:打(人),
被调用方:B / C。
当你大概get到这几组概念,那我们就可以开始来讲组件了。
第二节:什么是组件
我们来看一下某些学术定义:
是一个组装单元,它具有约定式规范的接口,以及明确的依赖环境。 构建可以被独立的部署,由第三方组装。
看着很绕口,让我们大白话翻译一下
1)有约定规范的接口: 有固定的输入与输出内容,且输入到输出执行逻辑是稳定的。
2)明确的依赖环境:在特定工程范围下才能使用,脱离了这个环境用不了。
3)构建时可独立部署:可以多次创建与利用,每次创建或利用是隔离开的,不会互相影响。
4)由第三方组装:这是一句有歧义的话,因为第三方不代表所有的第三方都可以。
问题就来了,什么是一个组装单元?
这个问题是不是很眼熟?跟# 为什么你的轮子很难用(二):从战略到抽象2 我曾经问的一样,什么是一个实例?
让我们再次回到这张图:
基于前面的认知,我们给出一个新的定义,
组件:在特定工程范围内,具备 可复用性的 一个 实例/模块/页面/应用/系统。
组件化:某个实例/模块/页面/应用/系统,在特定工程范围内,具备可复用性。
也就是说,组件是比它大一级的对象所定义的。
让我们来举个例子:
这里有 一个X系统,它封装了多个调用接口,
此时 公司内多个系统可以调用 X 来实现 与其他系统无关的 特定功能逻辑。
== X 具备组件化能力。即 X在公司内部是一个组件。
而 X系统是由 M+N...多个应用组合起来的,且 X可以重复创建利用,灵活编排 M,N...
== M...N具备组件化能力。即 M..N应用在X系统内部是一个组件。
此时你就明白,复用和复制的区别了。
复制:CTRL+C CTRL+V, 在一个或多个工程范围内,有一模一样的多个对象
复用:在同一个工程范围内,重复创建 或 利用 这个对象。
所以经常运营问一个问题:
明明这里已经有了功能A,为什么在那里不能加上直接用?
答案很明显:压根不在一个工程范围内。
第三节 一个组件有多复杂
1.碎碎念:
首先 当你需要组件时:也就意味着 你需要多次创建或利用它。可就是所谓 可复用性。
那么你 不光要关心 组件内部的逻辑,更要关心 组件与外部的关系。
组件本身是一个对象,这个对象本身包含相应的行为。
行为的执行,具备步骤的,也就是说 对象的内部 可以是面向过程的。
( 如果 组件内部可以再细颗粒度的拆分,也可以是 面向对象 乃至切面。)
而组件与外部的关系,是面向切面的,脱离了共同行为的统一抽象封装,就丧失了复用性。
那组件与外部的关系,就需要思考 对象的层级。
2.对象的层级需要分端思考:
之前我们讲过从宏观到微观是从 layer 到 entity。
如果说 layer到entity为横坐标,是 一个平面内的 宏观 到 微观
那分端作为纵坐标就是对象的层级,是 多个平面上的关系,
第一个大平面:大前端
移动与PC( H5--webview--bridge--客户端--操作系统) // 小程序,RN,flutter同理
web ( web--浏览器--操作系统)
第二个大平面:大前端与服务器的串联
( API/soket--CDN/网关BFF--node),
第三个大平面:业务后端与服务器的串联
( API/soket--服务器/容器)
第四个大平面:业务后端与业务admin
( 业务后端:MVC/MVP/MVVN/MVI--服务端之间的传输:RPC/MQ/kafka )
( API--各种各样的WEB配置admin)
第五个大平面:底层中台和中台admin
各种各样的底层服务:音视频 / 用户信息 / 关注 / 分享
本质上也是:
( 业务后端:MVC / MVVN--服务端之间的传输:RPC/MQ/kafka )
( API--各种各样的WEB配置admin)
第六个大平面:数据基础设施
( 数据存储:mysql / ES / redis / hive / flink / hbase / mongoDB....)
( 数据计算:spark / hadoop )
多个层级的关系,即 从 前端 到 后端,从 内部服务 到 外部服务 ,从 业务服务 到 基础设施
让我们 再改写一下 组件的定义:
某端的组件:在特定工程范围内,具备 可复用性的 一个 实例/模块/页面/应用/系统。
某端组件化:某个实例/模块/页面/应用/系统,在特定工程范围内,具备可复用性。
—>
业务的组件:由1到多端的 实例/模块/页面/应用/系统 有机组成,且整体具备可复用性。
业务组件化: 将1到多端的 个实例/模块/页面/应用/系统 有机组织,改造具备可复用性。
知道 你为什么每天被研发绕晕了吗?
因为 9成9的概率,他讲的组件,是他那一端的组件,不是你业务意义上的组件。
另外,为什么我只写到 系统,因为 业务意义上的组件 最多也就到 系统 这个宏观视角了。
尤其 一个系统,大概率 是前后端共同参与的,有用户端,管理端,sever。
你说 如果 admin管理端 聚合了多个系统且具备可复用性 叫什么?
如果 吹牛逼,可以叫 XX平台 or XX中台了。
第四节:组件是怎么前后端分离的
如果你已经熟练掌握 一个 mpa 应用的写法,比如像我们之前梳理的PRD模板。
你应该有了起码的 前后端 分离 意识。
备注:作为一个 次抛型的MPA应用的PRD,它应该是这样的。
页面层逻辑:对所有页面进行总体说明
1.页面概述:一共有M个页面N个模块
A 页面:XXXXXXX
模块1:标题xxxx
模块2:标题xxxx
B 页面:YYYYYYY
分区1:yyyyy
模块1 :标题mmmmm
模块2: 标题nnnnn
...
2.页面胶水
当未登录时,不显示A页面和B页面,仅显示C页面
当已登录时,展示A页面和B页面,不显示C页面
模块详述:对单个模块进行书写
所属页面:页面XXXXX
所属分区:分区XXXXX
父模块1:标题XXXXXX
模块综述:该模块由 A+B+C 三个子模块组成
胶水逻辑:三个子模块,默认展示第一个。展开其他时,其余收起。
子模块A:标题XXXXX
前端逻辑
A 实例1:XXXXX
1)展示逻辑: 从M,根据 X 取 Y ,在N 进行数据展示
2)交互逻辑: 一共有哪些状态,怎么展示,怎么操作,不同状态有什么交互
3)胶水逻辑:(与其他前端对象的联动逻辑)当XXX模块发生XXX行为,此时该模块的XXX需要变更XXX
4)埋点逻辑: 模块ID--模块名称--交互行为(曝光/点击)上报内容{ xxxx , xxxx }
B 实例2:XXXXXX
1)展示逻辑: 从M,根据 X 取 Y ,在N 进行数据展示
2)交互逻辑: 一共有哪些状态,怎么展示,怎么操作,不同状态有什么交互
3)胶水逻辑:(与其他前端对象的联动逻辑)当XXX模块发生XXX行为,此时该模块的XXX需要变更XXX
4)埋点逻辑: 模块ID--模块名称--交互行为(曝光/点击)上报内容{ xxxx , xxxx }
后端逻辑:
A 接口1:XXXXXXX
1)依赖逻辑:从哪个后台配置什么后端功能, ID是多少
2)实现逻辑:使用什么数据,计算什么,判断什么,
3)存储逻辑:数据存多久,如何更新同步
4) 接口逻辑:出参是...,入参是......
B 接口2:XXXXXXX
1)依赖逻辑:从哪个后台配置什么后端功能, ID是多少
2)实现逻辑:使用什么数据,计算什么,判断什么,
3)存储逻辑:数据存多久,如何更新同步
4) 接口逻辑:出参是...,入参是......
我们知道:
前端——>负责 展示UI,执行交互,胶水通信,埋点上报
后端——>负责 数据接口,逻辑计算,数据存储,更新同步
如果是一个组件,必然涉及到 非常多的 开关和配置。
这些个 开关与配置,又也是需要有个admin管理和存起来的。
那就是说:前端的UI和交互也是要有个后端去管理的。
到底怎么分离好呢。
主要有以下3种模式:
第1种:H5/navtive 统一对应 1个admin 和 1个sever
UI/交互/逻辑/数据所有都在一起配
优点:对运营非常友好,不用跑来跑去;逻辑变更升级友好,前后端一起改,不用担心兼容性
缺点:这套admin和sever,仅能管理这一套 H5/navtive,别的管不了,复用性变低了
第2种:大前后端分离各自配各自,人工单向绑定
1)H5/navtive + 编辑器admin +编辑器的sever
只配 UI/交互/传参数据(比如业务sever 按id绑定请求数据接口,获取数据)
2)业务admin+业务sever:
只配 业务逻辑相关的数据和开关,比如 任务类型/计算周期/用户名单/奖品库存/上下架状态
优点:
H5/navtive 可以区分多个组件,分别获取不同sever的数据,但是每个组件还是对应固定的sever
业务sever的可复用性也提高了,可以应用在 产研活动/H5编辑器等多个场景
缺点:
单向数据绑定运营差评,配个东西跑来跑去,也不清楚 哪个前端组件 对应 哪个后端,要填什么参数
sever仅提供接口,也不清楚到底有多少前端H5和组件使用它,每次修改逻辑都可能导致前端不兼容
第3种:1与2的结合体,自动双向数据绑定
1)H5/navtive + 编辑器admin +编辑器的sever
UI/交互/逻辑/数据都在编辑器一起配,然后编辑器的sever调用业务的sever进行创建/修改
存储 H5/navtive 与 业务sever的绑定关系
2)业务admin+业务sever:
业务sever专门提供接口给编辑器sever进行创建/修改,仅提供部分能力
存储 H5/navtive 与业务sever的绑定关系
业务admin隐藏由编辑器sever创建的实例,admin照样可以配置给 产研乃至其他使用
优点:运营不用跑来跑去,也不用清楚有什么参数,业务sever可以根据调用方进行逻辑和数据隔离
缺点:业务sever和admin有修改和新增特性,需要 H5/navtive 编辑器 也进行变更
画外音:
当然还有更奇葩与复杂的情况:
比如
发布:H5的编辑器配置完,zip打包,用压缩文件到另外一个admin进行发布到cdn的
多语言:遍历文案后,文字翻译的映射要到某个gooledoc或者admin配,再跑回来导入的
埋点:搞 可视化埋点,需要到埋点平台创建,再跑回来绑定的
前置:业务admin里需要配置 中台的配置,然后 跑N个后台 连续配的
打包:前端组件作为npm包或者iframe,给第三方工程依赖,然后配置还在编辑器的
反推:前端组件约定通用协议格式,别的sever按照协议推数据过来进行绑定的
真不好意思,这些我都 做过和见过...
但是我们后面就不展开讲了。
此时,当我们制定完组件的基础实现,就可以开始尝试 设计一个 组件 了。