Coleção - oondemand/central-oondemand-backend GitHub Wiki
Como criar uma nova coleção
Chamamos de coleção
o conjunto de rotas necessárias para renderizar o componente completo datagrid
no front-end.
Essas rotas se consistem basicamente de:
- Rota de criação
- Rota de atualização
- Listagem com paginação
- Obter registro por id
- Excluir registro
A depender do registro também podemos ter as seguintes rotas:
- Exportar registros (usando os mesmos filtros de listagem)
- Importar registros
Tanto importar quando exportar referem se a arquivos no formato
excel
Rotas
Quando criando uma coleção, o primeiro arquivo a ser criado é o arquivo de rotas em /src/routers
.
⚠ Por questões de convenção o arquivo deve conter Router no nome, ex:
arquivoRouter.js
Nosso arquivo deve conter as rotas (previamente citadas) que são obrigatórias para nossa coleção. Além disso cada rota deve receber uma função handler
que deve estar localizada em controllers
.
É válido ressaltar também a presença de alguns middlewares e helpers importantes que podem ser utilizados em cada rota.
-
registrarAcaoMiddleware
espera a resposta da API, e se tudo der certo, salva no sistema de controle que o usuário fez uma ação (ex: criou ou alterou algo) numa entidade (ex: "usuário", "lista"). -
asyncHandler
esse é um helper que envolve 99,9% das nossas rotas, é umwrapper
para tratar erros automaticamente em nossoshandlers
, evitando ter que usar try/catch em todas elas.
Controllers
Nossas rotas, como dito antes, devem receber uma função handler
localizada em nosso controller /src/controllers
. Essa função handler se comunica com nossos service
e nos retorna uma resposta. Essa é a estrutura básica de uma função handler
ou controller
. Em código ficaria mais ou menos assim (salvo raras exceções)
// Usuario controller
import UsuarioService = require("../../services/usuario")
// função handler para criar usuario
const criar = async(req, res) => {
const usuario = await UsuarioService.criar(req.body)
return sendResponse({ res, statusCode: 200, usuario })
}
⚠ Vale ressaltar que nosso controller deve seguir o padrão
controllers/nomeDoController/index.js
Para nossos controllers temos também alguns helpers importantes
sendResponse
Serve para retornar uma resposta "simples" com status code, message (não obrigatória) e o objeto retorno, ex: usuario, pedido etcsendPaginatedResponse
Usado para retornar uma resposta paginada, segue o padrão de message (não obrigatória) status code, results (registros encontrados paginados) e paginação (current page, total pages, total items, items per page).sendErrorResponse
Esse helper geralmente não é muito utilizado, salvo exceções, uma vez que usamos nossoasyncHandler
para lidar com os erros em nossas funções handlers, mas em casos específicos esse helper é essencial.
É muito importante a utilização desses helpers em nosso controllers, pois para que asyncHandler
e registrarAcaoMiddleware
funcionem corretamente precisamos ter responses
muito bem padronizadas.
Services
Services src/services
são uma parte muito importante da aplicação é aqui que esta concentrada a comunicação entre a camada do moongose
com o banco de dados e as regras de negócio que podem ser modificadas de cliente para cliente.
Cada service deve seguir uma estrutura de pastas pre-definida:
services/
└── nomeDoService/
├── index.js # Concentra todo nosso crud (create read delete...). Caso algumas dessa funções tenha uma regra de negócio especifica, essa função deverá fazer um túnel para `business.js`
├── business.js # Concentra funções do crud com regra de negócio especificas, geralmente em create, update, delete.
├── validation.js # As validações usadas em nosso service deve ficar isolada nessa arquivo, ex: validar se um usuário é existente ou duplicado.
├── excel
├── index.js # Aqui temos as funções de exportar e importar
├── mapExporter.js # Função que retorna estrutura usada para exportar
└── mapImporter.js # Função que retorna estrutura usada para exportar
└── omie
└── index.js # Nesse arquivo se concentra toda lógica de comunicação/integração como omie, por exemplo a sincronização com omie;
Vale destacar também que em src/services
temos uma pasta erros
Essa pasta é onde alocamos os nossos errors previstos e personalizados, como por exemplo erro de usuário não encontrado, geralmente quando há um erro previsto, mas ele não é um erro que se repete por toda aplicação usamos um genericError
ao invés de lançar um error
do javascript
, mas quando o erro se repete por vário lugares da aplicação é comum criarmos um classe de erro personalizada que estende a classe genericError
deste modo:
const GenericError = require("../generic");
class UsuarioNaoEncontradoError extends GenericError {
constructor() {
super("Usuario não encontrado!", 404);
}
}
module.exports = UsuarioNaoEncontradoError;
Essa é uma prática muito importante para que
asyncHandler
eregistrarAcaoMiddleware
funcionem corretamente
Um util muito importante para os nossos services são os pagination
e filters
utils em utils/pagination
Esse util é muito importante para criar as rotas de listagem com paginação. Exemplo de como usar esses utils:
const listarComPaginacao = async ({
pageIndex,
pageSize,
searchTerm,
filtros,
...rest
}) => {
const camposBusca = ["status", "nome", "email", "tipo"];
// Filter utils
const query = FiltersUtils.buildQuery({
filtros,
schema: Pessoa.schema,
searchTerm,
camposBusca,
});
// PaginationUtils
const { page, limite, skip } = PaginationUtils.buildPaginationQuery({
pageIndex,
pageSize,
});
const [pessoas, totalDePessoas] = await Promise.all([
Pessoa.find({
$and: [...query, { status: { $ne: "arquivado" } }],
})
.skip(skip)
.limit(limite),
Pessoa.countDocuments({
$and: [...query, { status: { $ne: "arquivado" } }],
}),
]);
return { pessoas, totalDePessoas, page, limite };
};