前端领域的 “干净架构”

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-brandhttps://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),但您可以使用任何其他模式,例如 MVCMVP

    【讨论】:

    那你如何连接 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全面认识领域模型架构

    Android中具有干净架构的mvvm和没有干净架构的mvvm有啥区别?

    Bob大叔的干净架构方法——推荐的包结构是啥?

    干净的架构 UseCases 与具有功能的控制器

    干净的架构:与不同的层共享相同的模型/实体