Pular para o conteúdo principal
Versão: Versão Cestas

Arquitetura do Sistema

Esta página descreve a arquitetura técnica da plataforma Divino Cestas (commit 2b1881fafe).

🏗️ Visão Geral

A plataforma Divino Alimento é uma aplicação web full-stack construída com tecnologias modernas e arquitetura em camadas.

┌─────────────────────────────────────────────────────┐
│ Cliente (Browser) │
│ Bootstrap 5 + jQuery + EJS │
└───────────────────────┬─────────────────────────────┘
│ HTTP/HTTPS
┌───────────────────────▼─────────────────────────────┐
│ Express.js Server (Node.js) │
│ ┌─────────────────────────────────────────────┐ │
│ │ Controllers (Handlers) │ │
│ └──────────────────┬──────────────────────────┘ │
│ ┌──────────────────▼──────────────────────────┐ │
│ │ Business Logic (Model Layer) │ │
│ └──────────────────┬──────────────────────────┘ │
│ ┌──────────────────▼──────────────────────────┐ │
│ │ Database Layer (SQL Queries) │ │
│ └──────────────────┬──────────────────────────┘ │
└─────────────────────┼─────────────────────────────┘
┌─────────────────────▼─────────────────────────────┐
│ Sequelize ORM + PostgreSQL │
└─────────────────────────────────────────────────────┘

📦 Stack Tecnológico

Backend

  • Runtime: Node.js
  • Framework: Express.js v4.17.1
  • ORM: Sequelize v6.37.1
  • Banco de Dados: PostgreSQL (produção), SQLite (testes)
  • Template Engine: EJS v3.1.6
  • Autenticação: OpenID Connect (express-openid-connect v2.4.0)

Frontend

  • UI Framework: Bootstrap 5.0.2
  • JavaScript: jQuery 3.6.0
  • CSS: Custom CSS + Bootstrap

Ferramentas e Utilitários

  • PDF Generation: PDFKit v0.12.1
  • CSV Export: json2csv v5.0.7
  • Environment Config: dotenv v9.0.1
  • Development: Nodemon v2.0.7
  • Testing: Mocha v11.7.1, Chai v4.3.6

DevOps

  • Containerização: Docker & Docker Compose v2
  • Automação: Rake (Ruby-based task runner)
  • Migrations: Sequelize CLI v6.6.2

📂 Estrutura do Projeto

DivinoAlimento/
├── app/
│ ├── src/
│ │ ├── controllers/ # 🎮 Controladores (request handlers)
│ │ │ ├── IndexController.js
│ │ │ ├── CicloController.js
│ │ │ ├── CestaController.js
│ │ │ ├── ProdutoController.js
│ │ │ ├── OfertaController.js
│ │ │ ├── ComposicaoController.js
│ │ │ ├── PedidoConsumidoresController.js
│ │ │ ├── UsuarioController.js
│ │ │ ├── PontoEntregaController.js
│ │ │ ├── CategoriaController.js
│ │ │ ├── MovimentacaoController.js
│ │ │ ├── RelatoriosController.js
│ │ │ └── ...
│ │ │
│ │ ├── model/ # 🧠 Lógica de Negócio
│ │ │ ├── Ciclo.js
│ │ │ ├── Cesta.js
│ │ │ ├── Produto.js
│ │ │ ├── Oferta.js
│ │ │ ├── Composicao.js
│ │ │ ├── PedidoConsumidores.js
│ │ │ ├── Usuario.js
│ │ │ └── ...
│ │ │
│ │ ├── db/ # 💾 Camada de Banco de Dados
│ │ │ ├── cicloSql.js
│ │ │ ├── cestaSql.js
│ │ │ ├── produtoSql.js
│ │ │ ├── ofertaSql.js
│ │ │ ├── composicaoSql.js
│ │ │ ├── usuarioSql.js
│ │ │ └── ...
│ │ │
│ │ ├── views/ # 🎨 Templates EJS
│ │ │ ├── index.ejs
│ │ │ ├── ciclo.ejs
│ │ │ ├── cesta.ejs
│ │ │ ├── oferta.ejs
│ │ │ ├── parts/
│ │ │ │ ├── header.ejs
│ │ │ │ └── page-header.ejs
│ │ │ └── ...
│ │ │
│ │ ├── server.js # 🚀 Servidor Express
│ │ └── routes.js # 🛣️ Definição de Rotas
│ │
│ ├── models/ # 📊 Modelos Sequelize ORM
│ │ ├── usuario.js
│ │ ├── ciclo.js
│ │ ├── cesta.js
│ │ ├── produto.js
│ │ ├── oferta.js
│ │ └── ...
│ │
│ ├── migrations/ # 🔄 Migrations do Banco
│ │ ├── 20210501000000-create-usuario.js
│ │ ├── 20210502000000-create-ciclo.js
│ │ └── ...
│ │
│ ├── config/ # ⚙️ Configurações
│ │ └── config.js
│ │
│ ├── public/ # 🌐 Assets Estáticos
│ │ ├── images/
│ │ └── styles/
│ │
│ └── package.json

├── env.example # 🔐 Template de Variáveis
├── Rakefile # 🛠️ Tarefas de Automação
├── compose.live.yml # 🐳 Docker Compose
└── README.md

🔀 Arquitetura em Camadas

1️⃣ Camada de Apresentação (Views)

Responsabilidade: Renderização de páginas HTML para o usuário

  • Templates EJS com Bootstrap 5
  • Formulários dinâmicos com validação client-side
  • JavaScript/jQuery para interatividade

Arquivos: app/src/views/*.ejs

2️⃣ Camada de Controle (Controllers)

Responsabilidade: Processar requisições HTTP e coordenar fluxo

// Exemplo: CicloController.js
class CicloController {
async showIndex(req, res) {
// 1. Recebe requisição
// 2. Chama camada de negócio
// 3. Retorna resposta/view
}

async save(req, res) {
// Processa POST, valida, salva
}
}

Arquivos: app/src/controllers/*Controller.js

3️⃣ Camada de Negócio (Model)

Responsabilidade: Lógica de negócio e regras

// Exemplo: Ciclo.js (model)
class Ciclo {
static async getAll() { /* ... */ }
static async findOrCreate(data) { /* ... */ }
static async getCicloAtivo() { /* ... */ }
static validarDatas(dataInicio, dataFim) { /* ... */ }
}

Arquivos: app/src/model/*.js

4️⃣ Camada de Dados (Database Layer)

Responsabilidade: Queries SQL complexas

// Exemplo: cicloSql.js
const cicloSql = {
getAll: () => `
SELECT c.*,
COUNT(DISTINCT ce.id) as num_entregas
FROM ciclo c
LEFT JOIN cicloentregas ce ON c.id = ce.cicloid
GROUP BY c.id
ORDER BY c.id DESC
`,

getById: (id) => `
SELECT c.*, ce.*, cc.*
FROM ciclo c
LEFT JOIN cicloentregas ce ON c.id = ce.cicloid
LEFT JOIN ciclocestas cc ON c.id = cc.cicloid
WHERE c.id = ${id}
`
};

Arquivos: app/src/db/*Sql.js

5️⃣ Camada de Persistência (ORM)

Responsabilidade: Mapeamento objeto-relacional

// Exemplo: models/ciclo.js
module.exports = (sequelize, DataTypes) => {
const Ciclo = sequelize.define('Ciclo', {
nome: DataTypes.STRING,
dataInicio: DataTypes.DATE,
dataFim: DataTypes.DATE,
ativo: DataTypes.BOOLEAN
});

Ciclo.associate = (models) => {
Ciclo.hasMany(models.CicloEntregas);
Ciclo.hasMany(models.CicloCestas);
};

return Ciclo;
};

Arquivos: app/models/*.js

🔐 Autenticação e Segurança

OpenID Connect (OAuth 2.0)

// server.js
const { auth } = require('express-openid-connect');

app.use(auth({
authRequired: false,
auth0Logout: true,
issuerBaseURL: process.env.issuerBaseURL,
baseURL: process.env.BASE_URL_APP,
clientID: process.env.clientID,
secret: process.env.secret
}));

Fluxo de Autenticação

1. Usuário acessa aplicação

2. Middleware verifica req.oidc.user

3. Se não autenticado → Redireciona para login OAuth

4. Usuário faz login (Auth0 ou Mock Server)

5. Callback retorna com token

6. Sistema busca/cria usuário no banco

7. Sessão estabelecida (req.oidc.user disponível)

Perfis de Usuário

perfil: DataTypes.ARRAY(DataTypes.ENUM(
'info', // Apenas visualização
'master', // Super administrador
'admin', // Administrador
'fornecedor', // Fornecedor/Agricultor
'consumidor' // Consumidor
))

🗄️ Modelo de Dados

Diagrama de Relacionamentos (Simplificado)

┌─────────────┐
│ Usuario │────┐
└─────────────┘ │
│ hasMany
┌─────────────┐ ▼
│ Ciclo │ ┌───────┐ ┌─────────────────────┐
│ │─▶│Oferta │────▶│ OfertaProdutos │
│ │ └───────┘ └─────────────────────┘
│ │
│ │ ┌──────────────┐
│ │─▶│CicloEntregas │
│ │ └──────────────┘
│ │
│ │ ┌─────────────┐ ┌──────────────┐
│ │─▶│ CicloCestas │────▶│ Composicoes │
│ │ └─────────────┘ └──────────────┘
│ │ │ │
│ │ │ ▼
│ │ │ ┌─────────────────────┐
│ │ │ │ComposicaoOfertaProd │
│ │ │ └─────────────────────┘
│ │ │
│ │ ▼
│ │ ┌───────────┐
│ │ │ Cesta │
└─────────────┘ └───────────┘

│ hasMany

┌──────────────────────┐
│ PedidoConsumidores │
└──────────────────────┘

│ hasMany

┌──────────────────────────────┐
│PedidoConsumidoresProdutos │
└──────────────────────────────┘

┌─────────────┐
│ Produto │
└─────────────┘

│ belongsTo

┌──────────────────┐
│CategoriaProdutos │
└──────────────────┘

🚀 Fluxo de Requisição

Exemplo: Criar um Pedido de Consumidor

1. Browser → GET /pedidoconsumidores/:id

2. routes.js → PedidoConsumidoresController.showIndex()

3. Controller → PedidoConsumidores.getByCicloAndUser(cicloId, userId)

4. Model → pedidoConsumidoresSql.getById(id)

5. Database Layer → executa SQL complexo com JOINs

6. PostgreSQL retorna dados

7. Model processa e retorna objeto

8. Controller renderiza view: res.render('pedidoConsumidores.ejs', { data })

9. EJS + Bootstrap → HTML final

10. Browser exibe página

Exemplo: Salvar Pedido (POST)

1. Browser → POST /pedidoconsumidores (form data)

2. Controller valida dados

3. Model.save() → Transaction no Sequelize

4. Salva PedidoConsumidores

5. Salva PedidoConsumidoresProdutos (items)

6. Commit da transaction

7. Redireciona → /pedidoconsumidoresconfirmacao/:id

🐳 Infraestrutura Docker

Services

# compose.live.yml
services:
app.dev:
build: ./app
ports:
- "${APP_PORT}:3000"
depends_on:
- db.dev
environment:
- DATABASE_URL=postgresql://...

db.dev:
image: postgres:13
environment:
- POSTGRES_DB=divinoalimento
- POSTGRES_USER=divino
- POSTGRES_PASSWORD=***
volumes:
- pgdata:/var/lib/postgresql/data

Rake Tasks

# Rakefile
namespace :vivo do
task :constroi # docker-compose build
task :liga # docker-compose up -d
task :para # docker-compose down
task :popular # seed database
task :psql # access database
task :sh # access app container
end

📊 Performance e Escalabilidade

Otimizações Implementadas

  1. Queries SQL Otimizadas: JOINs complexos na camada de dados
  2. Índices no Banco: Migrations incluem índices estratégicos
  3. Connection Pooling: Sequelize gerencia pool de conexões
  4. Static Assets: Servidos via Express static middleware
  5. EJS Caching: Templates compilados em produção

Considerações para Escala

  • Horizontal Scaling: Adicionar mais instâncias app.dev
  • Load Balancer: Nginx/HAProxy na frente
  • Database Read Replicas: Para queries pesadas de relatórios
  • CDN: Para assets estáticos
  • Redis: Para sessões distribuídas (futuro)

🧪 Ambiente de Testes

// config/config.js
test: {
dialect: 'sqlite',
storage: ':memory:',
logging: false
}
  • Banco em Memória: SQLite para testes rápidos
  • Isolamento: Cada teste roda em transação
  • Mocha + Chai: Framework de testes

📝 Referências de Arquivos

ComponenteLocalização
Servidorapp/src/server.js:1
Rotasapp/src/routes.js:1
Controllersapp/src/controllers/
Modelsapp/src/model/
Database Layerapp/src/db/
ORM Modelsapp/models/
Viewsapp/src/views/
Configapp/config/config.js:1
Migrationsapp/migrations/

🔗 Próximos Passos


Dúvidas sobre arquitetura? Consulte o Troubleshooting ou abra uma issue no GitHub.