Node Modular Architecture Node Modular Architecture node.js node.js

Node Modular Architecture


I'd recommend going for solution 2.

  • Break down all your code in small modules.
  • Implement loose-coupling with event emitters.
  • there is no added value in storing each module as its own npm package, unless they are used stand-alone outside of your application.

The two problems you have described are simply caused by the fact that each module is independently stored as an npm package.

Benefits

  • Problem 1 is solved as you don't need to manage npm packages in package.json anymore.
  • Problem 2 is solved as you only have one package.json managing all the dependencies
  • You still have a clean modular structure thanks to usage of separate node.js modules.

Example implementation

A few months ago, I refactored a monolithic node.js app using these principles, and it really eased the maintenance, and didn't add overhead to the build process.

The pattern is the following:

main module is 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 workvar 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

A microservice looks like this:

/*** 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


Have you considered different modular structures? The decision of having microservices or a monolith affects how the components communicate with each other, the scalability and deployability of the system, but you will still need to follow best practices in package design. Otherwise you will have the same chain reaction when updating a low level package.

Your current structure of package C depending on package B depending on package A causes difficulties in package management because you need to make too many modifications to lower level packages.

diagram of the current structure

This type of issue is a sign of too much up-front package design, whereas package design really should be done as you go.

The advantage of your current structure is that it does not have any acylicdependencies. If you update module B, you know exactly module C is affected,and nothing changes with module A. It should stay this way.

Managing Dependencies

Dependency structure

There are package design principles that are directly related to the issues you have:

The Stable Dependencies Principle
Depend in the direction of stability

Given the original structure C -> B -> A, most of your changes should occur in C and A should not any reason to change.

The Stable-Abstractions Principle
A package should be as abstract as it is stable

Relates to the previous principle. Abstract classes leave out specific implementations, and there are many ways you can do that with Javascript.

If you follow these principles well, you might find that it is not an issueto have more than three packages, because the lower level packages won't change much.


What should be in a package?

Package by Feature

The MVC frameworks so popular these days have a structure that separate controller, model and view into different folders. This structure does not scale very well, and after the project has expanded for a while, it becomes hard to visualize what the project does, how different parts are connected to each other, and it's not very convenient to see all the files related to a particular feature. This approach called package by layer does not expand very well.

A better way to organize your packages would be to Package by Feature. Now layering is not a bad thing, and when you package by feature, you should still have this layered structure.


I am working on a Theoretical Model to solve this issue, and just making some research, a bit of experimentation and a bit of common sense.

Modular Principles List

  1. The code is physically organized in folders by feature.
  2. The code does one, and only one thing, and it does it very well.
  3. Each feature can be added or removed anytime because it has no dependencies with other features.
  4. All communications with other modules are not made directly. A Sandbox or middleware is used instead.
  5. If several modules require a common functionality, they take it from an upper hierarchical semi-immutable () structure.

Advantages and disadvantages This approach looks for one specific goal: loose coupling. The idea behind is that every module can be implemented on its own, it can be developed and tested individually, and many people can contribute at the same time with features.Look at WordPress, or node ecosystem. Take this example and move it down to your project.

One example

CSS has been a clear example to me of how this modular approach may work. If you have a site with many pages, and many sections in it, and your client wants every section to have a variant of the look and feel, probably you will end up with a few hundreds of CSS definitions in one minified big CSS file.

Governance of that CSS may require variables, preprocessor, PostCSS, Javascript handling.... but the real ting is that you wil never use more than a few CSS definitions on every page.

If all pages can be split into modular types and each type with its own rules, probably you can end up with much more code, but smaller files applied one at the time. Probably you will not need to minify any file because the all have what is needed.

Folder Structure ProposalI am working the idea that all code should be organized the same way.Main folders:

  • core
  • extensions
  • engines

Inside each a structure like this:core
----- extension-manager
------------ css
------------ img
------------ media
------------ js
------------ view
------------ db
------------ data
------------ aux
------------ config.json
------------ README.txt
------------ settings.txt
----- sandbox
------------ css
------------ img
------------ media
------------ js
------------ view
------------ db
------------ data
------------ aux------------ config.json
------------ README.txt
------------ settings.txt
----- global
------------ css
------------ img
------------ media
------------ js
------------ view
------------ db
------------ data
------------ aux
------------ config.json
------------ README.txt
------------ settings.txt
extensions
----- appointments
------------ css
------------ img
------------ media
------------ js
------------ view
------------ db
------------ data
------------ aux
------------ config.json
------------ README.txt
------------ settings.txt
----- calendar
------------ css
------------ img
------------ media
------------ js
------------ view
------------ db
------------ data
------------ aux
------------ config.json
------------ README.txt
------------ settings.txt
----- promotions
------------ css
------------ img
------------ media
------------ js
------------ view
------------ db
------------ data
------------ aux
------------ config.json
------------ README.txt
------------ settings.txt

Hope these ideas may help, any comments are welcome.