Skip to content

Arquitetura

O projeto segue arquitetura MVC e está organizado como mono-repo. Objetivos e justificativas:

  • MVC: Separação clara entre domínio (Model), apresentação (View) e orquestração (Controller) facilita testes, manutenção e substituição de camadas.
  • Mono-repo: Facilita compartilhamento de utilitários, coesão entre frontend e backend e manutenção de scripts/CI centralizados.
  • Dados em memória: Escolhido para simplificar implantação e permitir desenvolvimento rápido; facilita demonstração de comportamento.

Estrutura de Diretórios

src/
├── api/                    # Backend: Controllers + Models + Repositórios
│   ├── models/            # Entidades de domínio e validações
│   ├── controllers/       # Handlers HTTP, orquestração
│   ├── repositories/      # Acesso a dados (em memória)
│   ├── routes.js         # Definição de rotas
│   ├── index.js          # Entry point
│   └── package.json
├── web/                    # Frontend: Views + Controllers + Assets
│   ├── views/            # Páginas HTML
│   ├── controllers/      # Lógica de UI e eventos
│   ├── styles/           # CSS
│   ├── index.html
│   ├── app.js
│   └── package.json

Como a Constituição Guiou Escolhas

Consulte Constituição do Projeto para padrões de projeto e boas práticas.

Para detalhes, exemplos práticos e justificativas profundas de cada prática, veja Práticas e Padrões:

Fluxo de Dados

Request HTTP (Cliente)
    ↓
Router (routes.js)
    ↓
Controller (ex: TaskController)
    ├→ Valida entrada
    ├→ Chama Model/Repository
    └→ Formata resposta
         ↓
Response HTTP (Cliente)

Camadas Detalhadas

Model (src/api/models/)

  • Definição de entidades (Task, User, etc).
  • Validações lógicas.
  • Métodos de manipulação de dados (ex: markAsComplete()).

Exemplo:

class Task {
  constructor(id, title, completed = false) {
    this.id = id;
    this.title = title;
    this.completed = completed;
  }

  isValid() {
    return this.title && this.title.length > 0;
  }
}

Repository (src/api/repositories/)

  • Acesso a dados (em memória, BD futuro, etc).
  • Métodos CRUD (Create, Read, Update, Delete).
  • Sem lógica de negócio, apenas persistência.

Exemplo:

class TaskRepository {
  constructor() {
    this.tasks = [];
  }

  save(task) {
    if (!task.isValid()) throw new Error('Invalid task');
    this.tasks.push(task);
    return task;
  }

  findById(id) {
    return this.tasks.find(t => t.id === id);
  }
}

Controller (src/api/controllers/)

  • Orquestra Model + Repository.
  • Valida entrada HTTP.
  • Formata resposta.
  • Trata erros.

Exemplo:

class TaskController {
  constructor(repository) {
    this.repository = repository;
  }

  createTask(req, res) {
    try {
      const task = new Task(Date.now(), req.body.title);
      const saved = this.repository.save(task);
      res.json({ success: true, data: saved });
    } catch (error) {
      res.status(400).json({ success: false, error: error.message });
    }
  }
}

View (src/web/)

  • HTML estático.
  • CSS responsivo com design tokens.
  • JavaScript para interações (DOM manipulation, eventos).
  • Nenhuma lógica de negócio.

Exemplo:

<form id="task-form">
  <input type="text" id="task-input" placeholder="New task...">
  <button type="submit">Add</button>
</form>

<ul id="task-list"></ul>

<script>
  document.getElementById('task-form').addEventListener('submit', async (e) => {
    e.preventDefault();
    const title = document.getElementById('task-input').value;
    const response = await fetch('/api/tasks', {
      method: 'POST',
      body: JSON.stringify({ title }),
      headers: { 'Content-Type': 'application/json' }
    });
    const json = await response.json();
    if (json.success) {
      renderTask(json.data);
      document.getElementById('task-input').value = '';
    }
  });
</script>

Razões Arquiteturais

Decisão Razão Trade-off
MVC Separação de responsabilidades, testabilidade Mais arquivos inicialmente
Mono-repo Sincronização frontend/backend, code sharing Único repositório para gerenciar
Dados em memória Simplicidade, zero setup BD Dados perdidos ao reiniciar

Extensões Futuras

  • Persistência: Migrar TaskRepository para usar banco de dados sem quebrar Controllers/Models.
  • Autenticação: Adicionar middleware de auth entre Router e Controller.
  • Mobile: Reutilizar Models/Repositories com novo frontend mobile.