22
33## 前端业务模型
44
5- 在 Web 开发领域中,Web 服务的核心职责通常是接收请求、处理请求并返回响应,从系统建模的角度看,它是一个单输入单输出的处理过程。
6- 这使得 Web 服务的逻辑天然适合抽象为洋葱模型(onion model):外层中间件包裹内层中间件,请求按顺序穿透各层中间件进入核心业务处理逻辑,再逐层返回响应。常见的中间件包括身份验证、权限校验、日志记录、缓存处理等。
5+ 在 Web 开发领域中,后端服务的核心职责通常是接收请求、处理请求并返回响应,从系统建模的角度看,它是一个典型的单输入单输出的处理流程。
6+
7+ 这种特性使得后端服务的逻辑天然适合抽象为洋葱模型(Onion Model):外层中间件包裹内层中间件,请求按顺序穿透各层中间件进入核心业务处理逻辑,再逐层返回响应。常见的中间件包括身份验证、权限校验、日志记录、缓存处理等。
78
89<div style =" display : flex ; flex-direction : column ; align-items : center ; justify-content : center ;" >
910 <img src =" /model.drawio.svg " alt =" motion " />
1011</div >
1112
12- 而 Web 前端的业务模型与此完全不同。前端页面运行在用户浏览器中,是一个多输入多输出的复杂系统。它面向用户直接交互,输入来源包括用户点击 、输入、滚动、 窗口变化、定时器触发、网络 API 响应等,这些事件发生的时间不确定、频率不一致 ,构成了一个** 高度异步、事件驱动** 的环境 。
13+ 而 Web 前端的业务模型与此截然不同。前端应用运行在用户浏览器中,本质上是一个多输入多输出的复杂响应式系统。它直接面向用户交互,输入来源极其多样化:用户操作(点击 、输入、滚动)、系统事件( 窗口变化、定时器触发)、网络响应( API 调用、WebSocket 消息)等,这些事件在时间维度上不可预测、频率完全异构 ,构成了一个** 高度异步、事件驱动** 的执行环境 。
1314
14- 与此同时,前端系统也会产生多样化的输出,这些输出不再局限于一次性返回的响应 ,而是持续、分散地反馈到多个目标上。常见的输出包括更新页面中的 DOM 结构以反映状态变化,执行动画或过渡效果增强用户体验,发送新的 API 请求以拉取或提交数据,修改本地状态(如缓存、存储或组件状态)以准备后续交互甚至与浏览器原生功能交互(如剪贴板 、通知、文件系统等),这些输出通常也是 ** 动态分发、异步反馈 ** 的方式 。
15+ 与此同时,前端系统的输出同样复杂多样,不再局限于一次性的响应返回 ,而是持续、分散地作用于多个目标。典型的输出包括: DOM 更新(反映状态变化)、视觉反馈(动画、过渡效果)、网络通信( API 请求、数据提交)、状态同步(缓存更新、本地存储)、系统集成(剪贴板 、通知、文件系统等浏览器 API),这些输出操作通常采用 ** 动态分发、异步执行 ** 的模式 。
1516
1617复杂的输入和输出方式,与传统 Web 服务的“请求-响应”式单一输出形成鲜明对比。
1718
1819## 前端框架的演化
1920
20- 为了应对前端页面中 ** 高度异步、事件驱动、多输入多输出** 的复杂环境,前端开发逐步演化出依托各种框架的编程范式,其中最具代表性的就是 ` MVVM(Model-View-ViewModel) ` 框架 。
21+ 为了应对前端应用中 ** 高度异步、事件驱动、多输入多输出** 的复杂环境,前端开发社区逐步演化出基于框架的编程范式,其中最具代表性的是 ` MVVM(Model-View-ViewModel) ` 架构模式 。
2122
2223这一范式的核心思想是:将页面的显示逻辑与状态逻辑解耦,并通过** 响应式绑定** 让它们自动协同。
2324
@@ -40,13 +41,13 @@ MVVM 框架的最大优势在于:
4041
4142## 复杂业务架构分层
4243
43- 虽然前端框架在处理数据变化更新页面这件事情上做的非常出色,但是随着业务的复杂度不断提升,框架提供出来的编程范式反而会将成为业务的瓶颈。前端框架的编程范式大多都是以组件为核心,以 hooks为颗粒度,将业务逻辑组织在组件中 。
44+ 虽然现代前端框架在数据变化到视图更新的处理上已经非常成熟,但随着业务复杂度的指数级增长,框架原生的编程范式逐渐暴露出架构层面的局限性。主流前端框架普遍采用以组件为核心、以 Hooks/Composition API 为颗粒度的开发模式,将业务逻辑内聚在组件内部 。
4445
4546<div style =" display : flex ; flex-direction : column ; align-items : center ; justify-content : center ;" >
4647 <img src =" /page.drawio.svg " alt =" motion " />
4748</div >
4849
49- 将数据的请求、逻辑的处理统一放置在组件中进行处理,数据和逻辑链条通过组件树层层递进,功能也是通过组件来进行组织。对于简单业务这样做似乎没有问题,但是对于复杂业务,下面这些问题就会暴露出来 :
50+ 在这种模式下,数据获取、业务逻辑处理都内聚在组件内部,业务功能通过组件树的层级关系进行组织,数据和逻辑链路沿着组件层次结构传递。这种架构对于中小型应用运行良好,但在企业级复杂业务场景下,会产生以下架构性问题 :
5051
51521 . ** 组件过于臃肿** :数据的请求、数据的转换、数据的逻辑处理、全部堆在组件内部
52532 . ** 代码阅读困难** :业务逻辑散落在各个组件中,需按照组件链条来理解业务
@@ -56,15 +57,15 @@ MVVM 框架的最大优势在于:
56576 . ** 重复请求** :组件内部请求数据导致可以复用的数据难以复用
57587 . ** 复杂度高** :数据流呈现螺旋网状调用,牵一发而动全身
5859
59- 如果认为前端状态是一系列数据和逻辑的总和,那么 Url 变化、DOM 元素的操作、定时器、http 请求等副作用导致状态在一直动态变化 ,** 界面其实是状态某一个时刻的切片 ** 。
60+ 从架构设计角度来看,前端应用的状态是数据和逻辑的动态组合体,URL 路由、用户交互、定时任务、HTTP 请求等副作用操作持续驱动状态变迁。因此 ,** UI 界面本质上是应用状态在特定时刻的快照 ** 。
6061
61- 将状态直接放入界面中其实是本末倒置,将状态从视图层抽离构造出业务模型,视图消费状态才符合上述理念 :
62+ 将状态逻辑直接耦合在视图层是一种架构上的倒置,正确的做法应该是将状态管理从视图层解耦,构建独立的业务模型层,让视图成为状态的消费者 :
6263
6364<div style =" display : flex ; flex-direction : column ; align-items : center ; justify-content : center ;" >
6465 <img src =" /ddd.drawio.svg " alt =" motion " />
6566</div >
6667
67- 当将业务模型从组件抽离后,组件变成了 ** 受控 ** ,只消费模型提供的数据。那么我们该如何组织业务模型呢?我们的代码已经不在组件内, ` vue ` 或者 ` react ` 提供的编程范式也不再适用。
68+ 业务模型从组件中抽离后,组件转变为 ** 受控组件 ** ,专注于数据展示和用户交互,业务逻辑由独立的模型层负责。此时面临的核心问题是:在脱离了 Vue/React 组件体系后,如何有效地组织和管理这些业务模型?
6869
6970## 模型驱动与流
7071
@@ -74,9 +75,9 @@ MVVM 框架的最大优势在于:
7475 <img src =" /logic.drawio.svg " alt =" motion " />
7576</div >
7677
77- 从本质上来说,模型是高内聚的数据和逻辑的集合,似乎直接用 ` OOP ` 面相对象进行封装就可以达到目的,但是前面提到前端业务模型是 ** 高度异步、事件驱动、多输入多输出** 的,使用 ` OOP ` 进行封装,那么就会出现大量的异步调用链 。
78+ 从理论上讲,业务模型是高内聚的数据和逻辑的封装体,传统的 OOP 面向对象编程似乎是自然的选择。然而,前端业务模型具有 ** 高度异步、事件驱动、多输入多输出** 的特性,使用传统 OOP 封装会产生大量复杂的异步调用链和回调嵌套 。
7879
79- 这些调用链条由于是 ** 异步且往往横跨多个业务模型 ** ,导致代码难以阅读和维护,而 ` vue ` 的数据响应式往往会加剧这一问题,数据不知道在哪个环节被修改,修改后又可能不知情的触发其他逻辑,导致数据流变得难以追踪:
80+ 这些调用链 ** 异步执行且往往跨越多个业务域 ** ,极大增加了代码的理解和维护成本。Vue 的响应式系统虽然简化了数据绑定,但在复杂业务场景下可能加剧问题:数据修改的触发点难以定位,副作用的传播路径不可预测,整体数据流变得难以追踪和调试:
8081
8182<div style =" display : flex ; flex-direction : column ; align-items : center ; justify-content : center ;" >
8283 <img src =" /logic-complex.drawio.svg " alt =" motion " />
@@ -88,19 +89,19 @@ MVVM 框架的最大优势在于:
8889 <img src =" /logic-flow.drawio.svg " alt =" motion " />
8990</div >
9091
91- 流是一种对 ** 异步编程声明式的高级抽象 ** ,通过流以及操作符,可以完成非常复杂的异步编排从而摆脱传统的复杂的调用逻辑关系 。
92+ 流(Stream)是 ** 异步编程的声明式高级抽象 ** ,通过流式操作符的组合,可以优雅地处理复杂的异步编排,从根本上解决传统回调和 Promise 链式调用的复杂性问题 。
9293
93- 当流的每个节点既可以存储数据,也可以处理数据,那么业务模型的 Data 和 Methods 也可以丢弃,全部以流的节点的形式来进行 :
94+ 在流式架构中,每个节点既是数据容器也是处理单元,传统业务模型中的 Data 和 Methods 概念可以统一为流节点,实现数据和逻辑的一体化管理 :
9495
9596<div style =" display : flex ; flex-direction : column ; align-items : center ; justify-content : center ;" >
9697 <img src =" /logic-stream.drawio.svg " alt =" motion " />
9798</div >
9899
99- 这种以流为基础构成的前端业务模型,完美的契合了前端业务模型 ** 高度异步、事件驱动、多输入多输出** 的特点,它既可以很好的组织业务数据,也可以很好的组织业务业务逻辑 。
100+ 基于流构建的前端业务模型完美契合了现代前端应用 ** 高度异步、事件驱动、多输入多输出** 的本质特征,在数据管理和业务逻辑组织方面都能提供更优雅、更可维护的解决方案 。
100101
101102## 流在框架中落地
102103
103- 用流来组织业务模型,流的输入和输出都需要和前端框架进行交互,那么流如何转换为前端框架的可以消费的数据和逻辑呢?
104+ 采用流式业务模型后,关键问题是如何实现流与前端框架的无缝集成。具体来说,需要解决双向转换问题:
104105
105106- 对于` vue ` 来说,响应式的数据可以通过 [ to$] ( /cn/useFluth/to$.html ) 方法转换为流
106107
@@ -116,7 +117,7 @@ MVVM 框架的最大优势在于:
116117 <img src =" /traditional-code.drawio.svg " alt =" motion " />
117118</div >
118119
119- 传统的开发方式一般采用的命令式的开发方式 :
120+ 传统的前端开发采用命令式编程模式 :
120121
1211221 . 点击按钮后,调用 handleSubmit 方法
1221232 . handleSubmit 先 validateForm 方法,如果验证不通过,则提示报错
@@ -125,16 +126,16 @@ MVVM 框架的最大优势在于:
1251265 . 如果调用成功,则继续调用 handleDataB 方法、handleDataC 方法
1261276 . 如果调用失败,则提示报错
127128
128- 这种命令式的开发方夹杂着异步的操作,整体阅读体验非常差,随着业务逻辑的增加 ,handleSubmit 方法会变得越来越臃肿,难以维护 。
129+ 这种命令式开发模式混合了同步逻辑和异步操作,代码可读性较差,随着业务复杂度增长 ,handleSubmit 方法会变得越来越臃肿,形成典型的"上帝方法"反模式 。
129130
130131下面采用流的方式重新实现:
131132
132133<div style =" display : flex ; flex-direction : column ; align-items : center ; justify-content : center ;" >
133134 <img src =" /stream-code.drawio.svg " alt =" motion " />
134135</div >
135136
136- 而采用流的方式可以很好的解决这个问题,将基础数据拆分成一个个的流,通过流的组合操作符来完成业务,不管是代码阅读、维护都非常清晰,功能也趋于原子化,可以很好的复用 。
137+ 流式编程则能优雅地解决这些问题:将基础数据抽象为独立的流,通过组合操作符构建业务逻辑,代码结构清晰、逻辑原子化、复用性强 。
137138
138- 注意其中的 [ audit] ( https://fluthjs.github.io/fluth-doc/cn/api/operator/audit.html ) 、[ debounce] ( https://fluthjs.github.io/fluth-doc/cn/api/operator/debounce.html ) 、[ filter] ( https://fluthjs.github.io/fluth-doc/cn/api/operator/debounce.html ) 操作符,通过简单的操作符就完成了复杂的业务逻辑,让整体的代码阅读和维护都非常清晰 。
139+ 值得注意的是, [ audit] ( https://fluthjs.github.io/fluth-doc/cn/api/operator/audit.html ) 、[ debounce] ( https://fluthjs.github.io/fluth-doc/cn/api/operator/debounce.html ) 、[ filter] ( https://fluthjs.github.io/fluth-doc/cn/api/operator/debounce.html ) 等操作符以声明式的方式处理了触发器、节流、条件过滤等复杂的异步控制逻辑,代码的表达力和可维护性都得到了显著提升 。
139140
140- 从这个例子中可以看出,采用流来组织业务代码,非常适合前端的业务模型 。
141+ 通过这个对比案例可以看出,流式编程范式与前端业务的异步、事件驱动特性天然契合,是组织复杂前端业务逻辑的理想选择 。
0 commit comments