Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

A Arquitetura em Camadas

Hora de construir o backend que vai servir esses dados via HTTP. A tentação é colocar tudo num único arquivo — receber a requisição, consultar o banco, montar a resposta. Para 12 eventos e um endpoint, até funciona. Mas pense no que vem pela frente: autenticação, validação, tratamento de erro, múltiplos endpoints. Um arquivo monolítico cresce muito rápido.

A abordagem que vamos seguir é dividir o backend em camadas, cada uma com uma responsabilidade:

Route — Define as URLs e os métodos HTTP que a aplicação aceita. GET /api/events deve chamar tal função. Só mapeamento.

Controller — Recebe a requisição HTTP, extrai os dados relevantes (query params, body, headers) e repassa para a camada de serviço. Depois, formata a resposta HTTP com o status code e o body adequados. O controller conhece HTTP; as camadas abaixo dele, não.

Service — Contém a lógica de negócio. Hoje a resposta é simples — “retorna todos os eventos” — mas essa é a camada que vai crescer quando o TamoJunto ganhar regras mais complexas.

Repository — Faz as consultas SQL no banco de dados. É a única camada que sabe que o dado mora no PostgreSQL. Se um dia migrarmos para outro banco, só o repository muda.

A requisição flui assim: Route → Controller → Service → Repository → banco de dados. A resposta faz o caminho inverso.

Para um endpoint que faz SELECT * FROM events, a lógica de cada camada cabe em duas linhas. A separação não é sobre o tamanho do código hoje — é sobre onde cada tipo de decisão fica quando o projeto crescer.

Estrutura de arquivos

Dentro de backend/src/, crie os seguintes arquivos:

backend/
├── migrations/
│   ├── 001_create_events.sql
│   └── 002_create_users_add_fk.sql
├── seed.sql
├── migrate.ts
├── package.json
├── tsconfig.json
├── .env
└── src/
    ├── app.ts
    ├── config.ts
    ├── db.ts
    ├── events/
    │   ├── events.routes.ts
    │   ├── events.controller.ts
    │   ├── events.service.ts
    │   └── events.repository.ts
    └── errors/
        └── errorHandler.ts

Os arquivos de eventos estão agrupados no diretório events/. Quando o TamoJunto ganhar autenticação, teremos um diretório auth/ com a mesma estrutura. Cada feature num lugar.

Por que um framework?

O Node.js já sabe atender HTTP sozinho. O módulo node:http que vem embutido liga um servidor e entrega cada requisição para uma função. Um endpoint como o nosso ficaria assim:

import http from "node:http";

const server = http.createServer((req, res) => {
  if (req.method === "GET" && req.url === "/api/events") {
    const events = buscarEventos();
    res.writeHead(200, { "Content-Type": "application/json" });
    res.end(JSON.stringify(events));
    return;
  }
  res.writeHead(404, { "Content-Type": "application/json" });
  res.end(JSON.stringify({ error: "Not Found" }));
});

server.listen(3000);

Código 5-5: O mesmo endpoint escrito só com o módulo node:http.

Funciona. O problema aparece no segundo endpoint, e no terceiro. Cada rota vira mais um if comparando req.method e req.url na mão. Ler o corpo de um POST significa juntar pedaços do req conforme eles chegam pela rede. Um parâmetro como /api/events/42 exige fatiar a string da URL você mesmo. Nada disso é lógica do TamoJunto: é a mesma canalização que todo servidor HTTP reescreve.

Um framework é uma biblioteca que já resolve essa canalização e te dá uma estrutura pronta para pendurar o seu código. Você não chama o framework quando quer; ele chama o seu código quando uma requisição chega. O roteamento, a leitura do corpo, a extração de parâmetros da URL, o framework cuida disso, e você escreve só a parte que é do seu problema.

O Express é um framework HTTP minimalista para Node.js. O endpoint acima, com Express, vira uma linha de roteamento e uma função que responde, sem nenhum if de despacho. É o que vamos construir a seguir.

Instalando as dependências

Antes de escrever código, inicialize o projeto e instale os pacotes necessários:

$ cd backend
$ npm init -y
$ npm install express pg dotenv
$ npm install -D typescript @types/express @types/pg @types/node tsx

Além do express, o pg é o cliente PostgreSQL e o dotenv carrega variáveis de ambiente de um arquivo .env. Os pacotes com @types/ são as definições de tipo para TypeScript, e tsx é o executor que compila e roda TypeScript num passo só.