最佳实践Node.js-项目结构提示


哈Ha! 我将向您展示Yoni Goldberg对“ Node.js Best Practices ”第一章的改编译文。 关于Node.js的一系列建议已发布在github上,拥有近30吨的恒星,但到目前为止,在哈布雷(Habré)上尚未提及。 我想这些信息至少对初学者有用。

1.项目结构提示


1.1按组件组织项目


大型应用程序最严重的错误是具有大量依赖关系(意大利面条式代码)的巨大代码库形式的整体式体系结构,这种结构极大地减缓了开发速度,尤其是新功能的引入。 提示-将代码分成单独的组件,对于每个组件,请为组件模块选择自己的文件夹。 每个模块都必须小而简单,这一点很重要。 在“详细信息”部分中,您可以查看项目正确结构的示例。

否则:开发人员将很难开发产品-添加新功能和对代码进行更改将很慢,并且很有可能破坏其他相关组件。 人们认为,如果不划分业务部门,那么在扩展应用程序时可能会出现问题。

详细资料
一段说明

对于中等大小及以上的应用程序,单片程序确实很糟糕-一个具有很多依赖关系的大型程序简直难以理解,并且常常导致产生意大利面条式代码。 即使是经验丰富的程序员,他们也知道如何正确地“准备模块”,他们在体系结构设计上花费了大量的精力,并试图仔细评估对象之间关系的每次更改的后果。 最好的选择是基于一组小型组件程序的体系结构:将程序划分为不与任何人共享文件的单独组件,每个组件应包含少量模块(例如,模块:API,服务,数据库访问,测试等),从而使组件的结构和组成显而易见。 有些人可能将此架构称为“微服务”,但必须了解微服务不是您应遵循的规范,而是一系列原则。 根据您的要求,您可以采用这些原则中的个别原则,也可以采用微服务体系结构的所有原则。 如果您保持较低的代码复杂度,则这两种方法都很好。

您要做的最少的工作就是定义组件之间的边界:在项目的根目录中为每个组件分配一个文件夹,并使它们独立。 只能通过公共接口或API来实现对组件功能的访问。 这是保持组件简单性,避免“依赖关系”并让您的应用程序成长为成熟的微服务的基础。

博客引用:“扩展要求扩展整个应用程序”
来自MartinFowler.com博客
整体应用程序可能会成功,但是人们对其感到越来越沮丧,尤其是在考虑将其部署到云时。 在应用中进行任何甚至很小的更改都需要组装和重新部署整个整体。 经常很难保持良好的模块化结构,其中一个模块的更改不会影响其他模块。 扩展需要扩展整个应用程序,而不仅仅是单个应用程序,当然,这种方法需要更多的精力。

博客引用:“您的应用程序的架构是什么?”
来自叔叔鲍勃博客
...如果您去过图书馆,那么您将代表其建筑风格:正门,接待台,阅览室,会议室以及许多带书架的大厅。 建筑本身会说:这座建筑是图书馆。

那么您的应用程序的架构是什么呢? 当您查看顶级目录结构和其中的模块文件时,他们说:我是在线商店,我是会计师,我是生产管理系统? 还是他们大喊大叫:我是Rails,我是Spring / Hibernate,我是ASP?
(译者注,Rails,Spring / Hibernate,ASP是框架和Web技术)。

具有自主组件的正确项目结构



项目结构不正确,按用途对文件进行分组



1.2分隔组件的各层,不要将它们与Express数据结构混合


您的每个组件都必须具有“层”,例如,要使用Web,业务逻辑,访问数据库,这些层必须具有自己的数据格式,并且不能与第三方库的数据格式混合。 这不仅清楚地分离了问题,而且极大地方便了系统的验证和测试。 API开发人员通常通过将Express Web层对象(如req,res)传递到业务逻辑和数据层来混合层-这使您的应用程序依赖于Express并与Express紧密连接。

否则:对于混合了层对象的应用程序,提供代码测试,组织CRON任务和其他非Express调用将更加困难。

详细资料
将组件代码分为几层:Web,服务和DAL



另一面是在一个gif动画中混合图层



1.3将基本实用程序包装在npm软件包中


在由具有各自存储库的各种服务组成的大型应用程序中,应将通用工具(如记录器,加密等)与您自己的代码包装在一起,并以私有npm软件包的形式提供。 这使您可以在几个代码库和项目之间共享它们。

否则:您必须发明自己的自行车才能在单独的代码库之间共享此代码。

详细资料
一段说明

一旦项目开始发展,并且使用相同的实用程序在不同服务器上具有不同组件,就应该开始管理依赖项。 如何允许多个组件使用它而不在存储库之间复制实用程序的代码? 为此有一个特殊的工具,称为npm...。 首先用您自己的代码包装第三方实用程序包,以便将来可以轻松替换它,并将此代码作为私有npm包发布。 现在,您的整个代码库都可以导入实用程序代码,并使用所有npm依赖项管理功能。 请记住,有以下几种发布npm软件包供个人使用而无需打开它们以供公众访问的方法: 私有模块私有注册表本地npm软件包

在不同环境中共享自己的共享实用程序


1.4将Express分为“应用程序”和“服务器”


避免在一个大文件中定义整个Express应用程序的不愉快习惯,将“ Express”代码分成至少两个文件:API声明(app.js)和www服务器代码。 为了获得更好的结构,请在组件模块中放置一个API声明。

否则:您的API仅可用于通过HTTP调用进行测试(这比较慢,并且生成覆盖率报告的难度更大)。 不过,我想,在一个文件中使用数百行代码并不是一件很有趣的事情。

详细资料
一段说明

我们建议使用Express应用程序生成器及其构建应用程序数据库的方法:API声明与服务器配置(端口数据,协议等)分开。 这使您可以在不进行网络调用的情况下测试API,从而加快测试速度,并更轻松地获取代码覆盖率指标。 它还允许您为不同的服务器网络设置灵活地部署相同的API。 另外,您还可以更好地分隔职责和更简洁的代码。

示例代码:API声明,必须在app.js中
var app = express(); app.use(bodyParser.json()); app.use("/api/events", events.API); app.use("/api/forms", forms); 

代码示例:服务器网络参数,应位于/ bin / www
 var app = require('../app'); var http = require('http'); /** *          Express. */ var port = normalizePort(process.env.PORT || '3000'); app.set('port', port); /** *  HTTP-. */ var server = http.createServer(app); 


示例:使用supertest(一个流行的测试包)测试我们的API
 const app = express(); app.get('/user', function(req, res) { res.status(200).json({ name: 'tobi' }); }); request(app) .get('/user') .expect('Content-Type', /json/) .expect('Content-Length', '15') .expect(200) .end(function(err, res) { if (err) throw err; }); 


1.5使用基于环境变量的安全分层配置


理想的配置设置应提供:

(1)从配置文件和环境变量中读取密钥,
(2)在存储库代码之外保守秘密,
(3)配置文件的分层(而不是平坦)数据结构,以利于设置工作。

有几个软件包可以帮助实现这些点,例如:rc,nconf和config。

否则:不遵守这些配置要求将导致单个开发人员和整个团队的工作中断。

详细资料
一段说明

当您处理配置设置时,许多事情可能会令人烦恼并减慢速度:

1.如果需要输入100个以上的键(而不仅仅是在配置文件中进行固定),则使用环境变量设置所有参数将变得非常繁琐,但是,如果仅在设置文件中指定配置,则对于DevOps可能不方便。 一个可靠的配置解决方案应该结合这两种方法:配置文件和环境变量的参数覆盖。

2.如果配置文件是“平面” JSON(也就是说,所有密钥都写在一个列表中),那么随着设置数量的增加,将很难使用它。 可以通过根据设置部分形成包含键组的嵌套结构来解决此问题,即 组织一个分层的JSON数据结构(请参见下面的示例)。 有一些库可让您将此配置存储在多个文件中,并在运行时将它们中的数据合并。

3.不建议在配置文件中存储机密信息(例如数据库密码),但是对于在何处以及如何存储此类信息没有明确的便捷解决方案。 一些配置库允许您加密配置文件,另一些配置库允许您在git commit期间对这些条目进行加密,或者您可以将秘密参数保存在文件中并在部署期间通过环境变量设置其值。

4.一些高级配置方案要求您通过命令行(变量)输入密钥,或通过集中式缓存(例如Redis)同步配置数据,以便多个服务器使用相同的数据。

有npm库可以帮助您实现大多数建议,我们建议您看一下以下库: rcnconfconfig

代码示例:分层结构有助于查找记录并使用大量的配置文件

 { // Customer module configs "Customer": { "dbConfig": { "host": "localhost", "port": 5984, "dbName": "customers" }, "credit": { "initialLimit": 100, // Set low for development "initialDays": 1 } } } 

(译者注,注释不能在经典JSON文件中使用。上面的示例摘自配置库文档,该文档添加了用于从注释中预清除JSON文件的功能。因此,该示例相当有效,但是,带有默认设置的ESLint之类的linter可能相似格式的“发誓”)。

译者的后记:

  1. 该项目的描述说,已经启动了俄文翻译,但是我在那里找不到该翻译,因此我着手了这篇文章。
  2. 如果翻译对您来说很简短,请尝试在每个部分中扩展详细信息。
  3. 对不起,插图没有翻译。

Source: https://habr.com/ru/post/zh-CN454476/


All Articles