How to optimize module encapsulation in Node.js
Tags: node, designpattern
Published: 2017-09-27 21:32:56

---
title: How to optimize module encapsulation in Node.js
published: true
description: 
tags: nodejs, designpattern
---

## Standard encapsulation in Node.js


### Module export
Any expression declared within a Node.js module can be exported and become available throughout the application. The export mechanism relies on the use of the keyword `exports` to which we assigned a variable name and an expression. For example, if within my `oscar.js` module I want to export the `sayMyName()` and `sayMyAge()` functions I proceed this way:


```javascript
// oscar.js
exports.sayMyName = function() {
    let name = 'Oscar';
    console.log(`My name is ${name}`);
}

exports.sayMyAge = function() {
    let birthDate = '1990-09-19';
    let age = Date.now() - new Date(birthDate) / 31556952000;
    console.log(`I am ${age} years old`);
}
```
This mechanism is very useful insofar as it makes it possible to finely manage access to functions and variables. In fact, every expressions that are not preceded by the `exports` keyword remain privates. The `exports` keyword refers to an object that contains expressions that need to be exported. Rather than adding expressions one by one, this object can be directly manipulated through the `module.exports` keyword. Using this keyword we can refactor `oscar.js` this way:

```javascript
// oscar.js
module.exports = {

    sayMyName: function() {
        let name = 'Oscar';
        console.log(`My name is ${name}`);
    },

    sayMyAge: function() {
        let birthDate = '1990-09-19';
        let age = Date.now() - new Date(birthDate) / 31556952000;
        console.log(`I am ${age} years old`);
    }

};
```
### Module import
The import mechanism relies on the use of the `require` function with the relative path of the module we want to import as argument. Once called, this function returns the `module.exports` object and then it is possible to access by key the expressions it contains. For example, if within my `index.js` module I want to import the `oscar.js` module and call the `sayMyName()` and `sayMyAge()` functions I proceed this way:

```javascript
// index.js
let oscar = require('./oscar');

oscar.sayMyName();
oscar.sayMyAge();
```

## Limitations of standard encapsulation
Let's imagine that my `sayMyName()` and my `sayMyAge()` functions now require a client in oder to read `name` and `birthDate` values into a database. And this client is instantiated as a singleton in the `index.js` module. If I keep the standard encapsulation I need to rewrite my modules this way:

```javascript
// oscar.js
module.exports = {

	sayMyName: function(clientDb) {
		let name = clientDb.getOscarName();
		console.log(`My name is ${name}`);
	},

	sayMyAge: function(clientDb) {
		let birthDate = clientDb.getOscarBirthDate()
                let age = Date.now() - new Date(birthDate) / 31556952000;
		console.log(`I am ${age} years old`);
	}

}
```

```javascript
// index.js
let clientDb = require('./clientDb');
let oscar = require('./oscar');

oscar.sayMyName(clientDb);
oscar.sayMyAge(clientDb);
```

Although this encapsulation is viable and does not encounter any functional limit, it suffers at this point of a loss of optimization since the injection of the database client is not mutualized and must be repeated each time an imported function is called. And this loss of optimization is amplified as soon as we implement private expressions that need to use external parameters as well. To have an illustration let's update the function `sayMyAge()` in the `oscar.js` module so that now the variable `age` is the result of a private function named `calculateAge()`.

```javascript
// oscar.js
function calculateAge(clientDb) {
  let birthDate = clientDb.getOscarBirthDate()
  return Date.now() - new Date(birthDate) / 31556952000;
}

module.exports = {

	sayMyName: function(clientDb) {
		let name = clientDb.getOscarName();
		console.log(`My name is ${name}`);
	},

	sayMyAge: function(clientDb) {
	 	let age = calculateAge(clientDb);
		console.log(`I am ${age} years old`);
	}

}
```

In this case, it is the `calculateAge()` function that requires access to the database and no longer the `sayMyAge()` function. Since the `calculateAge()` function is private I am now forced to pass the `clientDb` parameter to the `sayMyAge()` public function just in purpose of making it transit to the `calculateAge()` function. Regarding factoring and mutualization of components, this solution is far from the most optimal.

## Optimized encapsulation
To counter the limitations of standard encapsulation it is possible to implement this design pattern:

```javascript
// Design Pattern
module.exports = function(sharedParameter) {

    function privateFunction() {}

    function publicFunctionA() {}

    function publicFunctionB() {}

    return {

        publicFunctionA: publicFunctionA,
        publicFunctionB: publicFunctionB

    };

};
```

Here `module.exports` no longer returns an object but a global function. And it is within it that the expressions of our module are declared. The global function then returns an object in which are mapped the functions that we want to make public and export. In this way the mutualization is no longer an issue since parameters can be passed as argument to the global function and become accessible to every expression wether private or public.

If I apply this design pattern to my example, my two modules now look like this:

```javascript
// oscar.js
module.exports = function(clientDb) {

    function sayMyName() {
        let name = clientDb.getOscarName();
        console.log(`My name is ${name}`);
    }

    function calculateAge() {
        let birthDate = clientDb.getOscarBirthDate()
        return Date.now() - new Date(birthDate) / 31556952000;
    }

    function sayMyAge() {
        let age = calculateAge();
        console.log(`I am ${age} years old`);
    }

    return {

        sayMyName: sayMyName,
        sayMyAge: sayMyAge

    };

};
```

```javascript
// index.js
let clientDb = require('./clientDb');
let oscar = require('./oscar')(clientDb);

oscar.sayMyName();
oscar.sayMyAge();
```