How can I avoid instantiating for each method my service and repository

Issue

I’m learning JavaScript with Express.js Framework. I’m trying to create a simple project for a restaurant (just for learning purposes), and I’m trying to create the CRUD of ingredients. I created the repository for all Prisma (ORM) requests, the service with business logic, and a controller for request/response handling. I wish to know if there is a better way of instantiating the service and repository on my controller. I’m doing that for each method. It worked, but I’m repeating this code block a lot.

This is IngredientRepository:

const prisma = require("../../prisma/client");

class IngredientRepository {
  async create({ name, price, image }) {
    return await prisma.ingredient.create({
      data: {
        name,
        price,
        image,
      },
    });
  }

  async findByName({ name }) {
    return await prisma.ingredient.findUnique({ where: { name } });
  }

  async findById({ id }) {
    return await prisma.ingredient.findUnique({
      where: { id },
    });
  }

  async updateImage({ id, image }) {
    return await prisma.ingredient.update({
      where: { id },
      data: {
        image,
      },
    });
  }
}

module.exports = IngredientRepository;

IngredientService:

const Error = require("../middlewares/Error");
const DiskStorage = require("../providers/DiskStorage");

class IngredientService {
  constructor(ingredientRepository, userRepository) {
    this.ingredientRepository = ingredientRepository;
    this.userRepository = userRepository;
  }

  async create({ name, price, image, loggedUser }) {
    const userIsAdmin = await this.userRepository.findById({ loggedUser });

    if (userIsAdmin.admin) {
      throw Error("", 401);
    }

    const ingredient = await this.ingredientRepository.findByName({
      name: name.toLowerCase(),
    });

    if (ingredient) {
      throw new Error("Ingredient already exists");
    }

    return await this.ingredientRepository.create({
      name: name.toLowerCase(),
      price,
      image,
    });
  }

  async updateImage({ id, image }) {
    const diskStorage = new DiskStorage();

    const ingredient = await this.ingredientRepository.findById({ id: id.id });

    if (!ingredient) {
      throw new Error("This ingredient doesn't exist", 401);
    }

    if (ingredient.image) {
      await diskStorage.deleteFile(ingredient.image);
    }

    const filename = await diskStorage.saveFile(image);

    ingredient.image = filename;

    const updatedIngredient = await this.ingredientRepository.updateImage({
      id: id.id,
      image: ingredient.image,
    });
    return updatedIngredient;
  }
}

module.exports = IngredientService;

IngredientController:

const IngredientRepository = require("../repositories/IngredientRepository");
const UserRepository = require("../repositories/UserRepository");
const IngredientService = require("../services/IngredientService");

class IngredientController {
  async create(request, response) {
    const ingredientRepository = new IngredientRepository();
    const userRepository = new UserRepository();
    const ingredientService = new IngredientService(
      ingredientRepository,
      userRepository
    );

    const loggedUser = request.user.id;

    const { name, price, image } = request.body;

    const ingredientCreated = await ingredientService.create({
      loggedUser,
      name,
      price,
      image,
    });
    return response.json(ingredientCreated);
  }

  async updateImage(request, response) {
    const ingredientRepository = new IngredientRepository();
    const userRepository = new UserRepository();
    const ingredientService = new IngredientService(
      ingredientRepository,
      userRepository
    );

    const loggedUser = request.user.id;
    const id = request.params;
    const image = request.file.filename;

    const ingredientWithImageUpdated = await ingredientService.updateImage({
      loggedUser,
      id,
      image,
    });

    return response.json(ingredientWithImageUpdated);
  }
}

module.exports = IngredientController;

Solution

It is possible to create your dependencies in constructor of IngredientController and then use that objects in methods.

Let me show an example:

class IngredientRepository {
  findByName() {
    return 'findByName: ' + Date.now();
  }
}

and:

class IngredientController {  
  constructor (){
    this.ingredientRepository = new IngredientRepository();
  }

  create(){
    // you can use your object here
    console.log(this.ingredientRepository.findByName())
  }

  update(){ 
    // you can use your object here
    console.log(this.ingredientRepository.findByName())  
  }
}

An example:

class IngredientRepository {
  findByName() {
    return 'findByName: ' + Date.now();
  }
}
class IngredientController {  
  constructor (){
    this.ingredientRepository = new IngredientRepository();
  }

  create(){
    console.log(this.ingredientRepository.findByName())
  }

  update(){   
    console.log(this.ingredientRepository.findByName())  
  }
}

const ingredientController = new IngredientController();
console.log(ingredientController.create())
console.log(ingredientController.update())

Answered By – StepUp

This Answer collected from stackoverflow, is licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply

(*) Required, Your email will not be published