节点模块化架构

The*_*heo 21 architecture node.js npm

我正在构建一个相当大的nodejs应用程序.为了避免单片节点应用程序,我已经沿着更模块化系统的架构路线走下去,将几个组件分解成单独的npm模块.这些是使用npm发布并安装在依赖模块中.我有大约6个不同的模块(我想要进一步突破),现在管理包变得很困难.

问题是:

  1. 有嵌套依赖,所以如果我更改模块A而模块B依赖于模块A而模块C依赖于模块B,那么当我更新模块AI时需要发布它的新版本,这意味着我需要在模块中更新它B,这意味着我还需要发布它,然后最后我需要在模块A中安装新版本......你可以看到那可能是一个痛苦的地方.更重要的是,所有package.json中的版本更新都是手动的,因此容易出错,等待每次发布都很耗时.
  2. 模块可以共享npm依赖项,因此有时会在程序包更新时发生冲突.模块越多,冲突的可能性就越大.

好处是我们有一个非常模块化的系统,其中库可以轻松地重复使用,并且由于不存在任何循环依赖性,因此存在明确的模块层次结构.

可能的解决方案

  1. Monolith - 在单个存储库中将依赖关系作为单个应用程序进行管理,每个模块只是成为一种服务.这意味着只需要进行一次更新,并且所有模块apis都将同步.但是,引用代码中的库可能有点痛苦(因为我相信它们必须相对于本地文件被引用),我不确定如何强制执行模块之间的结构层次结构并且代码重用将会对存储库外的模块更难.

  2. 微服务 -每一模块的微型服务.这保留了模块化系统的所有好处,但我担心它会给构建增加很多复杂性,并且管理所有服务本身就会成为一个全职工作.

  3. 继续 - 找出一种方法来保持当前的体系结构,但消除推送更新的麻烦等.也许脚本更新版本和shrinkwrap以确保正确的依赖.我认为这既困难又可能导致它成为不同种类的单片系统.

选项1对我来说似乎最容易管理,但如果我不需要,我不想失去模块化结构.

这是一个相当广泛的问题,但任何建议/意见/评论都会非常有用.

谢谢

Meh*_*hdi 6

我建议去解决方案2。

  • 将所有代码分解为小模块。
  • 与事件发射器实现松耦合。
  • 将每个模块存储为自己的 npm 包没有任何附加价值,除非它们在您的应用程序之外独立使用。

你所描述的两个问题仅仅是因为每个模块独立存储为npm包导致的。

好处

  • 问题 1 解决了,因为您不再需要管理 npm 包package.json了。
  • 问题 2 已解决,因为您只有一个package.json管理所有依赖项
  • 由于使用了单独的 node.js 模块,您仍然拥有干净的模块化结构。

示例实现

几个月前,我使用这些原则重构了一个单体 node.js 应用程序,它确实简化了维护,并且没有增加构建过程的开销。

图案如下:

主要模块是 app.js

var sys = require('sys')
    , events = require('events')
    , UserModel = require('./model/user') // this is a microservice taking care of managing user data
    , Logic = require('./controller/logic')   // this is another microservice doing some work

var App = function (server, app) {

    this.controller = (
        logic: new Logic(this) // "this" is the app instance, it's passed to all microservices, which will benefit from the access to event emitter...
    }
    this.model = {
        new UserModel(this)
    }

    // ...
}

sys.inherits(App, events.EventEmitter)

module.exports = App
Run Code Online (Sandbox Code Playgroud)

一个微服务看起来像这样:

/**
* Logic functions
* 
* this controller does ...
*
* @constructor
*
*/
function Logic(app) {

    /****************************************
    *
    * Private methods
    *
    ****************************************/

    /**
    * this is a private function in the controller
    * 
    * @private
    */
    function computeSomething () {

        var res = 3

        app.emit('computed', res) // emit event, that can be liseted to by some other modules

        return res
    }


    /****************************************
    *
    * Public methods
    *
    ****************************************/    

    /**
    * 
    * This function can be called directly by the other modules using "app.controller.logic.work()"
    * 
    */
    this.work = function () {

        return 'result is ' + computeSomething()
    }


    /****************************************
    * 
    * Event listeners
    * 
    ****************************************/

    /**
    * listener: event from the app - loose-coupling magic happens thanks to this. Recommended over public functions.
    *
    * @private
    */
    app.on('data-ready', this.work)

}

module.exports = Logic
Run Code Online (Sandbox Code Playgroud)