DEV Community

Vishnu C Prasad
Vishnu C Prasad

Posted on • Updated on • Originally published at vishnucprasad.Medium

Clean Architecture with Inversify in Node.js with TypeScript: A Code-Driven Guide

Clean Architecture in Node.js

Introduction

As Node.js applications grow in complexity, maintaining a well-organized and scalable codebase becomes crucial. Clean Architecture is a software design approach that promotes separation of concerns and decoupling between different layers of an application. In this article, we’ll explore how to implement Clean Architecture in Node.js using Inversify, a powerful dependency injection container, along with inversify-express-utils to build a web API. By the end, you’ll have a solid foundation for developing maintainable and testable applications in Node.js.

Understanding Clean Architecture

The key idea of Clean Architecture is to divide an application into multiple layers, each with a specific responsibility and minimal dependencies on other layers. The core layers of Clean Architecture are:

  1. Entities: Represents the business domain models, encapsulating the application’s business rules and state.

  2. Use Cases: Contains application-specific business logic. It represents the application’s primary use cases or interactions.

  3. Interface Adapters: This layer converts data from the use cases into a format suitable for external interfaces such as web APIs or databases.

  4. Frameworks & Drivers: Contains the implementations of the external interfaces and tools used in the application, like web frameworks, databases, etc.

Using Inversify in Node.js

Inversify is a popular library for implementing dependency injection (DI) in Node.js applications. It allows us to manage dependencies and achieve loose coupling between components. Before getting started, make sure you have Node.js and npm installed.

Step 1: Setting Up the Project

Create a new Node.js project and initialize npm:

mkdir clean-architecture-demo
cd clean-architecture-demo
npm init -y
Enter fullscreen mode Exit fullscreen mode

Step 2: Installing Dependencies

Install the necessary dependencies — Inversify, inversify-express-utils, and Express:

npm install inversify inversify-express-utils express reflect-metadata
npm install @types/express --save-dev
Enter fullscreen mode Exit fullscreen mode

Step 3: Configuring TypeScript

We’ll use TypeScript for this example. Create a tsconfig.json file at the root of the project:

{
  "compilerOptions": {
    "target": "es6",
    "module": "commonjs",
    "outDir": "dist",
    "strict": true,
    "esModuleInterop": true,
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true
  }
}
Enter fullscreen mode Exit fullscreen mode

Step 4: Implementing Clean Architecture

Let’s start implementing the Clean Architecture layers:

  1. Entities:

Create a folder named entities and add a file User.ts:

// entities/User.ts
export class User {
  constructor(public id: number, public name: string, public email: string) {}
}
Enter fullscreen mode Exit fullscreen mode
  1. Use Cases:

Create a folder named useCases and add a file UserUseCase.ts:

// useCases/UserUseCase.ts
import { injectable } from "inversify";
import { User } from "../entities/User";

@injectable()
export class UserUseCase {
  getUsers(): User[] {
    // Simulated data for demonstration purposes
    return [
      new User(1, "John Doe", "john@example.com"),
      new User(2, "Jane Smith", "jane@example.com"),
    ];
  }
}
Enter fullscreen mode Exit fullscreen mode
  1. Interface Adapters:

Create a folder named controllers and add a file UserController.ts:

// controllers/UserController.ts
import { Request, Response } from "express";
import { controller, httpGet } from "inversify-express-utils";
import { UserUseCase } from "../useCases/UserUseCase";
import { inject } from "inversify";

@controller("/users")
export class UserController {
  constructor(@inject(UserUseCase) private userUseCase: UserUseCase) {}

  @httpGet("/")
  async getUsers(_: Request, res: Response) {
    const users = this.userUseCase.getUsers();
    return res.json(users);
  }
}
Enter fullscreen mode Exit fullscreen mode

Step 5: Setting Up the Inversify Container

Create a file named inversify.config.ts:

// inversify.config.ts
import { Container } from "inversify";
import { UserController } from "./controllers/UserController";
import { UserUseCase } from "./useCases/UserUseCase";

const container = new Container();
container.bind<UserController>(UserController).toSelf();
container.bind<UserUseCase>(UserUseCase).toSelf();

export default container;
Enter fullscreen mode Exit fullscreen mode

Step 6: Creating the Express Server

Create a file named app.ts:

// app.ts
import "reflect-metadata";
import { InversifyExpressServer } from "inversify-express-utils";
import container from "./inversify.config";

const server = new InversifyExpressServer(container);

server.build().listen(3000, () => {
  console.log("Server started on http://localhost:3000");
});
Enter fullscreen mode Exit fullscreen mode

Conclusion

In this article, we explored how to implement Clean Architecture in Node.js using Inversify and inversify-express-utils. By adhering to the principles of Clean Architecture, we achieve a scalable and maintainable codebase that is easy to test and extend. Using Inversify as the dependency injection container allows us to manage dependencies effectively and promotes loose coupling between components.

Remember that this is a simplified example, and real-world applications may have more complex use cases and domain entities. However, the fundamental principles of Clean Architecture and Inversify remain the same, providing a solid foundation for building robust Node.js applications.

Here’s a link to a sample project repository for a more concrete demonstration of the concepts discussed in this article: Sample Clean Architecture with Inversify Repository.

Top comments (2)

Collapse
 
chema profile image
José María CL

Nice article!

Little feedback: when you write code snnipets you can specify the language to add some syntax colors

for example:

´´´js
 /// a piece of code
´´´
Enter fullscreen mode Exit fullscreen mode

p. d. I'm using ´ instead of ` to be able to write the character inside a code snnipet

Collapse
 
vishnucprasad profile image
Vishnu C Prasad

Thank you for the feedback! I appreciate your suggestion about adding syntax highlighting to code snippets. I'll definitely keep that in mind to enhance the readability of code examples in the future.