前端领域的 “干净架构”
Posted 奇舞精选
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了前端领域的 “干净架构”相关的知识,希望对你有一定的参考价值。
大家好,我是 ConardLi
,前端有架构吗?这可能是很多人心里的疑惑,因为在实际业务开发里我们很少为前端去设计标准规范的代码架构,可能更多的去关注的是工程化、目录层级、以及业务代码的实现。
今天我们来看一种前端架构的模式,原作者称它为“干净架构(Clean Architecture
)”,文章很长,讲的也很详细,我花了很长时间去读完了它,看完很有收获,翻译给大家,文中也融入了很多我自己的思考,推荐大家看完。
https://dev.to/bespoyasov/clean-architecture-on-frontend-4311
https://github.com/bespoyasov/frontend-clean-architecture/
首先,我们会简单介绍一下什么是干净架构(Clean architecture
),比如领域、用例和应用层这些概念。然后就是怎么把干净架构应用于前端,以及值不值得这么做。
接下来,我们会用干净架构的原则来设计一个商店应用,并从头实现一下,看看它能不能运行起来。
这个应用将使用 React
作为它的 UI 框架,这只是为了表明这种开发方式是可以和 React
一起使用的。你也可以选择其他任何一种 UI 库去实现它。
代码中会用到一些 TypeScript
,这只是为了展示怎么使用类型和接口来描述实体。其实所有的代码都可以不用 TypeScript
实现,只是代码不会看起来那么富有表现力。
Angular
,或者改变某些用例的时候不会变的那一部分。在商店这个应用中,领域就是产品、订单、用户、购物车以及更新这些数据的方法。数据结构和他们之间的转化与外部世界是相互隔离的。外部的事件调用会触发领域的转换,但是并不会决定他们如何运行。
比如:将商品添加到购物车的功能并不关心商品添加到购物车的方式:
在这两种情况下,都会返回一个更新之后的购物车对象。
一起将事件转换为我们的应用程序可以理解的信号。驱动型会和我们的基础设施交互。在前端,大部分的基础设施就是后端服务器,但有时我们也可能会直接与其他的一些服务交互,例如搜索引擎。
注意,离中心越远,代码的功能就越 “面向服务”,离应用的领域就越远,这在后面我们要决定一个模块是哪一层的时候是非常重要的。
目录下,应用层定义在application
目录下,适配器都定义在 service
目录下。最后我们还会讨论目录结构是否会有其他的替代方案。或 DateTimeString
。这些其实都是类型别名:代替 string
来更清晰的表明这个字符串是用来做什么的。这些类型越贴近实际,就更容易排查问题。这些类型都定义在 shared-kernel.d.ts
文件里。共享内核指的是一些代码和数据,对他们的依赖不会增加模块之间的耦合度。
在实践中,共享内核可以这样解释:我们用到 TypeScript
,使用它的标准类型库,但我们不会把它们看作是一个依赖项。这是因为使用它们的模块互相不会产生影响并且可以保持解耦。
并不是所有代码都可以被看作是共享内核,最主要的原则是这样的代码必须和系统处处都是兼容的。如果程序的一部分是用 TypeScript
编写的,而另一部分是用另一种语言编写的,共享核心只可以包含两种语言都可以工作的部分。
在我们的例子中,整个应用程序都是用 TypeScript
编写的,所以内置类型的别名完全可以当做共享内核的一部分。这种全局都可用的类型不会增加模块之间的耦合,并且在程序的任何部分都可以使用到。
,而不是它们的具体实现。在这个阶段,描述必要的行为对我们来说很重要,因为这是我们在描述场景时在应用层所依赖的行为。如何实现现在不是重点,我们可以在最后再考虑调用哪些外部服务,这样代码才能尽量保证低耦合。
另外还要注意,我们按功能拆分接口。与支付相关的一切都在同一个模块中,与存储相关的都在另一个模块中。这样更容易确保不的同第三方服务的功能不会混在一起。
方法,这个方法将接受需要支付的金额,然后返回一个布尔值来表明支付的结果。会提示我们没有给出接口的实现,先不要管他。的方法来创建一个订单:了,现在我们来检查一下现实是否符合我们的需求。通常情况下是不会的,所以我们要通过封装适配器来调用第三方服务。
来封装用例,建议把所有的服务都封装到里面,最后返回用例的方法:来作为一个依赖注入。首先我们使用 useNotifier,usePayment,useOrdersStorage
这几个 hook
来获取服务的实例,然后我们用函数 useOrderProducts
创建一个闭包,让他们可以在 orderProducts
函数中被调用。另外需要注意的是,用例函数和其他的代码是分离的,这样对测试更加友好。
接口,我们先来实现一下。对于付款操作,我们依然使用一个假的 API 。同样的,我们现在还是没必要编写全部的服务,我们可以之后再实现,现在最重要的是实现指定的行为:
这个函数会在 450
毫秒后触发的超时,模拟来自服务器的延迟响应,它返回我们传入的参数。来实现通知,因为代码是解耦的,以后再来重写这个服务也不成问题。和 Hooks
来实现本地存储。我们创建一个新的 context
,然后把它传给 provider
,然后导出让其他的模块可以通过 Hooks
使用。
。这样我们就不会破坏服务接口和存储,至少在接口的角度来说他们是分离的。 return useStore();
此外,这种方法还可以使我们能够为每个商店定制额外的优化:创建选择器、缓存等。
模块引入的 totalPrice
方法。这样使用本身没有什么问题,但是如果我们要考虑把代码拆分到独立的功能的时候,我们不能直接访问其他模块的代码。没有监控并强制执行它们的机制。这看起来也不是个问题:你是用 string
类型去替代 DateTimeString
也不会怎么样,代码还是会编译成功。但是,这样会让代码变得脆弱、可读性也很差,因为这样你可以用任意的字符串,导致错误的可能性会增加。
有一种方法可以让 TypeScript
理解我们想要一个特定的类型 — ts-brand
(https://github.com/kourge/ts-brand
)。它可以准确的跟踪类型的使用方式,但会使代码更复杂一些。
函数的领域中创建了一个日期:这样的函数可能会被重复调用很多次,我们可以把它封装到一个 hleper
里面:函数最好是所有数据都从外面传进来,日期可以作为最后一个参数:没有任何问题。这样的 Helper
甚至可以被视为共享内核,因为它们只会减少代码的重复度。会包含 Cart
, 因为购物车只表示 Product
列表:会更合理:函数很难独立于 React
来测试,这不太好。理想情况下,测试不应该消耗太多的成本。问题的根本原因我们使用 Hooks
来实现了用例:
的外面,服务通过参数或者使用依赖注入传入用例:的代码就可以当做一个适配器,只有用例会留在应用层。orderProdeucts
方法很容易就可以被测试了。和 hooks
的情况下,我们可以将它们用作“容器”,返回指定接口的实现。是的,虽然还是手动实现的,但它不会增加上手门槛,并且对于新手开发人员来说阅读速度更快。中的块和修饰符概念来帮助你思考,如果我在 BEM
的上下文中考虑它,它可以帮助我确定我是否有一个单独的实体或代码的“修饰符扩展”。BEM - Block Element Modfier(块元素编辑器)是一个很有用的方法,它可以帮助你创建出可以复用的前端组件和前端代码。
等多种发展方向供员工选择,并辅以提供相应的技术力、专业力、通用力、领导力等培训课程。奇舞团以开放和求贤的心态欢迎各种优秀人才关注和加入奇舞团。
Android中具有干净架构的mvvm和没有干净架构的mvvm有啥区别?
【中文标题】Android中具有干净架构的mvvm和没有干净架构的mvvm有啥区别?【英文标题】:What is difference between mvvm with clean architecture and mvvm without clean architecture in Android?Android中具有干净架构的mvvm和没有干净架构的mvvm有什么区别? 【发布时间】:2020-02-17 10:44:36 【问题描述】:我正在学习 MVVM 和 Clean Architecture。然后我发现了一些关于 MVVM + Clean Architecture 的文章,但我仍然没有了解 mvvm with clean architecture 和 mvvm without clean architecture 之间的区别。这些东西有什么总结吗?谢谢。
【问题讨论】:
【参考方案1】:干净的架构旨在分离各层。业务层、数据层和表示层将是独立的应用程序。因此,您将增加它们中的每一个的可重用性。 MVVM 作为设计模式应该在表示层中实现。表示层将与业务层(或领域层)交互,业务层将使用数据层来共享数据。
【讨论】:
那你如何连接 use sasese 和 mvvm layer 呢? mvvm 中的 "model" 与 "CA" 一起使用时代表什么【参考方案2】:MVVM 只是presentation layer
中干净架构的一部分。它只是一组关于如何从 UseCase 显示数据的规则。
使用干净架构的好处之一是我们可以更改presentation layer
中的设计模式,而无需更改domain layer
或用例。
因此,例如,如果我们使用 let say MVI,然后更改为 MVVM,则可以轻松顺利地完成.. :)
【讨论】:
那你如何连接 use sasese 和 mvvm layer 呢? mvvm 中的 "model" 与 "CA" 一起使用时代表什么 @IronHide 我们可以通过简单地将 useCase 与 MVVM 层连接到 ViewModel 中。 MVVM(表示层)中的模型是用例的输出。当然,viewModel 代码中的实现取决于你如何返回 useCase 的输出。 好的有趣的想法,我在考虑使用用例代替模型,对我来说,用例不仅仅是单个动作,它是一系列动作(步骤)可能需要调用它的方法,所以对我来说,该模型是从一开始就使用的,而不是作为最终结果。 我猜你的意思是模型是简单的 DTO 用于用例交互而不是像纯 mvvm 模式那样建模?可惜 bob 叔叔在他的例子中没有使用 mvvm 如果 DTO 对你来说不够用,那么你可以在 viewModel 中将 DTO 模型转换为 UI 模型。而且我同意 UseCase 不应该对 UI 中的模型(MVVM)一无所知,因为 UseCase 只是给出了一个流程的输出。如果 useCase 没有输出,至少在处理完成时返回 void。【参考方案3】:MVVM 只是一种管理您正在使用的任何架构的视图层的技术。
简洁架构是一种组织层间通信的方式。它们不是相互排斥的
具有干净架构的 MVVM 层 代码分为三个独立的层:
表示层 域层 数据层表示层 在这里,与视图和动画相关的逻辑发生在这里。它使用 Model-View-ViewModel (MVVM),但您可以使用任何其他模式,例如 MVC 或 MVP
【讨论】:
那你如何连接 use sasese 和 mvvm layer 呢? mvvm 中的“model”与“CA”一起使用时代表什么?【参考方案4】:据我了解:
没有干净架构的 MVVM:
______________________________________________
UI
- - - - - - - - - - - - - - - - - - - - - - -
Presenter/ViewModel (Business Logic)
______________________________________________
Repository
DataSource
______________________________________________
具有简洁架构的 MVVM:
______________________________________________
UI
Presentation Layer
Presenter/ViewModel
______________________________________________
UseCases + Entity (Business Logic) Domain/Business Layer
______________________________________________
Repository
Data Layer
DataSource
______________________________________________
【讨论】:
以上是关于前端领域的 “干净架构”的主要内容,如果未能解决你的问题,请参考以下文章
asp.net core系列 64 结合eShopOnWeb全面认识领域模型架构