koa 分离路由_路由中的关注点分离

koa 分离路由

Trying to use MVC methodology, b̶u̶t̶ ̶n̶o̶t̶ ̶m̶u̶c̶h̶, let’s talk about each letter in the acronym. Beyond that, I gonna show you my vision why should be more letters in there, even to simplest applications.

尝试使用MVC方法论,让我们讨论首字母缩写词中的每个字母。 除此之外,我将向您展示我的愿景,为什么即使在最简单的应用程序中也应该包含更多字母。

— We gonna shuffle the presentation of MVC acronym starting from…

—我们将从…开始改组MVC首字母缩略词的表示形式

CThe class Controller should, just to write or read from the database, b̶u̶t̶ ̶t̶o̶ ̶m̶e̶, business rules should not be in there because of the separation of concerns.

C类Controller应该只是为了从数据库中写入数据或从数据库中读取数据,所以由于关注点的分离,业务规则不应放在其中。

VUsing REST-full API, is not interesting require whole pages from backend, so the Views maybe don’t have much meaning, but putting the static pages in there, could change that. Everything is required from some frontend, so It’s clear why we should have the letter V in MVC.

V使用REST-full API并不是一件有趣的事情,它需要后端的整个页面,因此View可能没有太大的意义,但是将静态页面放在那里可能会改变这一点。 从前端开始,一切都是必需的,因此很清楚为什么在MVC中我们应该使用字母V。

MThe M of Model never makes me any sense using NodeJS until I started to use SQL in backend. At least until I found a pretty useful thing to put in there (even using NoSQL), the validations! I think Celebrate is awesome tool for validation in NodeJS, using Joi and all that stuff. So it’s usual to think in one validation per route, but we gonna mesh things out and introduce a n̵e̵w̵ way to do this, we gonna use two validations per route.

M在开始在后端使用SQL之前,使用NodeJS对Model M毫无意义。 至少直到我发现其中有一个非常有用的东西(甚至使用NoSQL),然后进行验证 ! 我认为Celebrate是使用Joi和其他所有工具在NodeJS中进行验证的强大工具。 因此,通常考虑对每条路线进行一次验证,但是我们将把所有内容划分开来,并采用一种新的方式来做到这一点,我们将对每条路线使用两次验证

RuleA business rule is a method that does anything with the data to transform that in information, m̶y̶ ̶v̶i̶s̶i̶o̶n̶. Talking about REST-ful APIs, to me is pretty clear that most of the transformation, summarizing, t̵r̵a̵n̵s̵f̵e̵r̵ ̵f̵u̵n̵c̵t̵i̵o̵n̵, happens in backend. Sometimes for abstraction, for optimization (the backend could be more capable to realize some tasks faster), for language problem (part of backend could be done in another language, not javascript), for security (environment secrets could be robbed and compromise the business) o̶r̶ ̶j̶u̶s̶t̶ ̶b̶e̶c̶a̶u̶s̶e̶ ̶t̶h̶e̶ ̶t̶e̶c̶h̶n̶i̶c̶a̶l̶ ̶l̶e̶a̶d̶ ̶i̶s̶ ̶a̶n̶ ̶a̶w̶e̶s̶o̶m̶e̶ ̶w̶i̶z̶a̶r̶d̶ ̶a̶n̶d̶ ̶w̶a̶n̶t̶ ̶t̶h̶a̶t̶ ̶i̶n̶ ̶t̶h̶a̶t̶ ̶w̶a̶y̶.

规则业务规则是一种对数据进行任何操作以将其转换为信息的方法,即信息。 对我来说,谈到REST-ful API,很明显,大多数转换(总结)都是在后端进行的。 有时为了抽象,为了优化(后端可能更有能力更快地完成某些任务),为了语言问题(后端的一部分可以用另一种语言而不是JavaScript来完成),为了安全(可能窃取环境秘密并损害业务) )或者仅仅因为技术领导是一个真棒向导,并希望出现这种情况那样。

You should agree with me, about the capability of that rule be immense, with much complexity and even encapsulating in one class, if you call them in the controller, gonna pass to the controller a responsibility that is not the main focus of it. In case of a thrown exception, the first target will be in the controller.

您应该同意我的看法,即该规则的功能巨大,非常复杂,甚至封装在一个类中,如果您在控制器中调用它们,则将责任转移给控制器,这不是它的主要重点。 如果抛出异常,则第一个目标将在控制器中。

So, I purpose to create a new letter and a new directory to put all business rules in there, the name of the directory could be a rule ,and the letter in the new methodology should be R.

因此,我打算创建一个新字母和一个新目录以在其中放置所有业务规则,该目录的名称可以是一个规则,新方法中的字母应为R。

const moment = require('moment-timezone');


module.exports = class SensorRule {
  static date(req, res, next) {
    try {
      const { timestamp } = req.body;


      const dateFull = new Date(timestamp).getTime();
      const date = moment(dateFull).tz('UTC').format('YYYY/MM/DD HH:mm:ss.SSSSSSSSS');


      req.body.date = date;
      next();
    } catch (error) {
      res.send(error);
    }
  }
};

DTOIn nowadays validation files are called for DTO acronym of (Data Transfer Object), so we gonna use D for name the validations in the new methodology.

DTO如今,验证文件被称为DTO( 数据传输对象 )的缩写,因此我们将使用D来命名新方法中的验证。

const { Segments, Joi } = require('celebrate');


module.exports = class SensorDTO {
  static getSensorTimestamps() {
    return {
      [Segments.QUERY]: Joi.object().keys({
        name: Joi.string().required(),
      }),
    };
  }


  static getSensor() {
    return {
      [Segments.QUERY]: Joi.object().keys({
        name: Joi.string().required(),
      }),
    };
  }


  static postSensor() {
    return {
      [Segments.QUERY]: Joi.object().keys({
        name: Joi.string().required(),
      }),
      [Segments.BODY]: Joi.object().keys({
        value: Joi.number().required(),
        timestamp: Joi.number().integer().required(),
      }),
    };
  }


  static deleteSensor() {
    return {
      [Segments.QUERY]: Joi.object().keys({
        name: Joi.string().required(),
      }),
      [Segments.BODY]: Joi.object().keys({
        timestamp: Joi.number().integer().required(),
      }),
    };
  }


  static ruleDate() {
    return {
      [Segments.QUERY]: Joi.object().keys({
        name: Joi.string().required(),
      }),
      [Segments.BODY]: Joi.object().keys({
        value: Joi.number().required(),
        date: Joi.string().required(),
        timestamp: Joi.number().integer().required(),
      }),
    };
  }
};

RoutesThe route name its self a pretty awesome name, and summarize much for beginners, and this is a great goal. The concept of routes in Express with each middleware passing the parameters (request, response, next) to another, is awesome. B̶u̶̶̶t̶̶̶ ̶̶̶t̶̶̶o̶̶̶ ̶̶̶m̶̶̶e̶̶̶, the routes should be a map of all that happens in all microservices in that project, without any complexity and respecting the separation of concerns.

路线路线名称本身就是一个很棒的名称,可以为初学者总结很多,这是一个不错的目标。 在Express中使用每个中间件将参数(请求,响应,下一个)传递给另一个中间件的路由的概念很棒。 B̶u̶̶̶t̶̶̶t̶̶̶o̶̶̶m̶̶̶e̶̶̶,路线应该是该项目中所有微服务中发生的所有事情的地图 ,而没有任何复杂性并且尊重关注点的分离。

Thinking about remove complexity, the routes could be much messed with validations. So thanks to this Mr. Harrison Kamau on this article, I could found how to separate Celebrate from validations, even though I couldn't put Celebrate inside of some of the validators to become more similar with Clean Code (if you know to do this, send me a PR).

考虑到删除复杂性,路线可能会与验证混淆不清。 所以,感谢这个哈里森卡马乌先生这样的文章 ,我可以找到如何分开验证庆祝,即使我不能把庆祝里面的一些验证,成为与清洁代码更相似(如果你知道如何做到这一点,向我发送PR)。

Important: If you require in routes, DTO, Rule and Controller as classes with static methods you don’t need (and can’t) instantiate the class. Your code gonna be more cleaner.

重要提示:如果您需要将路由,DTO,Rule和Controller作为具有静态方法的类,则不需要(也不能)实例化该类。 您的代码将更加干净。

const { celebrate } = require('celebrate');
const express = require('express');


const SensorDTO = require('../model/SensorDTO');
const SensorRule = require('../rule/SensorRule');
const SensorController = require('../controller/SensorController');


module.exports = class Routes {
  constructor() {
    this.routes = express.Router();


    return this.init();
  }


  init() {
    this.map()
    return this.routes
  }


  map() {
    this.routes.get('/sensor', celebrate(SensorDTO.getSensor()), SensorController.index);
    this.routes.post('/sensor', celebrate(SensorDTO.postSensor()), SensorRule.date, celebrate(SensorDTO.ruleDate()), SensorController.store);
    this.routes.delete('/sensor', celebrate(SensorDTO.deleteSensor()), SensorController.delete);
  }
};

Two ValidationsInitially, I just thinking in one validation, with it doing the validation of parameters that comes from frontend, and next the added parameters from business rule. But thanks to Mr. Sebastián Alvarez, that write a phrase in below on this article, which made me change my mind.

两次验证最初,我只想进行一次验证,首先要验证来自前端的参数,然后验证业务规则中添加的参数。 但是感谢塞巴斯蒂安·阿尔瓦雷斯先生 在下面的这篇文章中写了一个短语,这使我改变了主意。

Avoid Invalid Requests to Your Express.js Server Using celebrate”

避免使用Celebrate对您的Express.js服务器进行无效请求”

Initially, I just think about validation to not mess up the business rules, but with that, you don’t gonna even wait for an error, you gonna have the error faster as you can, if you pass wrong types or even parameters named wrong. If I didn’t put validation in the first chain of command, the user gonna have to wait for the error after business rule. T̶h̶e̶s̶e̶ ̶d̶a̶y̶s̶ ̶I̶’̶v̶e̶ ̶b̶e̶e̶n̶ ̶p̶a̶s̶s̶i̶n̶g̶ ̶f̶o̶r̶ ̶m̶u̶c̶h̶ ̶o̶f̶ ̶t̶h̶e̶ ̶w̶a̶i̶t̶i̶n̶g̶ ̶e̶r̶r̶o̶r̶ ̶p̶r̶o̶b̶l̶e̶m̶,̶ ̶s̶o̶ ̶I̶ ̶c̶a̶u̶g̶h̶t̶ ̶f̶a̶s̶t̶e̶r̶, ̶t̶h̶e̶ ̶i̶d̶e̶a̶,̶ ̶a̶n̶d̶ ̶d̶e̶c̶i̶d̶e̶d̶ ̶̶b̶r̶i̶n̶g̶ t̶o h̶e̶r̶e̶.̶

最初,我只是考虑不使业务规则混乱的验证,但是,即使您传递错误的类型甚至是错误的参数,您也不必等待错误,就可以更快地遇到错误。 。 如果我没有在第一个命令链中添加验证,那么用户将不得不在业务规则之后等待错误。 这些日子从来就被路过的大部分时间等待的误差问题,̶所以我抓住了更快的想法,̶并决定提请h̶e̶r̶e̶.̶

So, maybe you just have caught the why we need two validators, is just because the idea here, is adding the parameters that will return from business rule to request parameters.

因此,也许您刚刚了解了为什么我们需要两个验证器的原因,仅仅是因为这里的想法是将将从业务规则返回的参数添加到请求参数中。

I never, look in a good way for validations in the database, s̶i̶n̶c̶e̶ ̶f̶r̶o̶m̶ ̶a̶c̶c̶e̶s̶s̶,̶ ̶a̶ ̶p̶r̶e̶t̶t̶y̶ ̶f̶a̶n̶ ̶o̶f̶ ̶j̶a̶v̶a̶s̶c̶r̶i̶p̶t̶,̶ ̶l̶e̶t̶,̶ ̶a̶n̶d̶ ̶c̶o̶n̶s̶t.

我从不以一种很好的方式在数据库中进行验证,s̶i̶n̶c̶e̶̶f̶r̶o̶m̶̶a̶c̶c̶e̶s̶s̶s̶,̶̶a̶̶p̶r̶e̶t̶t̶y̶̶f̶a̶n̶̶o̶f̶̶̶̶

These days I’ve met a database using in PostgreSQL that stores or read just using functions in PL/pgSQL, and I asked why having validations in the database, summarizing, I added a wrong validation (varchar to a number), and we spend 5 hours adding seven lines to production, and the error was disconnected at all with the problem. After that, it’s pretty clear to me that there is no gain validating anything in PL/pgSQL or in any database.

这些天来,我遇到了一个在PostgreSQL中使用的数据库,该数据库存储或仅使用PL / pgSQL中的函数进行读取,我问为什么在数据库中进行验证,总结,我添加了错误的验证( varchar表示数字 ),并且我们花了5小时将7条生产线添加到生产中,该错误因问题而完全断开。 在那之后,我很清楚,在PL / pgSQL或任何数据库中验证任何东西都没有收益。

Later, I told the history to my girlfriend and ask her “Why two validations?” and the answer was “Because it is!”, and I asked again “Why not 10000?”, and now she answered kindly “Because exists two modules, backend, and database.”. She was right, but do the validation in a language like PL/pgSQL, was a complete nightmare. So if you have to do, do it in NodeJS using Celebrate.

后来,我告诉女友历史,问她“为什么要两次验证?” 答案是“因为是!”,然后我又问“为什么不输入10000?”,现在她的回答是“因为存在两个模块,即后端和数据库”。 她是对的,但是用PL / pgSQL这样的语言进行验证是一场噩梦。 因此,如果必须这样做,请使用Celebrate在NodeJS中进行。

Important:

重要:

  1. You must remove the parameters that will be not stored in the database, or not used in a query on the controller, to avoid errors (this parameter is not allowed) from Celebrate.

    您必须删除将不会存储在数据库中或未在控制器上的查询中使用的参数,以避免Celebrate错误(不允许使用此参数)。
  2. You should validate each parameter used in the controller.

    您应该验证控制器中使用的每个参数。

The methodologyThe full methodology is focused in redesign the routes. So the route should be composed by:

方法论完整的方法论着重于重新设计路线。 因此,路线应由以下组成:

Image for post
Flowchart of idea.
想法流程图。

The nameBased on the description above, the full name of the new methodology should be, t̶u̶n̶d̶u̶n̶t̶u̶n̶d̶u̶n̶t̶u̶n̶d̶u̶n̶, DRDC (DTO Rule DTO Controler).

名称基于上面的描述,新方法的全名应为DRDC (DTO规则DTO控件)。

Final observationMaybe not even all routes should be in DRDC way, which may don’t make any sense in delete routes, if you don’t have authentication, like I didn’t have in this little demonstrated project.

最终观察甚至不是所有的路由都应该采用DRDC方式,如果您没有身份验证,那么删除路由可能就没有任何意义,就像我在这个小型演示项目中没有的那样。

ComplementJust to complement, more files of this project… The whole project is in below on Github.

补充仅作为补充,该项目的更多文件…整个项目在Github上

exports.up = (knex) => {
  return knex.schema.createTable('sensor', (table) => {
    table.string('id').primary();
    table.bigInteger('timestamp').notNullable().primary();
    table.string('date').notNullable();
    table.string('name').notNullable();
    table.float('value').notNullable();
  });
};


exports.down = (knex) => {
  return knex.schema.dropTable('sensor');
};
const knex = require('knex');


module.exports = class Cloud {
  constructor(enviroment) {
    // Object
    this.knex = knex;


    // Variables
    this.enviroment = enviroment;
  }


  connection() {
    const development = this.development();


    if ((this.enviroment = 'development')) return this.knex(development);
  }


  development() {
    return {
      client: 'sqlite3',
      connection: {
        filename: './model/database/db.sqlite',
      },
      migrations: {
        directory: './model/database/migrations',
      },
      useNullAsDefault: true,
    };
  }
};
const Cloud = require('../model/database/Cloud');


const db = new Cloud('development').connection();


module.exports = class SensorController {
  static async index(req, res) {
    try {
      const { name } = req.query;


      const count = await db('sensor')
        .where({
          name,
        })
        .count()
        .first();


      const todos = await db('sensor')
        .where({
          name,
        })
        .select(['timestamp', 'date', 'value']);


      res.header('X-Total-Count', count['count(*)']);


      return res.status(200).json(todos);
    } catch (error) {
      res.send(error);
    }
  }


  static async store(req, res) {
    try {
      const { name } = req.query;
      const { date, value, timestamp } = req.body;


      const [id] = await db('sensor').insert({
        id: timestamp,
        name,
        date,
        value,
        timestamp,
      });


      return res
        .status(200)
        .json({ message: `Timestamp '${timestamp}', of sensor '${name}'  added! Id:`, id });
    } catch (error) {
      throw error;
    }
  }


  static async delete(req, res) {
    try {
      const { name } = req.query;
      const { timestamp } = req.body;


      const data = await db('sensor').where({ name, timestamp }).select('*').first();


      if (!data) return res.status(404).json({ error: 'Was not found that data.' });


      await db('sensor').where({ name, timestamp }).delete();


      return res
        .status(200)
        .json({ message: `Timestamp '${timestamp}', of sensor '${name}'  deleted!` });
    } catch (error) {
      throw error;
    }
  }
};
const express = require('express');
const cors = require('cors');
const { errors } = require('celebrate');


module.exports = class App {
  constructor(routes) {
    this.routes = routes;
    this.server = express();
    this.middleware();
    this.router();
  }


  middleware() {
    this.server.use(
      cors()
      // origin: ''
    );
    this.server.use(express.json());
    this.server.use(errors());
  }


  router() {
    this.server.use(this.routes);
  }
};
const Routes = require('./Routes');
const App = require('./App.js');


const routes = new Routes();
const app = new App(routes).server;


app.listen(3000);

Fork this, in Github.

Github中分叉。

翻译自: https://medium.com/@juliocflima/separation-of-concerns-in-routes-nodejs-deac5bf844cb

koa 分离路由

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值