【问题标题】:NodeJs MVC architecture, is this macaroni code?NodeJs MVC 架构,这是通心粉代码吗?
【发布时间】:2019-09-26 05:43:29
【问题描述】:

我是一名初级 NodeJS 开发人员,目前在加密货币交易平台上工作。这是项目文件夹结构。

/app
    /controllers
        /user
        /order
        /settings
        ...
        index.js
    /middlewares
    /models
    /routes
        user.js
        order.js
    /services
        user.js
        order.js
        ...
    /views
    index.js
/config
/migrations
/public
/utils
server.js
.env
...

现在,一开始有点不知所措,但后来,我在应用程序中移动变得很舒服。
除了一个特定的文件! 控制器目录中的 index.js, 这是它的设置方式。

const { readdirSync, lstatSync } = require('fs');
const { validationSchema } = require('../../utils/validator');

module.exports = readdirSync('app/controllers')
  .filter(name => lstatSync(`app/controllers/${name}`).isDirectory())
  .reduce((controllersAccumulator, dir) => Object.assign(
    controllersAccumulator,
    {
      [`${dir}Controller`]: readdirSync(`app/controllers/${dir}`)
        .map(fileName => require(`./${dir}/${fileName}`))
        .reduce((accum, controllerFile) => Object.assign(
          accum,
          Object.keys(controllerFile).reduce(validationSchema.bind(null, dir, controllerFile), {}),
        ), {}),
    },
  ), {});

我不得不承认,这对我来说一直很害怕,只是看看而已! 所以简单来说就是,它将路由请求映射到控制器目录中的处理程序。

例如,如果一个用户想发出一个帖子请求注册一个新帐户:路由路径将是这样的:

// in the routes dir, user.js 
const { userCOntroller } = require('../controllers/');

router.post('/registration', userController.registration);


// in the /controllers, then /user, there will be a registration.js that includes:
const UserService = require('../../services/user');

exports.registration = async (req, res) => await UserService.create(req.body);


//then in the /services directory, user.js
...
class UserService {
  static create(body) { ... }
  ...
}

module.exports = UserService

所以我仍然无法理解的是,我们是如何在最后一个 sn-p 的用户路由中导入 userController 的?这就是/controllers 中的 index.js 文件所产生的内容!
当我问团队中的高级人员时,他们说,是的,它很难阅读,但它的代码更少。 好吧,好吧:\

那么,为了使这个文件更具可读性,可以做些什么不同的事情, 换句话说,有没有办法重构它? 提前致谢!

【问题讨论】:

  • 第一个代码 sn-p 确实很难阅读,因为没有任何 cmets
  • 为了部分回答您的问题,userController 来自[${dir}Controller],因为${dir} 被替换为“用户”、“订单”、“设置”等目录名称,因此生成的模块属性称为userControllerorderController 等。最后一个代码 sn-p 使用解构来提取其中一个属性,在本例中为userController。这就是名字的由来。实际控制器通过以下语句导入:require(./${dir}/${fileName})。然后通过validationSchema 函数过滤(减少)所需的文件。
  • 是的,我明白你的意思了,非常感谢,但是如果我想导入实际控制器并直接在用户路由文件中使用它,例如,而不是导入userControllers 然后调用userController.registeration我想直接在用户路由文件中导入registration函数,在这种情况下我应该在哪里传递req.body?再次感谢,
  • 另外,据我所知,userController.registration 只是一个文件,我的意思是它不是函数调用,那是怎么回事?我的意思是这个 req.body 在特定情况下是如何传递的??
  • 我假设文件registration.js 导出一个包含注册函数并位于user 目录中的对象。如果是这种情况,那么validationSchema 应该返回它的第二个参数 (controllerFile)。这就是registration 函数可以出现在userController 对象中的方式。为了直接使用这个函数,而不是解构整个用户控制器,你可以这样做:const { userController: {registration} } = require('../controllers/');。然后就可以直接调用registration函数了。

标签: javascript node.js oop model-view-controller controller


【解决方案1】:

解释第一个代码 sn-p 的最简单方法是重写它并添加适当的 cmets:

//Get all file names inside the controllers directory
const fileList = readdirSync('app/controllers');
//Get only directories from the file list
const onlyDirectories = fileList.filter(name => lstatSync(`app/controllers/${name}`).isDirectory());
//Create an empty object that will be exported from this module. Properties of this object will be assigned in the "for of" loop
const objectToExport = {};
//Import all files from the given directory and assign them to objectToExport
for (let directoryName of onlyDirectories){
    //First get all file names
    const controllerFiles = readdirSync(`app/controllers/${directoryName}`);
    //Then require each of these files
    const controllerModules = controllerFiles.map(fileName => require(`./${directoryName}/${fileName}`));
    //Assign the imported modules to `objectToExport`
    controllerModules.forEach((controllerModule) => {
        //Validate each module and assign it only if it passes validation
        if (validationSchema(directoryName, controllerModule)){
            objectToExport[`${directoryName}Controller`] = controllerModule;
        }
    });
}

module.exports = objectToExport;

下面我将解决您来自 cmets 的后续问题。 生成的对象现在如下所示:

{
    userController: {
        registration: [Function],
        ...
    },
    orderController: {
        ...
    },
    ...
}

注册函数最终出现在 userController 属性中,因为它是由 registration.js导出,然后由我的第一个代码 sn 中的 require 语句导入 -p。为了在其他文件中直接使用这个函数,你必须通过以下方式对其进行解构:

const { userController: {registration} } = require('../controllers/');
//now you can use it like this:
router.post('/registration', registration);

您的最后一个问题是关于req.body。如您所见,该函数有两个参数:reqres

exports.registration = async (req, res) => await UserService.create(req.body);

然后将其作为中间件传递给您的路由器。我假设您使用Express.js 作为您的框架。如果是这样,reqres 会被路由器自动传递给registration 函数。这就是UserService 在此函数中使用它们的方式。 body 属性由 Express 自动创建,如 here 所述。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-16
    • 2019-01-31
    • 1970-01-01
    • 2011-06-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多