HEXIN Node Framework

This is a node framework that enforces scalability, seperation of concern, and obscuring redundent code while empowering developers with what they need to implement additional features without looking at bloated files. This framework was built with .NET's MVC ideology in mind, and takes ideas from their use of Service & Repository (Repo coming soon) Pattern, Auto-Mapper, Inversion of Control, app_start config.

App

The app folder is where developers will spent majority of their time in.

The Flow

All requests made to the server will flow through the system into the following five layers and in this specific order:
Controllers <-> Services <-> (UnitOfWork <-> Repos) <-> Models

It is very important to remember that a layer can never skip over another layer and directly communicate with another layer. All communication must be to their adjacent layers.

The purpose of this flow is to maintain the Separation-of-Concern between each of them. Each layer has its own duty and will only handle their own tasks. A very good example for this would be if you decide to change the database to another type, you just have to swap the models and update the code for the repo (adaptor for data accessing). For this change, all code updates are contained and you do not have to scour the whole project for all endpoints.

Controllers

Controller is responsible for authorization, DTO mapping, calling service methods, and returning of result/errors

The controller will have two base class to extend from; the ControllerBase and ControllerCrudBase. The ControllerBase holds the essential methods and variables needed. ControllerCrudBase is an extension of ControllerBase and holds all basic CRUD calls for the given Service provided in the constructor. Every controller must extend either ControllerBase or ControllerCrudBase.

Constructor

constructor(app: Object) - controller is instantiated with an app argument

Helper methods

authenticate - a middleware that verifies whether user is logged in or not

router.post('/',this.authenticate,(req, res, next){

authorize(...roles: Array<string>) - a middleware that can be used as authenticate or can be called as function with permitted roles

router.post('/',this.authorize,(req, res, next){

router.post('/',this.authorize(),(req, res, next){// (same as above)

router.post('/',this.authorize('user','admin'),(req, res, next){

renderRoutes(router: Object): void - this is the method where you declare all the routes for this controller. You must override this method in each controller

this.isVerb(verb: string, inVerbList: Array<string>|string): boolean - Function that returns true if first arg is in second argument. Useful for checking if req.method is equal to one of the verbs listed

this.isVerb(req.method,'PUT|POST|DELETE')

this.isVerb(req.method,['PUT','POST','DELETE'])

Example

'use strict';

const{ControllerCrudBase}=require('hexin-core');

// Service

constAuthService=newrequire('../services/AuthService');

module.exports=classAuthControllerextendsControllerCrudBase{

constructor(app){

constbaseMiddlewares=[

(req,res,next)=>{

// if method is create, update, or delete, ensure user is authenticated first

if(this.isVerb(req.method,'PUT|POST|DELETE')){

this.authenticate(req, res, next);

}else{

next();

}

}

];

super(app,'auth', AuthService, baseMiddlewares);

}

renderRoutes(router){

const{authorize}=this;

// Routes definition set here

// [POST] Register

router.post('/register',(req,res,next)=>{

co(function*(){

const{m}= req;

constresult=yieldm.createUser(req.body);

res.send(result);

})

.catch(error=>{

next(error);

});

});

}

}

Helpers

Helpers are small reusable functions that'll be applicable in multiple endpoints and could even possibly be transferable to other projects

Models

A model defines a database collection/table structure and its constraints/rules. By default, the model uses mongoose to manage this definition and data-modification methods.

Services

Service is responsible for the system's business logic

The service will have two base class to extend from; the ServiceBase and ServiceCrudBase (corresponds to controller's two base classes). The ServiceBase holds the essential methods and variables needed. The ServiceCrudBase is an extension of the ServiceBase class and holds all basic CRUD calls for the given Model provided in the constructor. Every service must extend either ServiceBase or ServiceCrudBase. It is highly recommended that you use the library HandleError to handle any errors