Like any kind of apps, JavaScript apps also have to be written well.
Otherwise, we run into all kinds of issues later on.
In this article, we’ll look at some best practices we should follow when writing Node apps.
Microservices
Microservices architecture is a style that structure apps with a collection of services.
They’re very maintainable and testing.
Also, they’re loosely coupled so they can mostly work on their own.
They’re also independently deployable.
And each service is organized around business requirements.
This architecture lets us do continuous delivery and deployment of large systems.
We can also improve its technology stack in a piecemeal fashion.
If we’re creating a simple app, we probably don’t need microservices.
However, as our system grows, we don’t want to create one complex app that’s hard to maintain and scale.
It makes more sense to have them in individual modules.
Microservices are useful for replacing monolithic apps that are common until container solutions like Docker are commonplace.
We can divide our system into microservices with domains driven design.
Each domain is divided into bounded contexts which are mutually exclusive.
Each context correlates to a microservice.
Our goal is to create a cohesive and loosely coupled domain model.
We can identify the microservices we need by analyzing our domains, defining the bounded contexts, and define the entities and services.
Node.js Services
Now that we divided our system into microservices, each microservice can have their own pattern to organize their code.
Our app would be an MVC app which makes it easier to handle the model definition and interaction with the rest of the app.
We divide these entities into their own folders.
An MVC app has controllers to handle requests and responses.
It has no business logic.
Services have business logic. They’re passed to the controller.
Controllers can talk to many services.
Repository interacts with the models that are in the model folder.
These are used to query the database and won’t have business logic.
Models have the model definition and associations
Utilities have helper functions that are used by our app.
Tests have test cases. They test against controller methods to ensure max code coverage.
Cluster Modules
To maximize the use of CPU cores in our server, we should create clusters so that we can use more than one CPU core to run our app.
This is useful if we run our app outside Docker.
If we run our app in Docker, then we have one process per container so we don’t need to create clusters with apps in Docker.
Control Flow in Node.js
We should use promises to run async code in our app.
Any potentially long-running process should be written as promises.
This way, they won’t hold up the rest of our app from running.
Promises are native to ES6+, so we don’t have to add anything.
We can also convert some module methods to promises.
The fs module methods can be converted to promises.
Loops
We can run loops step by step in order.
We can run loops with a delay with for-await-of.
Things that aren’t dependent on each other can also be run in parallel.
For example Promise.all
can be used to run multiple unrelated promises in parallel.
Conclusion
Microservices architecture results in coherent and loosely-coupled services that are part of a larger system.
This makes maintenance easier and we can improve faster.