How i fixed the circular dependency issue in my Node.js application
Tags: javascript
Published: 2021-06-30 20:58:01

I'm going introduce you in a problem that maybe you've been through and some point in your node.js career. 
Usually i split my business logic from anything else in my code (let's name it as a service), been my business layer responsible to trigger the resources that are required to make some action. Sometimes, one item in this business layer needs to use another one in the same layer.

__________________________________

**Example:**

![CustomerService requires UserService to create the login credentials](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dzpoftf1byf26d267pkb.png)_CustomerService requires UserService to create the login credentials_


![Both services depending each other](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4d90qx5oc40pbqg6tf6n.png) _and in another moment, UserService will call CustomerService to validate Customer profile_. 

___

## Failure scenario: 

![File structure](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a06164kcq1hfrz36bw60.png)


**|--/services/CustomerService.js**

```typescript
const UserService = require('./UserService')

class CustomerService{
    create() {
        UserService.create();
        console.log('Create Customer');
    }

    get() {
       return {
          name: 'test'
       }
    }
}

module.exports = new CustomerService;

```

**|--/services/UserService.js**
```javascript
const CustomerService = require('./CustomerService')
class UserService {
    create() {
        console.log('Create user');
    }

    get() {
        let customer = CustomerService.get();
        console.log({customer});
    }
}
module.exports = new UserService;
```

**|--/index.js**
```javascript
const CustomerService = require('./services/CustomerService');
const UserService = require('./services/UserService');

CustomerService.create();
UserService.get();
```


So, after implementing this code and run _node index.js_ in your terminal, you gonna get the following error: 

![Error due circular dependency](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a2l04h5jxoj6pn6mejwy.png)


You may be thinking: **WTF??? But this method does exist!!!!**

Yes, that was my reaction. You're getting this error due the circular dependency, which happens when you create a dependecy between two modules, it means that we're importing and using UserService inside CustomerService and vice-versa. 


## How can we solve this? One possible solution.

We're going to load our modules in a centralized file called index.js, so after this, we're going to import only the index.js file and specify the Objects that we need to use.


![Centralized module](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1du4x2dv66mil9h4jxva.png) 


### Hands on:

1 - Create a index.js file inside the services folder (Attention: it's our crucial code snippet):

**|--/services/index.js**
```javascript
const fs = require('fs');
const path = require('path');
const basename = path.basename(__filename);
const services = {};

// here we're going to read all files inside _services_ folder. 
fs
    .readdirSync(__dirname)
    .filter(file => {
        return (file.indexOf('.') !== 0) &&
                (file !== basename) &&
                (file.slice(-3) === '.js') &&
                (file.slice(-8) !== '.test.js') &&
                (file !== 'Service.js')
    }).map(file => {
        // we're are going to iterate over the files name array that we got, import them and build an object with it
        const service = require(path.join(__dirname,file));
        services[service.constructor.name] = service;
    })
    
    // this functionality inject all modules inside each service, this way, if you want to call some other service, you just call it through the _this.service.ServiceClassName_.
    Object.keys(services).forEach(serviceName => {
        if(services[serviceName].associate) {
            services[serviceName].associate(services);
        }
    })

module.exports = services;
```

2 - Let's create a Parent class that gonna be inherited by each service that we create.

**|--/services/Service.js**
```javascript
class Service {
    associate(services) {
        this.services = services;
    }
}

module.exports = Service;
```

3 - Let's rewrite ou code to check how it's gonna be.

**|--/services/CustomerService.js**
```javascript
const Service = require('./Service')
class CustomerService extends Service{
    create() {
        this.services.UserService.create();
        console.log('Create Customer');
    }
    get() {
       return {
          name: 'test'
       }
    }
}

module.exports = new CustomerService;
```

**|--/services/UserService.js**
```javascript
// now you only import the Service.js
const Service = require('./Service.js')
class UserService extends Service{
    create() {
        console.log('Create user');
    }

    get() {
        // now we call the service the we want this way
        let customer = this.services.CustomerService.get();
        console.log({customer});
    }
}
module.exports = new UserService;
```


4 - now., let's see how we're going to call the services outside the services folder.


```javascript
// now we only import the index.js (when you don't set the name of the file that you're intending to import, automatically it imports index.js)
const {CustomerService, UserService} = require('./services/')
// and we call this normally
CustomerService.create();
UserService.get();
```




__________


PROS: 

 - Now it's easier to load another service, without getting any circular dependecy error.
 - if you need to use a service inside other service, you just have to call the property _this.service.NameOfTheService_.

CONS: 

 - It's not trackeable anymore by your IDE or Code Editor, as the import handler is inside our code and it's not straight in the module that you want to use anymore.
 - A small loss of performance due the loading of all modules, although some of them are not used.


__________



If you think that there's something confusing, or impacting the understanding, or that i can improve, please i'm going to appreciate your feedback.

See you guys and thanks a lot