{clean Architecture}

with nodejs and typescript 
# BASICS

What is node.js?

 

Node.js is a server-side runtime environment that runs JavaScript code outside of web browsers. It allows developers to build scalable and high-performance network applications using JavaScript. Node.js uses an event-driven, non-blocking I/O model and has a vast ecosystem of modules and packages available through npm. It is well-suited for real-time applications and provides a unified language (JavaScript) for both the client and server components of an application.

Typescriprt

Inroduction

TypeScript is a syntactic superset of JavaScript which adds static typing.This basically means that TypeScript adds syntax on top of JavaScript, allowing developers to add types

what is Typescript?

# Basics
# BASICS

Typescriprt

JavaScript is a loosely typed language. It can be difficult to understand what types of data are being passed around in JavaScript.

In JavaScript, function parameters and variables don't have any information! So developers need to look at documentation, or guess based on the implementation.

TypeScript allows specifying the types of data being passed around within the code, and has the ability to report errors when the types don't match.

why to use typescript?

# Basics
# why and what

One of the paramount parts of writing code is what practices we follow while structuring the code. Evidently it’s easier to follow clean architecture principles in JAVA as it is purely object-oriented, but in the case of JavaScript, there are no clear instructions as such. The flexibility that JavaScript offers is both a boon and a bane in itself. In this PPT, we shall go through the ways of writing a monolithic node js application, inspired by clean architecture by uncle bob.

# Difference

Clean architecture 

VS

Clean Code

Clean Architecture is about boundaries and dependencies between "subsystems" and components of your software system. It helps structuring your code for changeability. By that following Clean Architecture will give you a "good" project structure

 

Clean Code is about the implementation of your "sub systems", components, classes and functions. It helps in structuring your code for readability and maintainability.

# Benifits

Benefits of Clean Architecture

  • Independence from any underlying framework
  • Freedom to plug and play with any database
  • Improve testability of code
  • Helps in maintaining larger code base easily.
# Diagram

If you see in this diagram we have concentric circles which represent layers of any application.

# HOW IT WORKS

The overriding rule that makes this architecture work is The Dependency Rule. This rule says that source code dependencies can only point inwards. Nothing in an inner circle can know anything at all about something in an outer circle.

Now the point of interrogation is the way of communication among layers as they are isolated from each other, and how does this contribute in making the application work. This can be achieved from something known as dependency injection.

 

Lets take an example

interface IEngine {
  start: () => void
  stop: () => void
  ... 
}
class PowerfulEngine implements IEngine {
  start() {}
  stop() {}
  // ...
}
class MorePowerfulEngine implements IEngine {
  start() {}
  stop() {}
  // ...
}
class CarBody {
  constructor(private engine: IEngine) {}
  // ...
}
const car1 = CarBody(new PowerfulEngine())
const car2 = CarBody(new MorePowerfulEngine())
# PRESENTING CODE
# STRUCTURING

Structuring our Node.JS application

Now we will be discussing how node applications can be structured using clean architecture principles, with the help of a Todo application (github repo link).

To make it easier let’s walk through every single layer from the module of the application to acknowledge the core concepts.

We will be using MongoDB, Node, TypeScript, express to create the application.

# LAYERS

Application Layers

Diagram representing our app structure

Our application is divided into 5 layers, here is the detailed explanation of each layer.

# LAYER 1

Layer 1 (Routes)

export default function TodoRoutes() {
  const router = express.Router();
  const todoController = dIContainer.get<ITodoController>(
    Types.Todo_CONTROLLER
  );

  router
    .route("/")
    .post(todoController.createNewTodo)
    .get(todoController.getAllTodo)
    .patch(todoController.updateTodo)
    .delete(todoController.deleteTodo);

  return router;
}

Routes is the inner most layer because it is very rare that you will change any route name so it will be the core of application.

Here if you see the only responsibility of this layer is to register routes and redirect incoming request to its respective controller.

# LAYER 2

Layer 2 (Controller)

Controller is responsible for.

  • Validating request body.
  • Sending response to client.
  • Handling incoming request.
# LAYER 2
@injectable()
export class TodoController implements
  ITodoController {
  @inject(Types.Todo_SERVICE)
  private todoService: ITodoService;

  public createNewTodo = async 
  (req: AuthenticatedRequest, res: Response) => {
    ...
  };

  public getAllTodo = async (
    req: AuthenticatedRequest,
    res: Response
  ): Promise<Response> => {
    ...
  };

  public updateTodo = async 
  (req: AuthenticatedRequest, res: Response) => {
   ...
  };
  public deleteTodo = async
  (req: AuthenticatedRequest, res: Response) => {
   ...
  };
}
# LAYER 3

Layer 3 (Service)

Service is our 3rd layer which is responsible of handling our business logic, it is dependant on TodoRepository and any class which satisfy the interface ITodoRepository can be injected in TodoService.

# LAYER 4

At number 4 we have repository which is responsible of any kind of outer world connection whether it calling any external service API or talking to database all such things are handled in this layer.

Layer 4 (Repository)

# LAYER 4
@injectable()
class TodoRepository implements ITodoRepository {
  @inject(Types.ToDo_TABLE)
  private todoTable: TodoAppDataSource<ITodoModel>;

  private getParameterObj = (
    content: string,
    userId: string
  ): Omit<ITodoModel, "_id"> => ({
    ...
  });

  createNewEntry = async (
    content: string,
    userId: string
  ): Promise<ITodoModel> => { ... };

  getAllUserTodo = async (userId: string) => { ... };

  deleteTodo = async (
    userId: string, 
    todoId: string
  ): Promise<ITodoModel> =>{ ... };

  updateTodoDetails = async (
    userId: string,
    todoId: string,
    todoDetails: Partial<ITodoModel>
  ): Promise<ITodoModel> => { ... };
}
# LAYER 5

Layer 5 (Database)

At number 5 we have kept our data layer, it encapsulates all our DB queries and expose it with the single interface, there is one base class for each type of DB, refer example shown below for mongoDB and postgresql.

# LAYER 5
export interface TodoAppDataSource<T> {
  create(data: T): Promise<T>;
  findOne(filter: Partial<T>, project?: Projection): Promise<T>;
  findMany(filter: Partial<T>, project?: Projection): Promise<T[]>;
  findOneAndUpdate(filter: Partial<T>, updates: Partial<T>): Promise<T>;
}

Common interface

# LAYER 5
export class MongoDataSource<T> implements TodoAppDataSource<T> {
  private table: mongoose.Model<T>;
  constructor(tableName: DB_TABLES) {
    this.table = ALL_TABLES[tableName] as mongoose.Model<T>;
  }
  public async findOne<T>(
    selectQuery: Partial<T>,
    project: Projection = {}
  ): Promise<T> {
    return this.table.findOne(selectQuery as FilterQuery<T>, project);
  }
  public async create<T>(data: T): Promise<T> {
    const newRecord = new this.table(data);
    return newRecord.save() as Promise<T>;
  }
  public async findOneAndUpdate<T>(
    selectQuery: Partial<T>,
    updates: Partial<T>
  ): Promise<T> {
    return this.table.findOneAndUpdate(selectQuery as FilterQuery<T>, updates, {
      new: true,
    });
  }
  public findMany = async (
    filter: Partial<T>,
    project?: Projection
  ): Promise<Array<T>> => {
    const result = await this.table.find(filter as FilterQuery<T>, project);
    return result as unknown as Promise<Array<T>>;
  };
}
export class UserTable extends MongoDataSource<IUserModel> {
  constructor() {
    super(DB_TABLES.USER);
  }
}
# CONCLUSION

There is no hard and fast rule of setting up or efficiently structuring the code repository. Hence exploring, investigating and experimenting is the best way suggested.

The concise way of writing the code and structuring is the sole reason of this presentaion

Conclusion

Clean architecture with Node.js

By Disha Kumra4

Clean architecture with Node.js

  • 9