Внедрение зависимости в сервисы в компонентной структуре

#javascript #node.js #express #dependency-injection #integration-testing

#javascript #node.js #выразить #внедрение зависимостей #интеграция-тестирование

Вопрос:

Я следую модульной или компонентной структуре. Я нашел образец репозитория.

https://github.com/sujeet-agrahari/node-express-clean-architecture

Итак, существует основной component.module.js файлы, которые отвечают за подключение всех других элементов, таких как контроллер, маршрут и службы.

Для контроллера службы внедряются с использованием функций более высокого порядка. Теперь контроллер очень прост в тестировании, я могу легко заглушать или имитировать сервисы.

auth.module.js

 const router = require('express').Router();

const {
  makeExpressCallback,
  makeValidatorCallback,
} = require('../../middlewares');

// validator
const AuthValidator = require('./auth.validator');

// service
const { doRegister, doLogin, doCheckUserExist } = require('./auth.service');

const { BadRequestError } = require('../../utils/api-errors');

// controller
const controller = require('./auth.controller');

const register = controller.register({ BadRequestError, doCheckUserExist, doRegister });
const login = controller.login({ doCheckUserExist, doLogin });

const AuthController = { register, login };

// routes
const routes = require('./auth.routes')({
  router,
  AuthController,
  AuthValidator,
  makeValidatorCallback,
  makeExpressCallback,
});

module.exports = {
  AuthController,
  AuthService: {
    doCheckUserExist,
    doLogin,
    doRegister,
  },
  AuthRoutes: routes,
};
  

auth.controller.js

 const login = (doCheckUserExist, doLogin) => async (httpRequest) => {
  const { username, password } = httpRequest.body;
  const userData = await doCheckUserExist({ username });
  const loginData = {
    username,
    role: userData.role_id,
    passedPassword: password,
    actualPassword: userData.password,
  };
  const loginResult = await doLogin(loginData);
  return {
    statusCode: 200,
    body: {
      success: true,
      message: 'Successfully logged in!',
      data: loginResult,
    },
  };
};

const register = ({ BadRequestError, doCheckUserExist, doRegister }) => async (httpRequest) => {
  const { username, password } = httpRequest.body;
  try {
    await doCheckUserExist({ username });
  } catch (error) {
    // user doesn't exist
    const registerResult = await doRegister({ username, password });
    return {
      statusCode: 200,
      body: {
        success: true,
        message: 'Registered successfully!',
        data: registerResult,
      },
    };
  }
  throw new BadRequestError('User already exist!');
};

module.exports = { register, login };
  

С контроллером все в порядке, теперь проблема в сервисах. Я не могу найти какой-либо шаблон, чтобы сделать их тонкими и чистыми.

auth.services.js

 const {
  JWT_ACCESS_TOKEN_SECRET,
  ACCESS_TOKEN_EXPIRES_IN,
  SIGN_OPTION,
} = require('config');
const bcrypt = require('bcryptjs');
const { User } = require('../../db');
const { generateJWT } = require('./jwt.service');
const { NotFoundError, BadRequestError } = require('../../utils/api-errors');

const doRegister = async ({ username, password }) => {
  const user = await User.create({
    username,
    password,
    role_id: 1, // assign role id here
  });
    // generate access token
  const payload = {
    username,
    role: user.role_id,
  };
  const token = await generateJWT({
    secretKey: JWT_ACCESS_TOKEN_SECRET,
    payload,
    signOption: {
      ...SIGN_OPTION,
      expiresIn: ACCESS_TOKEN_EXPIRES_IN,
    },
  });
  return {
    access_token: token,
    ...payload,
  };
};

const doLogin = async ({
  username, userRole, passedPassword, actualPassword,
}) => {
  const isValidPass = bcrypt.compareSync(passedPassword, actualPassword);
  if (!isValidPass) throw new BadRequestError('Username or Password is invalid!');
  // generate access token
  const payload = {
    username,
    role: userRole,
  };
  const token = await generateJWT({
    secretKey: JWT_ACCESS_TOKEN_SECRET,
    payload,
    signOption: {
      ...SIGN_OPTION,
      expiresIn: ACCESS_TOKEN_EXPIRES_IN,
    },
  });
  return {
    access_token: token,
    ...payload,
  };
};
const doCheckUserExist = async ({ username }) => {
  const user = await User.findOne({
    where: {
      username,
    },
  });
  if (!user) throw new NotFoundError('User not found!');
  return user;
};

module.exports = { doRegister, doLogin, doCheckUserExist };
  

Многое происходит в сервисах, импорте моделей, импорте констант и других утилит.

Теперь сервисы становятся действительно сложными для тестирования.

Есть ли какой-либо способ или шаблон, по которому я могу отделить некоторую логику от сервисов и сделать их легче?

Я могу реализовать шаблон повторного размещения для методов БД, но я не знаю, как я могу реализовать с помощью sequelize?

Должен ли я использовать также функцию более высокого порядка для ввода всех утилит и констант в сервис, как я сделал для контроллера?