{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. 

# BASICS

Why nodejs ?

  • Great for prototyping and agile development
  • Superfast and highly scalaple applications
  • Javascript everywhere
  • Large ecosystem of open-source libs

Typescriprt

Inroduction

TypeScript is a syntactic superset of JavaScript which adds static typing.

what is Typescript?

# Basics
# BASICS

Typescript

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
# 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.

# Diagram

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

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).

# LAYERS

Application Layers

Diagram representing our app structure

# 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;
}
# 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);
  }
}
# BENIFITS

Benefits Of Using Clean Architecture

  • Improved Maintainability

By separating the concerns of the various components and enforcing the dependency rule, it becomes much easier to understand and modify the code. 

# BENIFITS
  • Modularity and Separation of Concerns

Each layer has a specific purpose and is decoupled from the others. This modularity also makes it easier to reuse components in other projects.

# BENIFITS
  • Testability

This can help to catch errors early on in the development process and reduce the overall testing effort.

# BENIFITS
  • Loose Coupling of Components

This can be especially useful when it comes to upgrading or replacing technology.

Clean architecture with node.js and typescript 8

By Disha Kumra4

Clean architecture with node.js and typescript 8

  • 35