Node Architecture
Node has an architecture that seperates it from other server-side platforms. We won't go into full details but we should look at some important architectural decisions. Before we move let's explain how web applications work:
Basic Components of Web Applications
In order to build succesful web or mobile applications, you need to understand how it works. Our focus in this course is web applications. Web applications are programs which runs on a server and gets rendered by a client's browser using the internet to access all the applications' resources. Web applications are broken into three parts which are:
- Client
- Server
- Databases
1. Client
This is the part of applications that users interact with. The users interact with the frontend of an application(web app in case of mobile applications) and this frontend that users interacts with can be developed with a variety of technologies such as HTML, CSS, JavaScript and some of its frameworks(React, Vue, Angular)etc.
2. Server
Yeah, this is the part that we are interested in. The server is responsible for handling client requests, perfoming tasks in background, and sending responses to the client(as described above). It serves as a connection(middleware) between the frontend and data stored and enable different operations on this data by a client. The most popular server-side technologies includes PHP, Java and our beloved Node.js and they are used to develop and maintain web servers.
3. Databases
Welcome to the brain of applications in terms of storage. The database stores the data for a web application. The data can be created, updated, read and deleted as requested by the client. There are also lots of technologies or popular databases used to store data for web appliactions and they include: MongoDB and MySQL etc.
Components of NodeJs Architecture
It's important to note that Nodejs is a single-threaded and asynchronous runtime environment and it's built on the Google Chrome's Javascript V8 engine. The core part of Nodejs is also written in C and C++. Let's break down some of the key features of its architecture:
- Single threaded loop(Event-driven architecture)
- V8 Engine
- Libuv
- Node.js API
- Zlib
Single Threaded Event Loop
Node.js uses the "Single threaded event loop" architecture to take care of multiple concurrent clients and it also manages all incoming connections and events. Nodejs runs one action at a time and this is managed using events and events emitters. Events tells the runtime things that needs to be completed at a given period of time with particular data. Event emitters are response objects that raises events that can be acted upon. Websocket, an example of event emitter can create a connection and emit events based on certain predetermined events with callbacks.
NodeJs generally has an event queue where events and prcoesses are added and resolved. Events acted upon are removed from this queue. NodeJs has a non-blocking I/O(Input/output) or Asynchronous architecture. When a request involves I/O operations(e.g reading a file or even making network request), NodeJs does not block this main single thread. Instead, it delegates the task to the operating system and registers a callback function to be executed when the operation completes. This allows NodeJs to handle a very large number of concurrent connections without overhead of creating new threads for each request, making it highly efficient for I/O bound applications.
ECMAScript improved the NodeJs technology by improving the use of various asynchronous methods such as Async/Await and Promises as they help seamless operations and reduces operation lag. All methods in the runtime are run asynchronously when using the single thread.
The whole point is that while running one operation using the single thred, NodeJs does not necessarily wait for the operation to resolve before moving to the next one due to the use of callbacks that can get the result of an operation and returns it.
V8 Engine
It is also important to point out that Node is built on top of the powerful Google Chrome's powerful engine and is responsible for executing Javascript codes. Before now, Javascript could only be executed on a browser. NodeJs made it possible for Javasript to be run server-side using the V8 Engine. The high performance of this engine contributes to Node speed.
Libuv
This C library helps Node to manage and handle asynchronous I/O operations, networking, file systems, and timers. It also manages a pool of threads for CPU-intensive tasks to prevent blocking the event loop. It basically runs the entire Node environment by maintaining disk operation interfaces like the file system, DNS, signal handling, child processing and streaming etc amongst other concepts.
Node.js API
NodeJs provides an abstraction layer by providing a set of APIs for interacting with the operating system, network, file system, and other system resources. This simplifies common tasks and allows developers to build applications efficiently.
Zlib
This is a very popular industry-standard library used for compression and decompression. It is also used in other popular libraries like gzip and libpng. It is majorly used in Node for sync, async, streaming, compression and decompression interfaces in Node.
Features Of Node Js Architecture
A summary and breakdown of Node architecture can be explained with three sentences:
- Nodejs has an event-driven architecture through its single loop
- Nodejs also has an input/model that takes input and gives ouput as callbacks. Nodejs accepts inputs supported by Libuv like reading and writing files to disk, communicating with the servers, https request, and security protocols.
- Nodejs is asynchronous
Workflow for NodeJs Architecture
How does Node works under the Hood? That is an important part of the Nodejs architecture we should look at. Web servers developed with Node follow this workflow:
- Requests: Requests are sent by the client to the webserver to interact with the web application. Requests can be blocking(synchronous) or non-blocking(asynchronous). Example of requests include creating data, updating data, querying data or deletion of data.(basically CRUD operations).
- Event Loop:Node.js receives the incoming request and adds them to the event queue. The Event loop constantly checks for new events or pending callbacks. Requests are processed one-by-one in the event loop. Node also checks if the request are really complex and may need additional resources. Event Loop processes simple requests (non-blocking operations), such as I/O Polling, and returns the responses to the corresponding clients.
- I/O Operations: When an I/O operation is initiated, it's handed off to Libuv.
- Event Loop Continues: The event loop proceeds to process other events while waiting for the I/O operation to finish.
- Callback Execution: Once the I/O operation is complete, Libuv notifies the event loop, and the registered callback is executed.
A single thread from the Thread Pool is assigned to a single complex request. This thread is responsible for completing a particular blocking request by accessing the external resources, such as compute, database, file system, etc.Once, the task is carried out completely, the response is sent to the Event Loop that in turn sends that response back to the Client.
Architectural design patterns in Node.js
This architectural design part escalated quickly than I can imagine. It is a broad topic and you can totally skip it for now if you want to. I promise it's going to bore you up except you are like me 😂😂😂😂
There are certain design patterns that can be followed when developing servers with Node. This patterns enable Nodejs application scale. Choosing the right architecture depends on factors like application size and complexity, scalability requirements, development team size and expertise, time-to-market constraints etc. Some of these design patterns are:
Monolithic Architecture
This is a single, self-contained application where all components(frontend, backend, database) are tightly coupled and deployed together. They have all the logic in one base as the models, views and controllers are together. This monlothic approach is used by PHP a lot where it contains the interface like the frontend, 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.Frameworks like Express.js provide a robust foundation for building monolithic applications, offering features like routing, middleware, and templating. Also, while monolithic architectures can be challenging to scale horizontally, Node.js's non-blocking I/O model can help improve performance and scalability, especially for I/O-bound tasks.
Microservices Architecture
Unlike the monolithic architecture, microservices architecture involves collection of small, independent services that communicate with each other via APIs. Each service focuses on a specific business capability and can be developed, deployed, and scaled independently. Node.js is well-suited for building small, independent microservices due to its lightweight nature aand asynchronous model. Each microservice can focus on a specific business capability.
It is also common knowledge that Node.js is a popular choice for developing RESTful APIs that enable communication between microservices. Frameworks like Express.js and Fastify provide tools for creating well-structured and efficient APIs. Also, technologies like Kafka and RabbitMQ allows Node to be used for implementing event-driven commnuication patterns between microservices.
The notable advantages of microservice architecture are:
- Scalability: Microservices can scale independently allowing for better resource utilization and flexibility.
- Technology Diversity: Different microservices can use different technologies and programming languages, providing flexibility.
- Structure: The way microservices are structured allows for fast updates and addition of features especially ones which involves restructuring.
Serverless Architecture
This is a cloud computing model where you run code without managing servers. Functions are triggered by events and executed on demand. These are ways Node can fit into the core tenets of serverless architectures:
- Function-as-a-Service: Node.js is a popular language for writing serverless functions on platforms like AWS Lambda, Azure Functions, and Google Cloud Functions.
- Event-Driven Triggers: Node.js functions can be triggered by various events, such as HTTP requests, database changes, or scheduled events.
- Scalability: Serverless functions scale automatically, eliminating the need for manual provisioning and management.
- Cost-Efficiency: Serverless architectures can be cost-effective, as you only pay for the resources consumed.
It is important to note that serverless architecture ensures reduced operational overhead as it eliminates the need to manage servers, reducing operational costs.
Event-Driven Architecture
This is a pattern where events trigger actions, decoupling components and enabling real-time updates. Here are some ways in which node can fit into the event-driven architecture:
- Asynchronous Communication: Node.js's event-driven nature aligns well with event-driven architectures.
- Real-time Applications: Node.js is ideal for building real-time applications like chat, gaming, and IoT solutions.
- Messaging Systems: Node.js can be used to integrate with messaging systems like Kafka or RabbitMQ for asynchronous communication.
- Event-Based Triggers: Node.js can be used to create event-driven systems where events trigger specific actions or workflows.
CQRS (Command Query Responsibility Segregation)
This involves separating commands (which modify the system's state) from queries (which retrieve data). Nodejs does a great job for CQRS in the following ways:
- Separate Data Stores: Node.js can be used to implement CQRS with separate data stores for commands and queries, improving performance and scalability.
- Asynchronous Processing: CQRS often involves asynchronous processing, which Node.js is well-suited for.
- Event Sourcing: CQRS can be combined with event sourcing to provide a more robust and scalable architecture.
Event Sourcing
This is a pattern where all changes to the system's state are recorded as events. Node.js in event sourcing includes:
- Event Processing: Node.js can be used to process events and rebuild the system's state from scratch.
- Event Storage: Node.js can be used to store events in databases like MongoDB or PostgreSQL.
- Event Replay: Node.js can be used to replay events for testing, debugging, or recovery purposes.
Domain-Driven Design (DDD)
This is a software development approach that focuses on modeling the business domain and aligning software with business processes. Node.js can fit into Domain-driven designs in the following ways:
- Bounded Contexts: Node.js can be used to implement bounded contexts, which are self-contained units within a larger application.
- Aggregate Roots: Node.js can be used to implement aggregate roots, which are the root of a group of related entities.
- Value Objects: Node.js can be used to implement value objects, which are immutable objects that represent a value.
Final notes to developers
A good application should be:
- Secure
- Scalable
- Fast
The good thing is that Node gives developers all these capabilities as they are important considerations for the development of Node application overall. Try to measure your applications using those yardsticks irrespecitve of how your Node application is structured.