Building applications that are loosely coupled has many benefits: improved testability, less complex code, reusable components, and scalability. Loose coupling also makes it easier to write code that is additive rather than transformative, which reduces the likelihood of creating bugs when changing old code.
That sounds great, but what exactly does “loosely coupled code” mean? Well, components created in loosely coupled code know as little about each other as is possible.
In this post, I will be introducing three strategies that can help Node developers who wish to loosen their code. First, we’ll cover microservices with Moleculer. Then, we’ll through Inversion of Control with InversifyJS. Finally, we’ll discuss N-Tier Architecture and why it’s helpful.
Let’s get started by diving into microservices!
What are Microservices, and Why Should I Care?
As we briefly discussed above, Moleculer allows for loosely coupled code by providing a microservices architecture. Microservices architecture is a very simple concept. Similar to the practice of using Object-Oriented Programming (OOP) to break apart large applications, the idea of microservices is to split monolithic architecture into smaller, more manageable pieces.
As with OOP, not only do you create a system that is easier to understand, but you also end up creating multiple services that can be used in more than one place. For instance, in a microservices architecture, you could create a user service that can be used by multiple applications while still keep details about each hidden from the others.
In my next post, I will be creating a simple backend for a blogging application using Moleculer. For now, let’s use the concept of it as an example.
Without Moleculer, we would create an Express application that has all API endpoints and code bundled together, able to interact with each other directly, and sharing the same database. With Moleculer, on the other hand, using its microservices architecture, we will instead create three discrete services each with their own code and their own databases with only data that is necessary for that specific service.
The collection of processes running the service/services are called a node
. Each node
must have what is known as a Service Broker
, which controls the communication between nodes
, whether local or remote.
The Service Broker
will use what are called transporters
in Moleculer to send messages back and forth. The recommended option is NATS, but there are many other options such as TCP, UDP, Redis, etc.
Inversion of Control, Dependency Injection, and Coding to Interfaces
Inversion of Control helps with loose coupling by removing the instantiation of dependencies from inside the class that depends on them to somewhere external to the class. This is important for two reasons.
1. It ensures that the dependent class knows as little about its dependency as possible
2. It makes it easier to test as the dependency is not black-boxed within the class that is being tested.
Dependency Injection is a form of IoC where we inject interfaces rather than concrete implementations. This means that the class will be looking for a “contract.” A contract is a set of functions that need to be supplied without knowing the concrete implementations. This allows the class to know even less about its dependency, and it makes it much easier to change a function’s concrete implementation at any time.
Let’s say there is an instance where a client wishes to switch from using MongoDB to SQL. Since we used dependency injection, all we need to do is create new classes that implement the interface we use for data access. We’ll have the new classes access SQL rather than Mongo, and then swap them out in the configuration. All in all, this allows us to accomplish all this while only needing to make changes to a single tier.
While this will require time, it will require much less time than it would in a tightly coupled application. In a tightly coupled app, each component would have had references to the SQL driver rather than just the components that need to know that information. Loose coupling allows us to make changes much quicker and without so much of a hassle.
Dependency injection can be achieved without the help of a library, but a library makes the configuration much easier to manage. This is where InversifyJS comes in. It provides an easy-to-use framework that we can use to configure and manage our dependencies.
N-Tier Architecture
Of all the strategies for loosening code we’ve discussed today, perhaps the easiest to understand and implement is N-Tier architecture. N-Tier architecture is simply the idea of breaking up the application into separate “layers” based on what functions are doing what.
Typically, when it comes to the back-end of an application, there is a layer for routing, a layer for business logic, a layer for data access, and possibly another layer for data management. We always reference the business layer instead of directly referencing the data access layer, just so that if for some reason we ever need to change the data access layer, we won’t need to change any business logic.
In N-tier architecture, a class on one layer is allowed to communicate with other classes in its layer and classes one layer below. This makes the flow of information in the application easier to follow and bugs easier to trace.
Why Go Through All This Trouble?
So why should we make the effort to do all this? Isn’t just using Express enough?
There are instances when installing Express and jumping right in is enough, especially when applications are very compact and easy to understand. However, in large applications with components that could be used in different ways by different users, having a scalable and loosely coupled architecture is very handy.
Microservices allow us to reuse different parts of our architecture for separate use cases without having to refactor our code every time there is a new service that would like to make use of it.
Inversion of control allows us to easily extend our code rather than mutating it for large changes.
N-tier architecture gives us the ability to separate all of our code into sections that are based on what actions they need to perform. For any other action, they need to reach out to another layer to perform that action, which not only gives us cleaner, more organized code but reduces the complexity of the flow of information in our applications.
Creating loosely coupled code will save you time, effort, and frustration in the long run. Designing an application that can flex and change easily is in all of our best interests!
What’s Next
For those of you that have made it this far, thank you!
In the next part, we can finally put all the details behind us and get our hands dirty. I’ll walk through a tangible example of how to make use of Moleculer, InversifyJS, and N-tier architecture to build a simple blog site.
Stay tuned by subscribing to the Keyhole Dev Blog!