前端代码复用学习笔记:整洁架构与清晰架构
基础代码的复用往往比较简单,但是业务代码的复用通常是困难的,如果没有特殊的手段去治理项目会逐渐发展为难以维护的巨石应用,按照维基百科记载,代码的复用形式主要有三种,程序库,应用框架,设计模式
程序库
前端业务代码在程序库的体现主要是通过业务组件,稍微大点的团队都有自己的业务组件库,但是我去过的很多团队都有落地难的问题,其中有些是技术层面的,但是更多的是出现在跨职责协作上,其中,我认为影响最大的是 UI 设计师和前端开发之间的协作关系
UI 组件资产
UI 设计师作为前端的直接上游,生产前端工程师所需的设计资源,如果生产的设计资源本身就是不规范的,必然会极大影响前端团队组件化的发展
UI 设计师作为设计人员,和工程师的思维方式不同,对组件设计规范的意识并不强烈,大部分设计人员对规范的定义仅仅在主题色和主观意识强烈的 风格 上,UI 设计师的最终产出由少部分 视觉要素约定 +设计师个人 主观设计倾向 决定,也就是说,设计师的心情好坏和人员更替会直接影响每次产出设计稿的呈现,这对设计师来说很正常,但对前端工程师来说是致命的
基于此,我们需要在工具上对 UI 设计师的产出加以约束,使其在前端工程师可接受的范围内自由发挥,例如 figma,figma 的 component 概念与前端组件化概念高度吻合,我们可以要求设计稿中可复用的独立单元必须是一个 figma component,从而倒逼设计师建立业务的组件资产,进而确保组件设计的一致性
组件代码设计
按照软件设计原则,找到程序中的变化内容并将其与不变的内容区分开,而在前端,按照代码改动频率依次排列,视图代码>视图逻辑>业务逻辑,相比业务逻辑,UI 代码属于高频变动的部分,尤其是是由 html+css 组成的视图代码
例如电商的商品卡片,不同节日下的商品卡片很可能有不同的 UI 样式,但是他们的业务逻辑是一样的,他们使用同样的业务数据和业务行为
在这种情况下大部分开发会选择两条路
-
要么为每个节日商品卡片单独封装一个组件,
-
要么封装一个包含所有节日样式的巨石组件,然后由外部的一个 prop 参数控制组件的渲染
前者会导致代码的大量冗余,后者违背了单一职责的设计原则,组件内部根据外部传入的节日类型写了一堆 if else,时间久了没人看得懂一堆 if else 里写的是什么
为了减少代码冗余和提升可维护性,我们可以按照职责将组件划为四部分
-
业务上下文组件
-
交互上下文组件
-
无状态视图组件
-
入口组件
可以看到和 Flux 架构很像,但是又有些区别,更像是对 Flux 架构的补充完善
-
多了一层交互状态组件,在 Flux 架构里只是简单区分了无状态组件和有状态组件,但是对 UI 的状态管理很模糊,究竟是由视图组件自己生产自己消费,还是由状态组件统一管理。不过 Flux 架构都是五年前的东西了,当时对组件的状态管理还处于探索阶段,也没有 hooks 这种能够最小化抽取状态和逻辑的利器,对代码的粒度按照职责做了进一步的细分,自然就有了更多复用的可能
-
使用 context + hook 代替 props 传参,这个其实很好理解,在父组的 render 函数里声明子组件并对子组件传参,从而达到控制子组件渲染的目的,这在耦合定义里叫控制耦合,而使用 context 可以做到无需在父组件内声明子组件也依旧可以传递数据,这种基于消费数据结构而进行耦合的方式叫标记耦合,在对各种耦合的定义中,标记耦合就是要比控制耦合的耦合度更低,耦合度低复用率也就上去了
-
减少全局依赖,把全局依赖下沉到某种上下文里,之前做过几次老代码到新项目的迁移,迁移过去的组件有大量直接依赖全局环境的代码,request,localstorage,甚至是挂在 window 下的全局变量,为了不影响新项目的运行,迁移过去的老代码做了大量修改,如果一开始就把这些全局依赖封装在 context 里,平移过去的代码几乎不需要修改,或者只是修改 context 这一小块就够了
应用框架
一条业务线往往会存在多个项目,不同项目之间通用业务代码的复用一直是个问题,在若干个项目上往往存在着多次复制黏贴的代码,经过项目的多次迭代,他们可能还会在各自的项目上衍生出不同的版本,长此以往维护成本会让开发人员痛苦不堪,我们当然知道抽离到 npm 私库是最佳实践,但是抽离模块的繁琐过程很大程度上提高了业务线上开发人员的心智成本,从而降低了将代码抽离到 npm 的意愿,在这种情况下,Monorepo 也许是更好的解决方案
lerna 提供了一个简易的 Monorepo 方案,利用 lerna 软链接项目作为依赖的特性,使得项目间的代码共享变得更加容易
上面我们用 lerna 管理了三个项目,其中 shared 属于其他两个项目的共享代码,wap-app 和 web-app 可以通过依赖的方式直接消费 shared 项目中的组件,工具函数和业务服务,极大优化我们的工作流程
设计模式
很多时候我们提到代码复用第一时间想到的组件,但是组件有组件的局限性,它是和 UI 框架 强耦合 的,而有一些场景需要需要实现 ==跨平台跨框架的代码复用== ,例如一个产品它既有 PC 网页,也有 RN 开发的 App,还有小程序
他们存在完全迥异的视图层或者框架体系,与之强耦合的组件没办法实现复用,但它们背后的业务逻辑都是一样的,所以为了复用我们的业务逻辑,需要实现业务逻辑的 ==框架无关,环境无关==
设计本质上就是以一种可以将它们重新组合在一起的方式将事物拆开…… 将事物拆分成可以重新组合的事物,这就是设计。 — Rich Hickey《设计、重构和性能》
系统设计其实就是系统的拆分,最重要的是我们可以在不耗费太多时间的情况下重新把它们组起来。
我同意上面这个观点,但我认为系统架构的另一个主要目标是系统的可扩展性。我们应用的需求是不断变化的。我们希望我们的程序可以非常易于更新和修改以满足持续变化的新需求。干净的架构就可以帮助我们实现这一目标。
了解干净架构,可以阅读《前端领域的 “干净架构” 前端领域的 “干净架构” - 掘金》
整洁架构Clean Architecture
《架构整洁之道》中提出的整洁架构就是解决这个问题的,腾讯文档团队也有对整洁架构的相关实践 让 JS 摆脱框架的束缚
我们可以将 react 的代码快速的迁移到一个类 react 框架,但是我们很难将他迁移到 Vue 框架和 Angular 框架。可能在代码迁移合并升级的时候,我们能做的可能只有重构。
我们可能会想到使用 Web Components 或者是微服务,但是带来的可能会是,更多的浏览器限定,要看更多的框架文档去遵循更多的规范。
无论框架如何的迭代,我们 JS 是永远不会太大的变化的,所有框架都是基于 JS 去写的,如何让我们通用 JS 代码被不同的框架去应用,让我们的通用代码去框架化,是我们应该思考的问题。
既然我们要去框架化,那架构就不去用了,直接用 JS 就好了呀,那就是一种技术的倒退,我们要做的不是不用框架,而是用一种更巧妙的方式去可以适配更多的框架。
例如,目前前端的类 MVVM 架构,ViewModel 层可以取出 Model 的数据同时帮忙处理 View 中由于需要展示内容而涉及的业务逻辑。我们要去用我们不变的 JS 代码去适配更多框架的 VM。
Clean Architecture 是由 Robert C. Martin 在 2012 年提出的,最早只是在 Android,Android 应用有很重的 View 层。今天,前端应用走向了 MV* 的架构方案,也有了一层很重的 View 层。
《DDD中常提到的应用架构总结(六边形、洋葱、整洁、清晰) DDD系列 - 第0讲 DDD中常提到的应用架构总结(六边形、洋葱、整洁、清晰)_整洁架构 六边形架构_罗小爬EX的博客-CSDN博客》
架构随时间的演进可参见下图:
Robert C. Martin 总结了六边形架构(即端口与适配器架构):DCI (Data-Context-Interactions,数据-场景-交互)架构BCI(Boundary Control Entity,Boundary Control Entity)架构等多种架构,归纳出了这些架构的基本特点:
-
框架无关性。系统不依赖于框架中的某个函数,框架只是一个工具,系统不能适应于框架。
-
可被测试。业务逻辑脱离于 UI、数据库等外部元素进行测试。
-
UI 无关性。不需要修改系统的其它部分,就可以变更 UI,诸如由 Web 界面替换成 CLI。
-
数据库无关性。业务逻辑与数据库之间需要进行解耦,我们可以随意切换 LocalStroage、IndexedDB、Web SQL。
-
外部机构(agency)无关性。系统的业务逻辑,不需要知道其它外部接口,诸如安全、调度、代理等。
如你所见,作为一个普通(不分前后端)的开发人员,我们关注于业务逻辑的抽离,让业务逻辑独立于框架。而在前端的实化,则是让前端的业务逻辑,可以独立于框架,只让 UI(即表现层)与框架绑定。一旦,我们更换框架的时候,只需要替换这部分的业务逻辑即可。
这种架构使得,框架和一切外部相关的实现细节被隔离在框架与驱动层,业务逻辑层只负责业务本身,与外部的联系由接口适配器层控制,假设我们需要从 Vue 迁移到 React,或者从 Vue2 升级到 Vue3,我们只需要变动接口适配器层和框架与驱动层,业务逻辑层可以继续复用
如图所示 Clean Architecture 一共分为四个环,四个层级。环与环之间,存在一个依赖关系原则:源代码中的依赖关系,必须只指向同心圆的内层,即由低层机制指向高级策略。其类似于 SOLID 中的依赖倒置原则:
高层模块不应该依赖低层模块,两者都应该依赖其抽象
抽象不应该依赖细节,细节应该依赖抽象
与此同时,四个环都存在各自核心的概念:
实体 Entities (又称领域对象或业务对象,实体用于封装企业范围的业务规则)
用例 Use Cases(交互器,用例是特定于应用的业务逻辑)
接口适配器 Interface Adapters (接口适配器层的主要作用是转换数据)
框架和驱动(Frameworks and Drivers),最外层由各种框架和工具组成,比如 Web 框架、数据库访问工具等
这个介绍可能有些简单,让我复制/粘贴一下更详细的解释:
实体(Entities),实体用于封装企业范围的业务规则。实体可以是拥有方法的对象,也可以是数据结构和函数的集合。如果没有企业,只是单个应用,那么实体就是应用里的业务对象。这些对象封装了最通用和高层的业务规则,极少会受到外部变化的影响。任何操作层面的改动都不会影响到这一层。
用例(Use Cases),用例是特定于应用的业务逻辑,一般用来完成用户的某个操作。用例协调数据流向或者流出实体层,并且在此过程中通过执行实体的业务规则来达成用例的目标。用例层的改动不会影响到内部的实体层,同时也不会受外层的改动影响,比如数据库、UI 和框架的变动。只有而且应当应用的操作发生变化的时候,用例层的代码才随之修改。
接口适配器(Interface Adapters)。接口适配器层的主要作用是转换数据,数据从最适合内部用例层和实体层的结构转换成适合外层(比如数据持久化框架)的结构。反之,来自于外部服务的数据也会在这层转换为内层需要的结构。
框架和驱动(Frameworks and Drivers)。最外层由各种框架和工具组成,比如 Web 框架、数据库访问工具等。通常在这层不需要写太多代码,大多是一些用来跟内层通信的胶水代码。这一层包含了所有实现细节,把实现细节锁定在这一层能够减少它们的改动对整个系统造成的伤害。
从某种意义上来说,Clean Architectute 是一种规范化和模板化的实施方案。与此同时,它在数据层的三层机制,使得它存在两层防腐层,usecase 可以作为业务的缓冲层,repository 层可以隔离后端服务与模型。
清晰架构Explicit Architecture
2017年Herberto Graca在其《软件架构编年》史系列文章中提出清晰架构Explicit Architecture,即将DDD, Hexagonal, Onion, Clean, CQRS等进行融合后的架构。
-
最中心的红色多边形Application Core即表示业务逻辑实现,即应用核心
-
红色多边形的边界即表示端口Port,即应用核心的入口/出口定义
-
Application Layer - 应用层,包括:
-
Application Services,业务用例的编排服务即及其interface定义,应用服务的作用通常如下:
-
使用 Repostitory 查找一个或多个实体;
-
让这些实体执行一些领域逻辑;
-
再次使用 Repostitory 让这些实体持久化,有效地保存数据变化;
-
触发应用事件(如发送邮件、调用第三方API、发送MQ消息等)。
-
-
CQRS命令/查询处理器
-
Event Listener事件监听器
-
Port端口定义,如ORM 接口Repostitory、搜索引擎接口、消息接口等等
-
-
Domain Layer - 领域层,这一层含了数据和操作数据的逻辑,它们只和领域本身有关,独立于调用这些逻辑的业务过程。它们完全独立,对应用层完全无感知。
-
Domain Services - 领域服务,封装涉及多实体(相同或不同实体类型)的领域逻辑,且领域服务间可以相互调用。
-
Domain Models - 领域模型,在架构的正中心,完全不依赖外部任何层次的领域模型。它包含了那些表示领域中某个概念的业务对象,如实体、值对象、枚举以及其它领域模型种用到的任何对象(如领域事件Domain Events,简单理解为MQ消息)。
-
-
红色多边形的外侧左半圆部分即为主/主动适配器(用户界面User Interface实现)
-
如Spring MVC中的Controller实现
-
Command Query Bus 命令查询总线
-
-
红色多边形的外侧右半圆部分即次/被动适配器(基础设置Infrastructure实现)
-
如数据持久化实现Mysql、短信通知实现、MQ通知、搜索引擎ES实现等
-
Event Bus 事件总线
-
-
依赖方向由外到内,且内层不知道外层(参见之前洋葱架构)
采用按组件分包Package By Component,即整合传统按层分包Package By Layer和按特性分包Package By Feature,组件Component可以理解为专属于特定一个领域内的业务逻辑服务和数据访问逻辑的组合,也可以理解为特定领域,如账单、用户、评论或帐号等。
在清晰架构中可以理解为:
-
先按照层次进行分包
-
表现层Presentation
-
业务核心层Application Core
-
基础设施层Infrastructure)
-
-
之后每一层次再按照特性分包
参考文章:
前端业务代码如何复用 前端业务代码如何复用 - 知乎
让 JS 摆脱框架的束缚 让 JS 摆脱框架的束缚
DDD中常提到的应用架构总结(六边形、洋葱、整洁、清晰) DDD系列 - 第0讲 DDD中常提到的应用架构总结(六边形、洋葱、整洁、清晰)_整洁架构 六边形架构_罗小爬EX的博客-CSDN博客
Clean Frontend Architecture:整洁前端架构 Clean Frontend Architecture:整洁前端架构 | clean-frontend
微模块-前端业务模块化探索# 微模块-前端业务模块化探索 | Elux
“整洁架构” 和商家前端的重构之路 “整洁架构”和商家前端的重构之路 - 得物技术的个人空间 - OSCHINA - 中文开源技术交流社区
转载本站文章《前端代码复用学习笔记:整洁架构与清晰架构》,
请注明出处:前端代码复用学习笔记:整洁架构与清晰架构 - 前端架构设计 - 周陆军的个人网站
相关文章:

前端代码复用学习笔记:整洁架构与清晰架构
基础代码的复用往往比较简单,但是业务代码的复用通常是困难的,如果没有特殊的手段去治理项目会逐渐发展为难以维护的巨石应用,按照维基百科记载,代码的复用形式主要有三种,程序库,应用框架,设计…...

【python刷题】leecode官方提示“->“,“:“这些符号是什么意思?什么是Type Hints?
作者:20岁爱吃必胜客(坤制作人),近十年开发经验, 跨域学习者,目前于海外某世界知名高校就读计算机相关专业。荣誉:阿里云博客专家认证、腾讯开发者社区优质创作者,在CTF省赛校赛多次取得好成绩。…...

【华为OD机试真题2023 JAVA】最佳对手
华为OD机试真题,2023年度机试题库全覆盖,刷题指南点这里 最佳对手 知识点排序DFS搜索回溯 时间限制:1s 空间限制:256MB 限定语言:不限 题目描述: 游戏里面,队伍通过匹配实力相近的对手进行对战。 但是如果匹配的队伍实例相差太大,对于双方游戏体验都不会太好。 给定n个…...

css实现文字大小自适应
在页面编写中经常会碰到页面自适应的问题,也就是页面内部的元素会随着窗口的放大缩小而放大缩小,box可以通过calc 百分比的形式做到页面自适应,但是box内的字体却无法做到这点,往往box自适应大小了,内部的字体还是原来…...

【Redis】搭建哨兵集群
目录 集群结构 准备实例和配置 启动 测试 集群结构 这里我们搭建一个三节点形成的Sentinel集群,来监管之前的Redis主从集群。如图: 三个sentinel实例信息如下: 节点IPPORTs1192.168.150.10127001s2192.168.150.10127002s3192.168.150.…...

CTFHub | .htaccess
0x00 前言 CTFHub 专注网络安全、信息安全、白帽子技术的在线学习,实训平台。提供优质的赛事及学习服务,拥有完善的题目环境及配套 writeup ,降低 CTF 学习入门门槛,快速帮助选手成长,跟随主流比赛潮流。 0x01 题目描述…...

微机原理 || 8253 芯片 (详细讲解 + 经典例题)
一点点看!一定可以看懂!考试没有问题的!加油💪 前面知识写的详细,看不懂可以先看典例,回头来梳理就明白了【典例就是常考的题】 目录 Part 1: 芯片知识总结 (一)8253 芯片特点 …...

python Django高级操作-分页-定义CVS-发送邮件
分页 分页是指在web页面有大量数据需要显示,为了阅读方便在每个页页中只显示部分数据。优点: 1.方便阅读2.减少数据提取量,减轻服务器压力。Paginator对像 负责分页数据整体的管理对象的构造方法Paginator属性 Paginator方法 Paginator异常exception pag…...

React 用一个简单案例体验一遍 React-dom React-router React-redux 全家桶
一、准备工作 本文略长,建议耐心读完,每一节的内容与上一节的内容存在关联,最好跟着案例过一遍,加深记忆。 1.1 创建项目 第一步,执行下面的命令来创建一个 React 项目。 npx create-react-app react-example cd rea…...

9. C#面向对象基础
一、C# 类 在 C# 中,类是引用类型的。类由成员属性和成员方法构成。我们可以动态创建类的实例(instance),这个实例也被称为对象(object),我们可以通过类和对象来设计程序。 1、类的定义 类的定…...

【MIT 6.S081】Lab2: system calls
本Lab包括两个简单系统调用的实现,进一步熟悉系统调用接口。 笔者用时约1.5h 概述 根据文档说明,当我们添加一个系统调用时,比如第一个任务是添加一个trace,需要进行以下操作: 首先将系统调用的原型添加到user/user…...

设计模式之单例模式~
设计模式包含很多,但与面试相关的设计模式是单例模式,单例模式的写法有好几种,我们主要学习这三种—饿汉式单例,懒汉式单例、登记式单例,这篇文章我们主要学习饿汉式单例 单例模式: 满足要点: 私有构造 …...

top终端详解
1.top命令行使用 2.top每行意义 3.补充 1.top命令行使用 top命令是一个常用的Linux系统命令,用于实时查看系统的运行状态和进程信息。下面是top命令的几个常用参数的含义: -d seconds:设置top命令的更新间隔时间,单位是秒。默认是…...

解决一个偶现的503 bug,花了俺不少时间
概述 在3月2日晚上,大概8点左右,本想打道回府,回家休息,突然被人在bug群了一下,说是管理后台,访问不了,界面上出现了: 503 service temporarily unavailable我赶紧尝试访问了一下,确…...

什么是栈,如何实现?
欢迎来到 Claffic 的博客 💞💞💞 “但有一枝堪比玉,何须九畹始征兰?” 前言: 栈是一种特殊的线性表,就像开盖的桶一样,从底部开始放数据,从顶部开始取数据,那么栈具体是…...

在我的MacBook上捣鼓ESP8266
周三是我们的篮球日,打篮球后总是会有些兴奋,然后就容易睡不着,额,睡不着就拿我的ESP8266开发板出来捣鼓一下。先下载编译工具链https://github.com/espressif/ESP8266_RTOS_SDK下载sdkgit clone https://github.com/espressif/ES…...

【深度强化学习】(8) iPPO 模型解析,附Pytorch完整代码
大家好,今天和各位分享一下多智能体深度强化学习算法 ippo,并基于 gym 环境完成一个小案例。完整代码可以从我的 GitHub 中获得:https://github.com/LiSir-HIT/Reinforcement-Learning/tree/main/Model 1. 算法原理 多智能体的情形相比于单智…...

77.qt qml-QianWindow-V1版本界面讲解
上章介绍: 76.qt qml-QianWindow开源炫酷界面框架简介(支持白色暗黑渐变自定义控件均以适配) 界面如下所示: 代码结构如下所示:...

RHCE学习日记二
1、在 node1 主机上配置 chrony 时间服务器,将该主机作为时间服务器。 命令: vim /etc/chrony.conf 在文件位置添加命令: #Use public servers from the pool.ntp.org project. #Please consider joining the pool (https://www.pool.ntp.org…...

Dubbo原理简介
Dubbo缺省协议采用单一长连接和NIO异步通讯,适合于小数据量大并发的服务调用,以及服务消费者机器数远大于服务提供者机器数的情况。 作为RPC:支持各种传输协议,如dubbo,hession,json,fastjson,底层采用mina,netty长连接…...

JavaSE基础总结
JDK与JRE JDK,全称Java Development Kit,Java开发工具包 JRE,全称Java Runntime Environment,Java运行环境 JDK包含后者JRE。 JDK也可以说是Java SDK(Software Development kit,软件开发工具包)…...

5G(NR)信道带宽和发射带宽---频率资源
前言 查看此文之前建议先看看这篇 5G(NR)频率资源划分_nr运营商频段划分_达帮主的博客-CSDN博客NR频率有上面几个划分 ,可以使用低于1GHz的频端,既可以使用高于30GHz高频端。使用频端高于30GHz那我们称之为高频或者毫米波。使用毫米波是5G网络区别于4G…...

基于Spring Boot的酒店管理系统
文章目录 项目介绍主要功能截图:登录首页房间类型酒店预约部分代码展示设计总结项目获取方式🍅 作者主页:Java韩立 🍅 简介:Java领域优质创作者🏆、 简历模板、学习资料、面试题库【关注我,都给你】 🍅文末获取源码联系🍅 项目介绍 基于Spring Boot的酒店管理系统…...

Ae:混合模式
Ae 中内置了 Ps 的渲染引擎,同样可在多处应用混合模式 Blending Mode。与 Ps 相比,除了两组图层通道相关的特定模式,其它的混合模式几乎是一模一样。相关快捷键:下一图层混合模式:Shift 上一图层混合模式:…...

JS中的变量
系列文章目录 前端系列文章——传送门 JavaScript系列文章——传送门 文章目录系列文章目录前言1、概念2、定义变量3、变量名的规则4、变量本质5、赋值6、常用操作前言 相对于青龙面板来说,变量就是你填入青龙的cookie,简称ck 在实际项目中࿰…...

Hadoop运行模块
二、Hadoop运行模式 1)Hadoop官方网站:http://hadoop.apache.org 2)Hadoop运行模式包括:本地模式、伪分布式模式以及完全分布式模式。 本地模式:单机运行,只是用来演示一下官方案例。生产环境不用。伪分…...

Web自动化——前端基础知识(二)
1. Web前端开发三要素 web前端开发三要素 什么是HTMl? Html是超文本标记语言,是用来描述网页的一种标记语言HTML是一种标签规则的形式将内容呈现在浏览器中可以以任意编辑器创建,其文件扩展名为.html或.htm保存即可 什么是CSS?…...

NAS系列 硬件组装
转自我的博客文章https://blognas.hwb0307.com/nas/3260,内容更新仅在个人博客可见。欢迎关注! 前言 之前我在《NAS系列 硬件选择》里讲述了自己为了升级NAS如何选配硬件。本节我大概说一些我的新NAS硬件组装的注意事项。到目前为止,我只装过…...

IDAFrida
IDA&Frida 前言 偶然间发现了一本秘籍《IDA脚本开发之旅》,这是白龙的系列文章,主要是安卓平台,笔者只是根据他的知识点学习,拓展,可以会稍微提及别的平台。本文并不会贴出他的思路分析,只对于源码进…...

通过百度文心一言大模型作画尝鲜,感受国产ChatGPT的“狂飙”
3月16日下午,百度于北京总部召开新闻发布会,主题围绕新一代大语言模型、生成式AI产品文心一言。百度创始人、董事长兼首席执行官李彦宏,百度首席技术官王海峰出席,并展示了文心一言在文学创作、商业文案创作、数理推算、中文理解、…...