文章首发于我的博客 https://github.com/mcuking/bl...译者:这篇文章是在 medium 解说前端架构分层系列的第一篇文章,分层和之前翻译的文章相似,绝对一般我的项目多进去两层,畛域层(从业务抽离进去畛域实体)和用例层(实现利用业务逻辑)。另外在编程范式上,绝对面对对象,作者更偏向于采纳函数式,读者可依据我的项目特点抉择适宜本人的形式。
原文链接 https://blog.codeminer42.com/...
这篇博客是《可扩大的前端》系列的一部分,你能够看到其余局部:
#2 — Common Patterns 和 #3 — The State Layer。
对于软件开发的可扩展性这一概念有两个最常见的的意义:代码的性能和可维护性。你能够同时兼顾这两点,然而专一于良好的可维护性会让一件事件变得容易,那就是晋升性能且不影响利用的其余部分。更重要的是,前端与后端有一个重要的区别:本地状态。
在这个系列博客中,咱们将会探讨如何通过理论的通过验证的办法,来开发和保护可扩大的前端利用。咱们大部分的例子将会应用 React 和 Redux,然而咱们会常常与其余的技术栈对比拟,来展现你如何达到同样的后果。让咱们开始这个对于架构方面的系列探讨吧,这是你的软件中最重要的局部。
什么是软件架构?
那么架构到底是什么?说架构是软件中的最重要的局部仿佛很自以为是,但请耐新看上来。
架构是使软件的各个局部互相交互以突出必须要做出的最重要的决策,并且推延主要的决策和实现细节的形式。设计一个软件的架构意味着将理论的利用从反对它的技术中拆散开来。你的理论利用不晓得数据库、AJAX 申请、或者 GUI;而是由用例和畛域模型组成。这些用例和畛域模型代表了你的软件所涵盖的概念,请疏忽执行用例的角色或数据在哪里存储等。
对于架构还有一个重要的事件要说:那就是架构不意味着文件的组织,也不是如何命名你的文件和文件夹。 |
dependency container
。是的,看起来很奇怪,而且扩展性不好,但这并不意味着用例的实现就在该对象内。这些只是委托给用例的办法,这些用例在其余中央定义。应用程序的所有用例一起应用一个对象比将它们散布在整个代码库中要好得多,后者会使它们很难找到。有了这个对象,咱们要做的就是将其注入到 actions 中,让每个 action 决定将触发什么用例,对吗?如果你应用的是 redux-thunk,则应用 withExtraArgument 办法能够很容易地实现它,该办法容许你将容器中的每个 thunk 动作作为 getState 之后的第三个参数注入。如果你应用的是 redux-saga,则该办法应该很简略,在该办法中,咱们将容器作为 run 办法的第二个参数进行传递。如果你应用的是 Ember 或 Angular,则内置的依赖项注入机制就足够了。这样做会使 action 与用例解耦,因为你无需在定义 action 的每个文件中手动导入用例。而且将 actions 与用例离开进行测试当初变得非常简单:只需注入一个伪造的用例实现即可,该实现的行为完全符合你想要的形式。你是否想测试如果用例失败,将 dispatch 什么 action?注入一个总是失败的模仿用例,而后测试 action 如何对此做出响应。无需思考理论用例如何工作。太好了,咱们将 state 层注入了 view 层,并将 application 层注入了 state 层。其余的呢?咱们如何将依赖项注入用例来构建 dependency container
?这是一个重要的问题,有很多办法能够解决。首先,不要遗记查看你应用的框架是否内置了依赖项注入,例如 Angular 或 Ember。如果的确如此,则你不应该本人结构。如果没有,你能够通过两种形式来做到这一点:手动或在软件包的帮忙下。手动进行操作应该很简略:- 将你的模块定义为类或闭包,- 首先实例化没有依赖性的模块,- 而后再实例化有依赖的的模块,将它们作为参数传递,- 反复上述步骤,直到实例化所有用例为止,- 导出它们。太形象了?看一些代码示例:container.js`
jsimport api from './infra/api'; // has no dependenciesimport { validateUser } from './domain/user'; // has no dependenciesimport makeUserRepository from './infra/user/userRepository';import makeArticleRepository from './infra/article/articleRepository';import makeCreateUser from './app/user/createUser';import makeGetArticle from './app/article/getArticle';const userRepository = makeUserRepository({ api});const articleRepository = makeArticleRepository({ api});const createUser = makeCreateUser({ userRepository, validateUser});const getArticle = makeGetArticle({ userRepository, articleRepository});export { createUser, getArticle };`
createUser.js`
jsexport default ({ validateUser, userRepository }) => async userData => { if (!validateUser(userData)) { throw new Error('Invalid user'); } try { const user = await userRepository.add(userData); return user; } catch (error) { throw error; }};`
userRepository.js`
jsexport default ({ api }) => ({ async add(userData) { const user = await api.post('/users', userData); return user; }});`
你会留神到,重要局部(用例)已在文件开端实例化,并且是惟一导出的对象,因为它们将被注入到 actions 中。你的其余代码无需理解 repository 的操作形式和工作形式。这并不重要,而只是技术细节。对于用例,repository 是发送 AJAX 申请还是在 LocalStorage 中保留某些内容都没有关系;用例没有职责须要晓得。如果你想在 API 仍在开发中时应用 LocalStorage,而后切换为应用通过网络 API 的调用,只有与 API 交互的代码遵循与 LocalStorage 交互的接口,而无需更改用例。即便你有数十个 use cases(用例), repositories, services 等,也能够如上所述手动实现注入。如果太麻烦而无奈构建所有依赖关系,则能够始终应用依赖注入的库,只有它不会减少耦合。测验你的 DI(Dependency injection) 库是否足够好的一条教训法令是,查看从手动办法转移到应用库是否只须要操作 container 代码即可。如果不是这样,则阐明库太过侵入,你应该抉择其余库。如果你的确要应用库,咱们倡议你应用 Awilix。它非常简单易用,无需手动操作,只需操作 container 文件即可。这个库的作者撰写了一系列无关如何应用以及为什么应用它的很好的文章,点击查看。## 接下来好的,咱们曾经探讨了架构以及如何以一种很好的形式连贯各层!在下一篇文章中,咱们将为方才探讨的层展现一些理论的代码和通用模式,但 state 层除外,它会在独自的文章中介绍。花一些工夫来排汇这些概念。当咱们具体介绍这些模式时,它们将十分有用,所有都会变得更加有意义。到时候那里见!## 举荐浏览链接NodeJS and Good PracticesBob Martin — Architecture the Lost YearsRebecca Wirfs-Brock — Why We Need Architects (and Architecture) on Agile ProjectsDomain-Driven Design