Understanding the NodeJS Architecture
Christian Soto
Senior Staff Software Engineer @ Kunzig Consulting Inc | Full Stack Development
NodeJS is a single-threaded and asynchronous runtime environment used to run server-side applications with JavaScript as its primary language. NodeJS offers a robust architecture that provides seamless operation to run server-side code.
Features of NodeJS architecture
The Node.JS architecture comprises different features like
The NodeJS runs event-driven operations, where each task is triggered by the use of events which deploys a callback function. The NodeJS runtime is an asynchronous operation and doesn't block other tasks while enabling a smooth operation. Although, there are many operations that can run synchronously like the file reader API that provides the 'fs.readFileSync()' and can run synchronously.
The NodeJS runs an input/output model which involves reading and writing or manipulating files and returning data from and to the disk, HTTP requests, and also interacting with the database.
NodeJS also uses modules to run a lightweight environment which means that modules are imported at runtime to improve efficiency and reduce bloating.
Event-driven architecture
The NodeJS is a single-threaded environment which means that it runs one action at a time and runs its operations smoothly with the use of events and event emitters.
Events are actions that instruct the runtime what needs to be completed at a given time period with particular data. The event emitters are response objects that can be subscribed to and acted upon to perform certain operations.
Event emitters, like the WebSocket, create a connection and emit events based on certain predetermined events that accept a callback. The Node system adds events to a queue (batch) where events and processes are added that are resolved in an order and eventually removed from this queue.
This structure creates a robust system and enables it to perform one task at a time with an amazing speed.
I/O model
The input/output model is a concept used in Node runtime which accepts inputs that includes reading and writing files to disk, communicating with the servers, https request, and security protocols among other concepts which are supported by libuv. The I/O method is used to work on data inputs and outputs with the use of callbacks, referred to as handlers, which inject data to these callbacks.
The input/output provides lots of helpers like the filereader, socket, http request/response among several other methods. The Node documentation provides a brilliant implementation of the various methods available.
The I/O methods implement the non-blocking code
The blocking code run on the operation and are resolved before moving to another operation. This means that other operations must wait until one operation is completed before moving to the next operation. On the contrary, the non-blocking codes run asynchronously and accept callback functions to operate. To further understand this concept, we will use the filereader API.
The filereader provides both synchronous and asynchronous methods:
let fs=require('fs')
let readme=fs.readFileSync('readme.md'))
Its default asynchronous code will be:
const fs=require('fs')
fs.readFile('readme.md', (res,err)=>{
if(err) throw new Error
if(res){
console.log(res)
}
}))
The asynchronous operation always uses callbacks to resolve its operations.
Asynchronous architecture
The NodeJS runtime runs a single-threaded operation and to aid this operation, it runs an asynchronous non-blocking operation that allows it to carry on several operations at a time. This means while running one operation, it doesn't necessarily wait for this operation to resolve before moving to the next one but with the use of callbacks, it is able to get the result of an operation and return it.
The ECMAScript has really improved on these technologies with the implementation of various methods like async/await and promises among other methods. Asynchronous code is prioritized in NodeJS architecture as it helps to provide a seamless operation without blocking the whole code and also by reducing lag.
All methods in the runtime are run asynchronously to aid a smooth implementation that doesn’t seem obvious for the single-threaded operation.
Nodejs design patterns
There are various design patterns employed to make NodeJS a scalable environment, some of these patterns are beyond the scope of this article. However, NodeJS made some concepts immersing and easy to use, for example, the microservices, singleton.
Microservices
It is important to explain the monolithic applications in order to appreciate the concept of microservices. Monolithic applications are tightly structured together. These are structures that have all the logic in one base. The monolithic apps involve all the models, views, and controllers together.
PHP uses this approach a lot, where it contains the interface like the frontend app, its data fetching logic, and other parts all in one place. This has its advantages as it can be easy to understand the process used in creating the app but it brings in a bottleneck where restructuring code can be a really herculean task.
The microservice moves away from this concept as it delicately provides its different logic in bits where each software logic is separated from the other and is only applied wherever needed. This logic provides a structure that is easy to understand and follow.
A relatable instance is making an eCommerce application. The eCommerce application needs its stock price, quantity, deliverables, and restock options. These bits can be extracted as a logic where data is fetched separately and injected when it is needed.
The microservice patterns are loosely coupled and can be easily refactored when needed. The Angular framework also popularized this pattern with its use of services for supplying data which is then subscribed to in other parts that it is needed. These kinds of applications can easily be traversed and explained to someone new to the codebase.
Singleton
The concept of the singleton is about initializing a variable in an application only once. This was derived as a result of some variables that we want to be evaluated only once in the application flow and not reinitialize or change form. A good example is fetching an API. On declaration, we only want to declare the instance once and not change its value while fetching
领英推荐
let host='https://localhost:3000/getdogs'
let value;
fetch(host)
.then(res=>{
let value=res
})
.catch(err=>{
console.log(err)
});
Event loop
According to the NodeJS official?guide, the event loop is what allows Node.JS to perform non-blocking I/O operations — despite the fact that JavaScript is single-threaded — by offloading operations to the system kernel whenever possible.
Before moving forward, it is important to also explain?multithreading?and single threading. The multi-threading operation has the capacity to carry out several operations at the same time where it runs several operations in the background. On the other hand, the single-threading operation has the ability to run one operation at a time.
This process of single threading cannot be possible without the event loop. When an operation starts in the runtime, it employs the event loop, which helps to batch the operation of different tasks with methods like timers, polls, callbacks, and other methods. These methods are referred to as phase which uses the FIFO (First In First Out) technique to run operations smoothly.
The phase methods are explained below:
1. Timers?- This is a phase that uses a callback with timers as a threshold to decide when the operation should run, like the 'setInterval()' and 'setTimeout()'.
2. Pending callbacks?- These are callbacks that are left pending to be resumed at a later time.
3. Polls?- This is a queue for retrieving events.
4. Checks?- This is a method for allowing an operation to run immediately with the 'setImmediate()' method.
5. Close callbacks?- This is a method for closing open callbacks.
Concurrency
This concept refers to the ability of a running task to resume operations after the process finishes running another task. This is the process of running an operation's callback after working on another task.
A very good example of this concept is fetching data from the database and these kinds of operations can run asynchronously. In a situation where this operation needs 1s to run, another operation can also run through and then resume back to the database operation after 1s to save time.
Modules
NodeJS employs the use of core modules in its runtime environment. The Node runtime has global methods as well as modules. The global methods are the likes of 'setTimeout()', 'process.nextTick()' among others.
However, Node splits core concepts into modules that are not shipped by default like the http server module has to be imported or required before usage. This helps to improve the environment by shipping unnecessary modules and helps to keep the size at a minimal level.
Libraries
Node uses a couple of libraries to run. A few of those libraries include:
1. v8?- Node uses the v8 JavaScript engine under the hood to run its various tasks. The v8 engine is a JavaScript open-source engine that is maintained by Google and it is written in C++. Node uses this engine with the C++ API.
2. Libuv?- This is another core implementation used by Node to run its environment. It is a C library used to abstract non-blocking I/O operations. This library maintains its disk operation interface like the file system, DNS, child processes, signal handling, and streaming among other concepts.
3. Zlib?- This is used for compression and decompression purposes, it is an industry-standard library, popular for its use in gzip and libpng. It is also used to implement sync, async, and streaming compression and decompression interfaces.
4. OpenSSL?- This is used in tls and crypto modules to provide cryptographic functions to improve its security.
NPM
NPM is the short form for Node package manager. This is the standard package manager for Node which manages different packages on Node just like a composer in Laravel.
NPM is a registry that contains different dependencies and modules available to use in NodeJS. The NPM is widely accepted as it is also used in frontend libraries to frameworks, especially like Angular and Vue.
NPM keeps track of a project dependency with the use of 'package.json' which states the various dependencies and modules available in a project and its version.
Running 'npm install' automatically installs packages in the package.json not yet available in the file and also tries to update available files if needed.
NodeJS application architecture
This concept talks about all the core parts that make up a good application. To have a good application, there are three main things it comprises;
1. Security?- The safety and security of an application cannot be over-emphasized. It is important to know that clients are not at any risk of using your application. Node implements this concept with its use of SSL/TSL, secure https, web crypto API, and others.
2. Scalability?- Node applications are easily scalable and can also be easily refactored with the use of microservices and other concepts. Node is also continuously adding the latest features to improve its runtime and features.
3. Speed?- Due to its asynchronous nature and single threading, Node applications running time is fast by default and offers mind-blowing performances. These are all for the advantage of clients as clients are likely to revisit if they perceive a web application as fast.
Conclusion
NodeJS offers an impressive list of different features that makes working with it a breeze. Little wonder it is so popular now with its core implementations and use of different libraries and has also been largely adopted by different organizations, thereby, creating a plethora of?NodeJS developer job?opportunities. The NodeJS architecture displays a well-thought-out process aimed at improving the developer experience and seamless services.