Clean Architecture with Inversify in Node.js with TypeScript: A Code-Driven Guide
Vishnu C Prasad
Posted on August 11, 2023
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:
Entities: Represents the business domain models, encapsulating the application’s business rules and state.
Use Cases: Contains application-specific business logic. It represents the application’s primary use cases or interactions.
Interface Adapters: This layer converts data from the use cases into a format suitable for external interfaces such as web APIs or databases.
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
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
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
}
}
Step 4: Implementing Clean Architecture
Let’s start implementing the Clean Architecture layers:
- 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) {}
}
- 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"),
];
}
}
- 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);
}
}
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;
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");
});
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.
Posted on August 11, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
August 11, 2023