# Convenções — Backend ## Como criar um novo domínio Siga a sequência abaixo ao adicionar um novo módulo ao backend. O exemplo usa o domínio hipotético `Produto`. --- ### 1. Schema de validação (se houver body a validar) Crie `src/middleware/schemas/Produto.Schema.js`: ```js import { z } from "zod"; export const criarProdutoSchema = z.object({ nome: z.string().min(2).max(100), preco: z.number().positive(), }); ``` --- ### 2. Model Objection (se houver tabela no banco) Crie `src/models/Produto.model.js`: ```js import { Model } from "objection"; export class Produto extends Model { static get tableName() { return "produtos"; } } ``` Crie também a migration correspondente em `db/migrations/` (arquivos `.cjs`; o diretório é configurado no `knexfile.js`). --- ### 3. Service Crie `src/services/produtoService.js` com toda a lógica de negócio: ```js import { Produto } from "#models/Produto.model.js"; export async function listarProdutos() { return Produto.query().orderBy("Nome"); } export async function criarProduto(dados) { return Produto.query().insert(dados); } ``` --- ### 4. Controller Crie `src/controllers/Produto.Controller.js`. Controllers são finos — apenas extraem dados da requisição e delegam ao service: ```js import * as produtoService from "#services/produtoService.js"; import { AppError } from "#shared/errors/AppError.js"; export const ProdutoController = { async Listar(req, res, next) { try { const produtos = await produtoService.listarProdutos(); res.json({ produtos }); } catch (err) { next(err); } }, async Criar(req, res, next) { try { const produto = await produtoService.criarProduto(req.body); res.status(201).json({ produto }); } catch (err) { next(err); } }, }; ``` --- ### 5. Rotas Crie `src/routes/Produto.Rotas.js`: ```js import { Router } from "express"; import { ProdutoController } from "#controllers/Produto.Controller.js"; import { requireUser } from "#middleware/RequireUser.js"; import { requireAdmin } from "#middleware/RequireAdmin.js"; import { validate } from "#middleware/Validate.js"; import { criarProdutoSchema } from "#middleware/schemas/Produto.Schema.js"; export const produtosRouter = Router(); produtosRouter.get("/", requireUser, ProdutoController.Listar); produtosRouter.post("/", requireUser, requireAdmin, validate({ body: criarProdutoSchema }), ProdutoController.Criar); ``` --- ### 6. Registro no Roteamento Adicione o novo router em `src/routes/index.js`: ```js import { produtosRouter } from "./Produto.Rotas.js"; // dentro de IniciarRoteamento(app): app.use("/api/produtos", produtosRouter); ``` --- ## Regras gerais **Nomenclatura:** - Arquivos de rotas: `NomeDoModulo.Rotas.js` (PascalCase) - Arquivos de controller: `NomeDoModulo.Controller.js` (PascalCase) - Arquivos de service: `nomeDoModuloService.js` (camelCase) - Arquivos de model: `NomeDoModulo.model.js` (PascalCase) - Arquivos de schema: `NomeDoModulo.Schema.js` (PascalCase) **Imports:** o código atual usa caminhos **relativos** em todo o `src/` — siga esse padrão para manter consistência: ```js import { db } from "../config/db.config.js"; ``` > O `package.json` define o alias `#*` → `./src/*`, que é válido e funciona, mas ainda **não é usado** em nenhum arquivo do `src/`. Por ora, prefira imports relativos para alinhar ao restante do código. (Os exemplos acima usam `#` apenas para ilustrar; o código real usa `../`.) **Auth nas rotas:** - Leitura pública: sem middleware de auth - Leitura autenticada: `requireUser` - Escrita de dados globais / operações destrutivas: `requireUser, requireAdmin` - Consulte [auth.md](auth.md) para entender os níveis de acesso **Tratamento de erros:** - Sempre encapsule o corpo do controller em `try/catch` e passe o erro para `next(err)` - Use `AppError` para erros esperados (ex: não encontrado, sem permissão) - O `ErrorHandler` global formata a resposta automaticamente **Documentação:** - Ao criar novas rotas, adicione os endpoints em [api.md](api.md)