nodejs_design_patterns

Design Patterns in Node.js

Von am 17.01.2024

Design patterns play a pivotal role in developing robust and scalable software applications, and Node.js developers can benefit significantly from integrating these patterns into their projects. This blog article will discuss four design patterns in Node.js, providing practical examples and how they can be effectively applied.

What are design patterns?

Design patterns are standardized, reusable solutions to common software design challenges that developers encounter during the application development process. These patterns utilize best practices, providing an effective way to address recurring problems in a structured manner. Acting as templates for solving specific design issues, design patterns contribute to the creation of maintainable, scalable, and efficient software. They offer a shared vocabulary and a set of well-established solutions that developers can use to enhance the architecture and organization of their code.

Singleton Pattern

The Singleton pattern is a fundamental design strategy frequently employed in scenarios where a single instance of a class needs to be shared across various modules or components. By restricting instantiation to a sole object, this pattern ensures global access and promotes consistency in state management throughout the application. The example showcases a database connection class, highlighting how to implement this pattern and create a single instance shared across the application.

const databaseSingleton = (() => {
  let instance;

  const createInstance = () => {
    const database = new Database();
    return database;
  };

  return {
    getInstance: () => {
      if (!instance) {
        instance = createInstance();
      }
      return instance;
    },
  };
})();

class Database {
  constructor() {
    console.log("Creating a new database connection");
  }
}

const db1 = databaseSingleton.getInstance();
const db2 = databaseSingleton.getInstance();

console.log(db1 === db2); // Output: true

Observer Pattern

The Observer pattern supports one-to-many relationships between objects, commonly used in event-driven architectures. In Node.js, the built-in EventEmitter module seamlessly implements this pattern. It allows subjects to notify their dependents (observers) of any state changes, so they can react dynamically to changes in the subject, enabling a scalable architecture. The example demonstrates an EventObserver Class, acting as a subject, and the observerA and observerB functions, which are the observers. The subject keeps track of all its subscribers and when the broadcast method gets called, it sends data to each subscriber/observer.

class EventObserver {
  constructor() {
    this.observers = [];
  }

  subscribe(fn) {
    this.observers.push(fn);
  }

  unsubscribe(fn) {
    this.observers = this.observers.filter((subscriber) => subscriber !== fn);
  }

  broadcast(data) {
    this.observers.forEach((subscriber) => subscriber(data));
  }
}

const observer = new EventObserver();

const observerA = (data) => console.log(`Observer A: ${data}`);
const observerB = (data) => console.log(`Observer B: ${data}`);

observer.subscribe(observerA);
observer.subscribe(observerB);

observer.broadcast("Hello, Observers!");

Factory Pattern

The Factory pattern provides a flexible mechanism for creating objects without specifying their exact classes. It allows subclasses to determine the instantiated classes, enabling dynamic object creation at runtime. This proves valuable in scenarios where multiple implementations of an interface exist or when objects need to be created based on specific conditions or configurations. The example provides a vehicleFactory class, which uses a parameter called “type” to either return a new object of the Car or Truck class. Because the vehicleFactory is a central point of controlling the creation of objects, it simplifies adding or removing vehicles.

function vehicleFactory(type) {
  if (type === "car") {
    return new Car();
  } else if (type === "truck") {
    return new Truck();
  } else {
    throw new Error(`Unsupported vehicle type: ${type}`);
  }
}

class Car {
  drive() {
    console.log("Driving a car");
  }
}

class Truck {
  drive() {
    console.log("Driving a truck");
  }
}

const car = vehicleFactory("car");
car.drive(); // Output: Driving a car

const truck = vehicleFactory("truck");
truck.drive(); // Output: Driving a truck

Middleware Pattern

Prominently used in web frameworks like Express, the Middleware pattern orchestrates a chain of functions to handle incoming requests. This modular approach enhances the extensibility and maintainability of code, allowing developers to organize code efficiently and to process requests sequentially. The example demonstrates what a basic Middleware pattern can look like and when the according methods are called.

const express = require('express');
const app = express();

app.use((req, res, next) => {
  console.log('Middleware 1');
  next();
});

app.use((req, res, next) => {
  console.log('Middleware 2');
  next();
});

app.get('/', (req, res) => {
  res.send('Hello, world!');
});

app.listen(3000, () => {
  console.log('Server started on port 3000');
});

/*
When the / route is accessed, the output will be:

Middleware 1
Middleware 2

*/

Conclusion

In summary, design patterns serve as a set of best practices and solutions for recurring problems in software development. By understanding and implementing these patterns, developers can create well-organized, efficient, and maintainable code. These patterns act as reliable guides, streamlining the development process and contributing to the creation of robust and scalable Node.js applications. They offer valuable strategies for improving code structure, scalability, and maintainability. Whether you’re building a simple web server or a complex application, using these patterns will contribute to more organized and efficient code.


References

Kumar, S. (2023, August 15). Design patterns in Node.js. Medium. https://medium.com/@techsuneel99/design-patterns-in-node-js-31211904903e

Sharma, M. (2023, June 29). Design patterns in Node.js. Medium. https://medium.com/@manish90/design-patterns-in-node-js-9b0daab4fa5c

Doglio, F. (2023, July 24). A guide to node.js design patterns. LogRocket Blog. https://blog.logrocket.com/guide-node-js-design-patterns/

Rajchevski, K. (2023, October 5). A guide to node.js design patterns. Bugpilot Technical Guides. https://www.bugpilot.io/guides/en/a-guide-to-nodejs-design-patterns-6f1e

4 design patterns in node.js you should know. JavaScript Today Blog. (2023, February 12). https://blog.javascripttoday.com/blog/4-design-patterns-in-node/

Fundamental node.js design patterns. RisingStack Engineering. (2023, May 10). https://blog.risingstack.com/fundamental-node-js-design-patterns/

The 4 creational design patterns in node.js you should know. RSS. (n.d.). https://daily.dev/blog/the-4-creational-design-patterns-in-node-js-you-should-know

The comments are closed.