继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

深耕业务 ---- 探索复杂/超复杂前端业务的开发与设计

喵喵一只汪
关注TA
已关注
手记 315
粉丝 86
获赞 467

 同步更新博客:

    1. 知乎专栏:前端路上的摸索

    2. github:design

  距离上一篇博客,我已经有3个月没有写博客了,脑子里也有很多灵光和新点子,忙嘛,肯定忙,但是忙不是理由,所以见谅。这次给自己下了死命令,一定要产出点东西,so,将自己最近开发中能总结的东西慢慢再搞出一点。

    PS:这是一篇思维参考性的文章,比较枯燥,阅读时间30分钟(包括思考和印证)

  作为深耕的业务,我们就从一个我遇到的复杂需求开始做个引子。栗子如下(可先看图片过个眼瘾):

https://img3.mukewang.com/5addecc60001f98217290756.jpg

  需求列表如下:

  1. 有20种不同类型的活动,每种活动按钮文字不一样

  2. 每种活动根据活动状态有不同的show和hide方案(最多有3-4个字段,分别控制)

  3. 根据不同业务状态,定义按钮的展示方式(禁用? or 可用?)

  4. 每个不同按钮的功能都不一样

  5. 同一个按钮,根据不同活动也有不同的功能(比如调整优惠,针对不同活动,引用不同组件)

  6. 一个按钮,相同的功能,但是请求的接口和参数也是不一样的(老数据老接口,新数据新接口)

 

  一般要求:这是一个迁移不完整的入口项目,对接所有活动的详情和操作,考虑业务不稳定性(业务变动需要变更,例如新迁移活动需要增加新的操作)以及迭代(继续迁移其他老项目,对接上来),需要考虑到更简易的拓展以及敏捷操作,当然维护和开发的成本也需要考虑的。

 

  更高要求:

  1. view视图上,简洁清爽,各种逻辑判断不乱

  2. vm层,避免超繁琐,代码的逻辑和归类清晰明了

  3. 低耦合,达到更松散的控制,对于以后拆分和开发更敏捷

  4. 轻松的统一管理,可以统一管理

 

  相信大家看了这个需求和要求,每个人根据自己的程序员开发经验和设计经验上,每个人都有不同的解决方案。其实,每个解决方案都是一种方式,只是在不同角度上的实施的成本以及设计思维上的不同。So,我想分享给大家的,也是经过我思考后以及完善的一种解决方案,拿出来仅供大家参考。

 

  程序员写的所有代码全都服务于业务,这个毋庸置疑。但是我想先问大家一个问题,到底什么是业务?

 

  在老板眼中,业务就是我赚钱的工具;在销售员眼中,业务就是我需要完成的指标;在产品眼中,业务就是我要实现完成的需求…每个人对业务的理解都不一样,但是,有谁考虑过,在前端开发工程师眼中的业务到底是什么???


  下面是我站在前端的角度去理解的业务,如下:

https://img2.mukewang.com/5addecd90001f0e917291046.jpg

 

  so,在我的理解里,我把前端所写的业务拆分成这6大部分:


    1. 业务数据:负责获取业务数据

    2. 业务逻辑:实现产品所定义的规则

    3. 逻辑数据:通过一系列规则所产出的逻辑数据

    4. 视图数据:通过逻辑数据转换成视图数据(不将逻辑和视图直接绑定)

    5. 视图展示:通过视图数据,直接驱动视图层展示对应视图

    6. 视图功能:通过视图展示组装成的需求功能

 

  在简单的业务需求中,可能我拿到的后端数据,就直接可以去渲染视图层,然后就完善功能。从开发的成本和复杂度上考量上,是不值得去做业务拆分。所以,在复杂的业务需求中以及兼顾拆分和维护中,这种业务方法论就可以大展手脚了。以下,我就拿开头的例子,详细解析围绕业务的6大部分的设计。

 

具体实现步骤:

  1. 在现代框架中(vue、react、angular),最核心、也最灵活的处理视图变化的方式,就是将驱动的视图的数据做成可配的,所以第一步,我们将我们按钮的需求做成数据配置,代码如下:

https://img2.mukewang.com/5addecee0001e74b17290643.jpg

      ps:将按钮的数据,全都装配到一个数组中,然后数组中的子集封装一个配置对象,将按钮的文字、回调、是否禁用等功能装配到模板中了

 

  2. 首先在我的需求中数据的获取只有一个接口,不复杂,所以对于业务数据的获取来说我没有将这个部分进行拆分管理。

https://img.mukewang.com/5addecfb0001f11217290350.jpg

      ps:很简单的前端数据请求,因为改处的接口,在其他动态刷新数据的时候还需要,所以这里我将请求封装成一个单一函数,只获取业务数据

 

  3. 对于最终视图展示,我们需要一个视图数据去驱动。有时候,我们会将我们业务规则产出的数据直接挂载到视图数据上,然后通过业务逻辑产出的数据直接驱动视图。不过在我的设计中,不建议将视图数据=逻辑数据。因为这样,我们的视图数据和逻辑数据会产生一种强依赖关系,而且,有时候逻辑数据只是根据业务定义出来的,而不是最适合做视图数据,来渲染数据的。如果逻辑出问题,或者前置的数据(直白的就是获取后端接口数据)出问题,那么强依赖的视图除了出问题,有时候甚至会直接崩溃。所以每次在视图数据和逻辑数据中我会做一层数据转换。

https://img4.mukewang.com/5added160001c1d117290959.jpg

      ps:tempData为视图数据,在这里对每个业务数据进行处理,然后对视图数据进行赋值,在处理中,可以对业务数据进行容错处理,不管业务逻辑怎么变化,或者业务数据怎么变化,在我的视图数据层面来说,就算你挂了,我依然可以对我的视图进行驱动,顶多是默认值,而不会直接宕机。一定程度上,增长了代码的健壮性。

 

  4. 我们再从上面的代码看我们的业务逻辑,我们所有的逻辑,都统一拆到了视图数据的右边了,比如我们讨论的按钮逻辑,所以对于整个页面功能来说,我的按钮的业务逻辑与全局所有功能的耦合,只有一个地方。这样的设计,秉承低耦合的思想,没那么强的控制欲,很松散,很舒服。

https://img4.mukewang.com/5added2e00015cb617290499.jpg

      ps:红框的地方为按钮的业务逻辑。

 

  5. 下面为拆分的按钮逻辑,从软件工程的设计布局上,将逻辑拆到最外层,和其他配置文件,枚举文件同级(个人感觉方便顺手)。 

https://img1.mukewang.com/5added4a0001b13b10561636.jpg

      ps:在工程中创建逻辑存放地方

 

  6. 在真正业务使用的地方,将业务逻辑代码和其他统一管理文件引入进来。

https://img3.mukewang.com/5added750001787a17290170.jpg

      ps:该处引入了全局统一管理枚举值、按钮拆分逻辑以及逻辑的适配器

 

  7. 因为考虑到,我们的活动本身的业务,2个大类(店铺和单个商品活动)+7个小类(不同活动)+不同活动渠道(6种渠道)共20多种活动的需求,所以针对活动以及活动数据的共性去统一处理势必会导致逻辑混乱,数据混乱,代码混乱,各种if和嵌套if等等,所以该处引入状态机机制,罗列所有存在的活动。

https://img1.mukewang.com/5addedde0001a68a13862247.jpg

      ps:状态机中针对类型+渠道确定活动,每个到叶节点的状态包装该活动对应的业务逻辑,而针对这个活动类型通用的业务数据,都包装在活动类型的节点下。

 

  8. 我们详细看每个活动下的按钮逻辑,我们在按钮逻辑中,将对每个按钮所要做的功能还是未知的,但是我们知道按钮所要做的事情的类型,所以我们在这里将定义按钮的文字、文字的行为、属于这个按钮功能附带的数据。然后再通过传入进来的业务数据,根据业务逻辑,产出逻辑数据。

https://img.mukewang.com/5addee0d0001ab7117291783.jpg

      ps:font为按钮的文字、behavior为该按钮需要做的行为,data为操作这按钮之后需要附带的一些数据。然后再根据业务逻辑规则,产出一个活动下的逻辑数据,供视图数据使用

 

  9. 这样我们就可以根据不同规则,加载不同状态机中的不同逻辑,比如这段状态机输出代码,我需要按钮逻辑,我就匹配按钮的逻辑,我需要匹配整个活动下共有的图片数据,我就直接加载活动类型的状态机。

https://img1.mukewang.com/5addee1c0001f3ab17290494.jpg

      ps:加载状态机对应的逻辑规则,这样我们就完全把逻辑拆分出来了。

 

   10. 完全拆分的按钮逻辑,就成了单个点了,但是这个逻辑只定义的按钮所需要的行为,对于按钮具体行为的实现没有涉及。我在设计按钮具体实现的时候,考虑到按钮的所有实现,都在一个页面中,需要直接调用一些业务数据和视图数据操作视图变化以及逻辑数据改变,所以,在我的项目中,没有将行为的实现拆分出去。下面就是我定义的对应按钮的行为的列表和具体实现:

https://img2.mukewang.com/5addee4f0001828617291610.jpg

      ps:直接在vue的methods中定义按钮逻辑所需要行为

 

  11. 按钮业务逻辑设计完毕,预留功能接口。内部组件定义行为的具体的实现,开放功能的对接。下面就需要一个适配器,将定义和实现紧紧连接在一起。这个按钮适配器,就是将按钮的产出的逻辑数据和具体的实现连接,真正产出逻辑数据

https://img3.mukewang.com/5addee9000014d6e17290784.jpg

https://img2.mukewang.com/5addeea4000180bf17290575.jpg

      ps:通过适配器,将定义和实现进行单独拆分。然后衔接出逻辑数据

 

  12. 然后将最终搜集到的视图临时数据,绑定到真正的视图数据上去渲染视图,这样我们就得到了完整的业务需要的视图。用户点击按钮就能操作视图的功能,根据按钮拆分中得附带数据,去判断用户具体去操作哪个按钮,这个按钮的什么功能,以及需要请求不一样的接口,接口数据等等

https://img1.mukewang.com/5addeed3000172b517290303.jpg

      ps:data为附带数据,数据中定义的什么字段有什么用,就根据自己的业务需求来。

 

所以整体设计,可以归类成下面的一张图片:

https://img4.mukewang.com/5addeeea0001112517291650.jpg

 

根据这张思路的设计图,可以将这里的复杂需求进行拆分,然后对应问题解决业务上的需求点:

  1. 20种活动,使用状态机输出每种活动展示的不同按钮文字

  2. 在状态机下,根据业务数据统一做按钮的hide和show的业务逻辑包括禁用和可用的逻辑。产出逻辑数据

  3. 每个状态机的按钮定义用户行为,在行为实现中实现

  4. 在状态机中根据行为+附加数据确定一个按钮实现的不同功能,包括其他差异化的方案(比如请求不同接口)

  5. 状态机+实现接口产出不同数据的拆分,对于业务的不稳定性和迭代兼容有很好的适用。松散的控制和低耦合的状态,对于活动的增加和删除,功能按钮的删除和增加,以及按钮功能实现的增加等,更方便开发和操作

  6. 因为view做成可配,所有状态都在业务逻辑层实现,直接产出逻辑数据来驱动视图数据,所以view层简洁清爽

  7. 状态机统一管理业务逻辑和行为,实现接口统一管理行为实现

 

  这只是我在业务开发生涯中,最近总结出来的一种业务方法论,这是一种针对复杂和超复杂的业务的一种探索,上面只是一个需求中的案例,大家可以根据自己的案例参考这种方法论。具体业务特性具体把握,比如最初设计规模,持续增长规模,以及未来发展规模(是否需要拆分)等。

 

以下为我参考的一些思想:

  1. 单体架构和微服务架构
    a) 单体架构(monolithic architecture)的应用,各组件的代码是作为一个整体存在的,组件之间互相合作,共享内存和资源。
    b) 微服务架构(microservice architecture)则是由许许多多个互相独立的小应用组成,每个应用都有自己的内存空间,应用在扩容时也是独立于其它应用进行的。

 

  2. 浏览器事件设计思想
    a) 事件思想:浏览器的事件一般都是用户触发然后去触发开发者的实现。所以,在浏览器本身的设计中是不知道用户到底是怎么实现的。So,浏览器的事件设计的时候就是在浏览器中定义一个行为(click or 其他),然后通过一个适配器,也就是注册事件的方法,将行为的实现进行适配和组装

 

  3. 流程化管理
    a) 流程化管理是指以流程为主线的管理方法。前端从数据获取到最后的视图功能展示,包括中间的各种业务逻辑以及视图渲染等等,都是一系列的流程,流转出来的结果。so,而且拆分也可以根据这些子节点中的流程作为关键点进行拆分。

 

  4. 敏捷开发思维
    a) 敏捷开发以用户的需求进化为核心,采用迭代、循序渐进的方法进行软件开发。敏捷,存在的目的就是因地制宜的去根据自己的项目去评估和拆分。类似于这套方法论,因个人和项目去评估到底怎么拆分,而不是拉过来就是干满一套。

 

以上只是我深耕业务过程中自己总结的一套思想和方案,仅供大家参考。而且这套方法论也可能不是很完善,可以一起讨论和继续深入的探讨。

 

原文出处

打开App,阅读手记
1人推荐
发表评论
随时随地看视频慕课网APP