NodeSpring

v0.0.13

Dependency Injection for NodeJS using Javascript decorators

Getting Started

Controllers
   @Controller
   HTTP Methods

Services
   @Service

Dependency Injection
   @Interface
   @Implements
   @Inject
   @PostInject

Unit Testing
   @TestClass
   @Mock
   @InjectMocks
   @Before
   @Test

View the Project on GitHub calbertts/nodespring

Getting started

Start with NodeSpring, it's very easy:

Installation:

$ npm install nodespring --save

The main file must be a class that inherits from ExpressApp:

import {ExpressApp} from 'nodespring'

export default class App extends ExpressApp {

  constructor() {
    super({
        port: 5000,
        classDir: __dirname,
        implConfig: {
          '/interfaces/DBService': './services/DBServiceImpl'
        }
    })
  }
}

new App().start()

Using Express is optional, if you don't want to use it, you can create your own middleware extending from NodeSpringApp.

port: The port where your server is going to run
classDir: The directory where all your classes are placed
implConfig: If you have several implementations for the same interface, would be necessary to define, which one of them is going
to be used



Controllers

A Controller is a Javascript class where you define the end-points you have in your API. Controllers are used to handle the HTTP requests and call the service layer.

@Controller

This decorator expects a class to be used as a Controller

import {Controller} from 'nodespring'

@Controller
export default class MyController {

  // Methods to handle the HTTP requests
}

When you create a Controller, an URL is automatically created: http://localhost:8080/MyController

If you want to change the name used in the URL, you can do:

@Controller({path: 'users'})
export default class MyController {
  ...
}

Now we can access this controller in this way: http://localhost:8080/users



HTTP Methods

Following, the decorators used to handle the common HTTP methods, they expect a method to be used as end-point and can be used only in a Controller class.

import {Controller, Post, Get} from 'nodespring'

@Controller
export default class MyController {

  @Get
  getMessage() {
    return "Message from MyController"
  }

  @Post
  getGreet(name) {
    return "Hi " + name
  }
}

Also if you want to return a JSON or any other kind of contentType, you can do:

@Post({contentType: 'application/json'})
getJSONObject(id, name) {
  return {
    id: id,
    userName: name
  }
}

Each method is an end-point:

http://localhost:8080/MyController/getMessage http://localhost:8080/MyController/getJSONObject

Also, there are decorators to the other HTTP methods:

    @Put, @Delete, @Update



Services

A Service is a Javascript class where you define the methods to access a specific module of your application, let's understand a module as a unit on your business logic, don't think on NodeJS modules, let's assume that a module is a set of NodeJS modules which have a specific responsibility.

These modules usually need to communicate each other, but isn't a good practice to call directly a module from other one, it's preferable to have a Service layer where this communication is defined.

@Service

This decoractor expects a class to be used as a Service, all the services are singleton, it means, only one instance of this class is going to be created and shared through all the application.

Each time a Service is injected, the unique instance is used, that's why all the Services must to be stateless.

import {Service} from 'nodespring'

@Service
export default class MyService {

  // Methods to communicate with the module
}


Dependency Injection

The Dependency Injection (DI) is a common pattern used in several frameworks, if you're a Java programmer and have worked with frameworks like Spring, you will miss some of those great features on NodeJS.

Yes, NodeJS is not Java, I'm not pretending to make it like that, but honestly, going through several DI frameworks for NodeJS, I found them too verbose and compilcated to understad, so that's why, taking advantage of the decorators which are an experimental feature on Javascript, I've created this to make things like DI easier.

@Interface

This decoractor expects a class to be used as an Interface.
An Interface basically represents the shape of an object without specify its behavior, in this way, each time you need to use an object from that class, you aren't going to deal with a specific implementation of it, but with its interface. The only thing you need to know about that class is what it's offering, what are the methods you can use, the implementation details doesn't matter to other modules.

import {Interface} from 'nodespring'

@Interface
export default class DBService {

  saveEntity(entity) {}
  getEntityList(entityType) {}
}

As you notice, we are only specifying what are the methods that other modules will be able to use.

@Implements

This decoractor expects a class to be used as an Implementation.
An Implementation is where you define the behaviour of a specific Interface, where all your third-party libraries are used.

Following, a dummy example what an implementation does:

import {Implements} from 'nodespring'
import DBService from './DBService'

// Assuming you're using mongoose to create your models
import EntityModel from './models/EntityModel'


@Implements(DBService)
export default class DBServiceMongoDB {

  saveEntity(entity) {
    new EntityModel({
      id: '1',
      name: 'entity'
    }).save(() => {
      console.log('saved sucessfully!')
    })
  }

  deleteEntity(id) {
    EntityModel.find({id: id}).remove(() => {
      console.log('Removed!')
    })
  }
}

As you can see, all the details are here, if tomorrow we need to start using MySQL instead of MongoDB, the only thing we will need to do is to create another implementation of DBService and the other modules don't need to be aware about it because they inject using an interface and NodeSpring resolves which implementation has to be used.

@Inject

This decorator expects an Interface as a parameter and it can be used only in class properties.
This is the magic part, we are going to inject our DBService in MyService and make use of it.

import {Service, Inject} from 'nodespring'
import DBService from './DBService'

@Service
export default class MyService {

  @Inject(DBService)
  dbService

  saveEntity() {
    let entity = {
      id: 1,
      name: 'entityName'
    }

    dbService.saveEntity(entity)
  }

  deleteEntity() {
    let id = 1

    dbService.deleteEntity(id)
  }
}

Here, you can notice that MyService doesn't know I'm using MongoDB, also, I'm not creating the instance directly:

dbService = new DBServiceMongoDB()

Because in this way, I'd be creating a hard dependency between those classes and testing this class will be harder.
Instead, I'm injecting using the interface and NodeSpring will resolve at runtime the implementation that needs to be used.

This resolves several problems, the first one is that our code is now decoupled, which is great, the other one is unit testing, I'll be able to mock all those interfaces providing fake implementations in order to test a specific implementation with real isolation.

Finally, all of this ends with a code easier to maintain and be modified by other programmers.

There are some considerations about injecting dependencies:

@PostInject

This decorator expects a method to be executed once all the dependencies injected are resolved.
Although you can use the constructor in each Javascript class, it's important to know that it's possible all the declared dependencies aren't resolved at the moment when the constructor is called, this is because all the dependencies are injected setting them in the instance which is being created.

import {PostInject} from 'nodespring'

@PostInject
init() {
  // All the dependencies were injected
}


Unit Testing

One of the good parts of using Dependency Injection is how easy is to test our code, the injection will allow us to isolate our code without pain.

You can mock the injected dependencies in each test, it allows you to define the way that a certain object behaves, this is pretty similar what you can do with Mockito in Java ecosystem.

@TestClass

This decorator expects an class as a parameter, it's basically to tell to NodeSpring that it's a testing class in order to prepare all the necessary stuff to execute each test.

import {TestClass} from 'nodespring'

@TestClass
export default class MyServiceTest {

  ...
}

@Mock

This decorator expects an Interface as a parameter and it can be used only in class properties.
Mocks an interface basically will assign a fake implementation that you can adjust during the test in order to isolate the code you're testing.

import {TestClass, Mock} from 'nodespring'
import DBService from './DBService'

@TestClass
export default class MyServiceTest {

  @Mock(DBService)
  dbServiceMock

  ...
}

@InjectMocks

This decorator expects an Interface as a parameter and it can be used only in class properties.
When you're testing, you have basically two things: the dependencies and the class you want to test.

Once, you have mocked all the dependencies, you will need to inject those fake implementations in the real object you're gonna test.

import {TestClass, Mock, InjectMocks} from 'nodespring'
import DBService from './DBService'
import MyService from './MyService'

@TestClass
export default class MyServiceTest {

  @Mock(DBService)
  dbServiceMock

  @InjectMocks(MyService)
  myService

  ...
}

@Before

This decorator expects a method as a parameter.

Sometimes, it's necessary to prepare objects or data before to run each test, but remember, those tests should be stateless, it means, you have to prepare all you need for each test in a separate way, they shouldn't depend on any other test. This is essentially because you can have asynchronous operations inside of the class you're testing, it means, you cannot guarantee in which order the tests are going to finish.

This method decorated with @Before is executed just before to call each test, so, if you have three tests, the before method is execute three times. This is useful when you need to prepare something that is common to all the tests just before to start.

import {TestClass, Mock, InjectMocks} from 'nodespring'
import DBService from './DBService'
import MyService from './MyService'

@TestClass
export default class MyServiceTest {

  @Mock(DBService)
  dbServiceMock

  @InjectMocks(MyService)
  myService

  @Before
  initTest() {
    // stuff before test
  }
}

@Test

This decorator expects a method as a parameter.

Each method that is a test, must be decorated with this, otherwise, it isn't going to be called, unless you call it from a test method.

Additionally, each test method will receive an assert object, that is basically an instance of the great npm package: assert.

In order to support asynchronous testing, there's an additional method that you can call when your test is completed: assert.done().

import {TestClass, Mock, InjectMocks} from 'nodespring'
import DBService from './DBService'
import MyService from './MyService'

@TestClass
export default class MyServiceTest {

  @Mock(DBService)
  dbServiceMock

  @InjectMocks(MyService)
  myService

  @Before
  initTest() {
    // stuff before test
  }

  @Test
  test1(assert) {
    this.dbServiceMock.saveEntity = (entityType, entity) => {

      // Simulating async behavior
      setTimeout(() => {

        // You can use all the methods in "assert" npm package
        assert.equal(true, true)

        // Call done() method to finish the current test like in NodeUnit
        assert.done()
      }, 5000)
    }
  }
}


Hope you find NodeSpring useful, this is in an early stage, but it's functional. All kind of PRs are welcome, even with this docs, my english is still in progress ;)