Backend Modules
An Indepth guides of the CleverStack NodeJS Backend, covering installation, configuration, usage, extending, testing and much more.
An Indepth guides of the CleverStack NodeJS Backend, covering installation, configuration, usage, extending, testing and much more.
CleverStack introduces a new and powerful approach to modules, that was designed with three goals in mind; flexibility, simplicity, and re-usability - as best practice, all functionality including what is in the CleverStack Core should be defined inside of modules.
CleverStack removes the complexity from installing & configuring modules, makes developing modules easier (vertical scale), and allows you to easily re-use modules with little or no code change between projects. (horizontal scale).
CleverStack encourages developers to shift their thinking and minset towards not only viewing the modularization principles on a vertical scale, (e.g. reducing developing time), but also on a horizontal scale. (e.g. not needing to focus on every single detail, being able to take a step back and look at your environment across the horizon).
Complexity kills. It sucks the life out of developers, it makes products difficult to plan, build and test, it introduces security challenges and it causes end-user and administrator frustration.
A Module is similar to a plugin in other software, or an npm package, but even better. The key difference is that Modules are first-class citizens, and everything is a Module in CleverStack, including both the core framework functionality and the code written for your application.
A Module is simply a structured set of files within a directory that implement a single feature. You might create a blog-module, a forum-module or a users-module for user management. (many of these exist already as open source modules).
Each directory contains everything related to that feature, including all of it's classes, controllers, services, models, tasks, configuration, utils (both lib and grunt/gulp), seed data, e2e & unit testing, and defines all dependencies in it's own package.json file. Every aspect of a feature exists in a module and every feature lives in a module.
CleverStack makes it easy to pick and choose which features to enable in your application and gives you the freedom to optimize them the way you want!
There are two ways of constructing a software design. One way is to make it so simple that there are obviously no deficiencies. And the other way is to make it so complicated that there are no obvious deficiencies.
NPM is the package manager for Node.js. It was created in 2009 as an open source project to help JavaScript developers easily share packaged modules of code and is dedicated to the long term success of the JavaScript community.
But packages on node are often libraries/api's, are hard to install, or require complex setup and configuration of the package and it's dependencies after npm has finished installing the package before they are ready for use.
But that is not a bad thing, this is exactly how NPM should work, and why it's so good at what it does, it's designed to be used to manage all dependencies written in JavaScript and does not complicate things by trying to do too much or out of scope custom implementations for any given platform/browser and because of that it is extremely reliable.
NPM was designed to be flexible enough to provide an abstraction layer that developers can use to build upon - for example it could greatly reduce the codebase of Bower, improve its dependency management, increase collaboration, and allow bower to focus on new features instead of duplicating already solved software problems. This is only one example use case of using NPM like we have, the possibilities of other projects using NPM are endless when you look on github.
CleverStack uses NPM to enhance the development lifecycle for developers, but doesn't change or interfere with it, using industry best practices, so that when CleverStack is used to develop NodeJS powered Applications, they are flexible, simple, modular, and meet our vertical and horizontal goals.
Before CleverStack implements custom code we always carefully consider the options available at the time and the need for the feature before writing custom solutions.
Additionally, if one of CleverStack's custom implemented features or solutions is implemented and becomes available in a new version of NPM, that is a viable replacement for ours, we will always change over to use the NPM implementation - it reduces our code base (DRY) and ensures CleverStack is as collaborative as possible.
We take advantage of NPM as much as possible, and without NPM, CleverStack would be impossible! (because we would have to write a large and complex dependency management system... which would be a terrible idea considering npm is available now.)
CleverStack's Module System provides a very thin abstraction layer ontop of NPM, which because of its very small core codebase is easy to maintain, has a very low risk of severe bugs, has community support for speedy bug fixes to reported or common issues, that fascilitates creating the most modular code possible when developing on the NodeJS runtime, without sacrificing code quality or developer sanity.
Is an extended Class
, that inherits from EventEmitter
, and is responsible for iterating over the modules inside the modules folder to load and trigger a sequence of events for each of the modules in an asynchronous fashion.
Is an extended Class
, that inherits from EventEmitter
, and must be exported by all CleverStack modules - See the Module Class Documentation for more information, additionally you can view the code on github.
This module provides Dependency Injection for CleverStack, it is non blocking, Lightning-fast, easy to use, flexible, and makes using DI exactly the same for the Node Backend and the Angular Frontend, with the exception of optional dependencies - See the Dependency Injection Documentation for more information.
The Magic that enables CleverStack to do things like require('injector')
, require('config')
and require('module-name')
as well as many others - See the Magic Modules Documentation for more information.
Each Module is extended from a Class, we do this for many reasons but none as important as taking advantage of V8's Hidden Classes, Inline Caches & Optimizing Compiler to allow V8 to generate efficient and performant machine code automatically, so your application is as Lightning-fast to execute as well as write and maintain. - See the Class Documentation for more information.
NPM is the package manager for Node.js. It was created in 2009 as an open source project to help JavaScript developers easily share packaged modules of code and is dedicated to the long term success of the JavaScript community - It is responsible for our dependency management and is used by commands like clever init
, clever install
and clever-setup
.
For an in-depth example of how to create CleverStack enhanced Node Modules, take a look at the backend-example-module
To install this module in your application use the CleverStack CLI's install
command.
(ie. clever install backend-example-module
)
Installing, Removing, Upgrading or Downgrading modules is as simple as running clever
with the name of the command you want to run, like clever install
with the name of the module you want to install. (for example, clever install backend-example-module
)
grunt server
or node app
)
If you want to run, or are trying to run commands without the CLI (clever
command) - Remember to set your NODE_PATH
,
checkout Magic Modules for more information.
Module names should not conflict with published npm packages, unless you don't want to use the published npm package in your application.
For example imagine you wanted to build a cleverstack module that integrates stripe, you wouldn't name your cleverstack module stripe
, because if you and tried to use the stripe api library from npm, and tried to require('stripe')
it would include your module not the npm package you expected, in this case the best practice for naming the cleverstack module would be clever-stripe
.
If you see a command failed error in your terminal/console after installing a module and seeing a message that states it's about to rebase or seed your database, it is highly likely that something is wrong with your configuration that you need to fix.
See Configuration and Seed Data for information.
Because CleverStack points your NODE_PATH
to the backend/lib
and backend/modules
folders, this means the loading order of modules does not matter, as each module can be loaded using require('module-name')
in any js file from any module in the application - That mean's your application's will always resolve and load modules in the best possible way automatically.
You can specify multiple modules in each install, remove, upgrade or downgrade command separated by spaces.
$ clever install backend-example-module
$ clever install backend-example-module@1.0.3
After running this command you may be prompted to configure the module/s your installing, see Configuration for more information. Additionally you may be prompted to setup the seed data for the module/s your installing, see Seed Data for more information.
If you see a command failed error in your terminal/console after installing a module and seeing a message that states it's about to rebase or seed your database, it is highly likely that something is wrong with your configuration that you need to fix.
See Configuration and Seed Data for information.
$ clever remove backend-example-module
$ clever upgrade backend-example-module
$ clever downgrade backend-example-module@1.0.3
Class
that inherits from EventEmitter
The Module Loader is responsible for iterating over the modules inside the modules folder to load and trigger a sequence of events for each of the modules in an asynchronous fashion.
For an in-depth example of how the Module Loader is used in Cleverstack please see node-seed/app.js.
This section of the documentation is either out-dated or incomplete, please ask for help in github or gitter if you need more information.
Is triggered directly before resources (files) are loaded for all modules using the ModuleLoader.
Is triggered after all resources (files) have loaded but before any routes have been attached.
Is triggered directly before routes are initialized for all modules being loaded.
Is triggered before loading modules.
1 moduleLoader.on('beforeLoad', function() {
2 // ...
3 });
Is triggered after all modules have been loaded, please see backend/app.js for example usage.
Is triggered after all modules have had their routes initialized.
1 moduleLoader.on('routesInitialized', function() {
2 // ...
3 });
To make configuration as easy and comprehensive as possible, you are able to define two configuration files, default.json and global.json, these files are automatically deep merged with your projects other configuration files. See Backend Configuration Documentation for more information.
Each of these JSON config files must contain (if they exist on disk) an object that has all configuration objects nested in an object under one key which needs to be named the same as your <module>.
{
"module-name": {
"config" : "here",
"another" : "as well"
}
}
example-module/
├── bin/ # module scripts & binaries
├── classes/ # module classes
├── config/ # module specific configs and setting overrides
├── controllers/ # module controllers
├── exceptions/ # module custom exceptions
├── models/ # module models
├── schema/ # your schema is stored in here
│ └── seedData.json/ # seed data is used to populate your database tables
├── services/ # module services
├── tests/
│ ├── e2e/ # module end to end tests
│ └── unit/ # module unit tests
├── utils/ # module utils
├── Gruntfile.js # module grunt tasks
├── module.js # main module class
├── package.json # module npm dependencies
└── routes.js # module routes
{
"moduleName": {
"config": "here",
"another": "as well"
}
}
Modules have lifecycle hooks for every situation, like loading, routing, shutdown etc.
This presumes you have { "main": "module.js" } in your modules package.json, otherwise it is named what you want OR if not set at all this file will be called index.js. (As per standard NPM Modules)
Here is an example of a module.js file
// Example module.js
var Module = require( 'classes' ).Module;
module.exports = Module.extend({
preSetup: function() {
console.log( 'Hello, this module is about to be setup by the moduleLoader' )
}
});
Your 'module.js' file has some lifecycle hooks that you can use to perform operations at certain times during the applications lifecycle, the hooks are fired in the order they are listed here on each module.
Example of using a hook:
1 var Classes = require('classes')
2 , Module = Classes.Module;
3
4 var CleverModule = Module.extend({
5 preInit: function() {
6 console.log('preInit hook, init is about to be called');
7 }
8 });
9
10 module.exports = CleverModule;
Modules hooks are applied to each module in the order they are loaded.
This section of the documentation is either out-dated or incomplete, please ask for help in github or gitter if you need more information.
1 var Classes = require('classes')
2 , Module = Classes.Module;
3
4 var CleverModule = Module.extend({
5 configureApp: function (app, express) {
6 app.use(express.cookieParser());
7 this.emit('appConfigured');
8 }
9 });
10
11 module.exports = CleverModule;
Every Module in CleverStack is extended from the Module
Class, which was extended from the base CleverStack Class
and inherits from EventEmitter
, every Module
will automatically listen to events coming from the ModuleLoader
as well as emitting events that the ModuleLoader
and other Module
's can listen to.
$ clever new example-module
Imagine you had a module called example-module
, then backend/modules/example-module/module.js
should at the very least contain the following basic extended Module
Class.
1 var Classes = require('classes')
2 , Module = Classes.Module;
3
4 var CleverModule = Module.extend({
5 // Methods, Module Hooks and Templated Event Handlers go here.
6 });
7
8 module.exports = CleverModule;
IMPORTANT This example assumes that you had added "main": "module"
to example-module/package.json
file.
{
"name" : "example-module",
"description" : "Example CleverStack Module",
"version" : "1.0.0",
"main" : "module"
}
Otherwise it would expect to find the above code in the backend/modules/example-module/index.js
file.
Inline with NPM standards, the names of modules should be lowercase and seperated by dashes (dasherized) - and the names should not conflict with published npm packages unless you don't want to use the published npm package in your application.
This section of the documentation is either out-dated or incomplete, please ask for help in github or gitter if you need more information.
We believe in testing our code as we develop, then if something breaks we know exactly where to start looking. Spending more time on developing the features we love.
To run the unit tests for your app and modules, from the backend root directory run:
clever test unit
Or simply use the alias:
clever test
To setup your environment for e2e testing with Protractor run the following command. This will install Selenium Server (WebDriver), ChromeDriver & PhantomJS based on your OS. If you wish to run selenium server in a separate terminal then use Webdriver Manager (see below).
clever test e2e
To generate a code coverage report for your tests, from the backend directory run:
$ clever test coverage
Your browser should open the following URL:
$ http://localhost:5555
Then select your report from the list.
If you have any problems (including errors, exceptions and even general usage) with the CleverStack Node-Seed.