Desenhando a Estrutura de um Sistema Web: Guia Visual para Iniciantes

Desenhando a Estrutura de um Sistema Web: Guia Visual para Iniciantes
Objetivo: Este artigo mostra, de forma didática e visual, como dividir um sistema web em camadas bem definidas, escolher um padrão de projeto adequado (como MVC) e colocar tudo em prática com um exemplo completo em Node.js. Ideal para desenvolvedores que dão os primeiros passos na arquitetura de software.
📖 Introdução
Quando você cria seu primeiro aplicativo web, a tentação é colocar tudo em um único arquivo e deixar o código crescer desordenado. Essa abordagem funciona para protótipos, mas rapidamente se transforma em dor de cabeça: manutenção difícil, acoplamento excessivo e risco de bugs aumentam exponencialmente.
A arquitetura de software oferece um mapa para organizar o código em camadas com responsabilidades claras. Ao seguir um padrão como o MVC (Model‑View‑Controller), você ganha:
Separação de responsabilidades – cada parte do sistema tem um papel bem definido. Facilidade de teste e evolução – mudar a camada de dados não afeta a interface do usuário. Escalabilidade – novos recursos são adicionados sem refatorar tudo.
Neste guia, vamos:
Vamos ao desenho da arquitetura!
1️⃣ Entendendo as Camadas de uma Aplicação Web
Dica: Pense em uma camada como um “departamento” da sua empresa. Cada departamento tem sua própria função, mas todos colaboram para entregar o produto final.
| Camada | Responsabilidade principal | Tecnologias típicas (exemplo) |
|---|---|---|
| Apresentação | Interface com o usuário (HTML, CSS, JS). | EJS, Handlebars, React, Vue |
| Negócios | Regras de negócio, validações, orquestração de fluxo. | Serviços, Controllers |
| Persistência | Acesso a bancos de dados, leitura/escrita de registros. | SQLite, PostgreSQL, ORM (Sequelize) |
1.1 Camada de Apresentação (View)
Responsável por receber a requisição do navegador e devolver uma resposta visual. Nessa camada, evitamos lógica de negócio; usamos apenas templates que recebem dados já processados.
1.2 Camada de Negócios (Controller / Service)
Aqui mora a lógica de domínio: cálculos, regras de validação, fluxo de processos. Controllers recebem a requisição da camada de apresentação, chamam os serviços e retornam o resultado.
1.3 Camada de Persistência (Model / Repository)
Abstrai o acesso ao banco de dados. Em vez de escrever SQL espalhado pelo código, criamos repositórios que expõem métodos como findById, save, delete.
Diagrama de Alto Nível
O diagrama acima ilustra a comunicação entre as três camadas, mostrando como a camada de apresentação nunca toca diretamente o banco de dados.
2️⃣ Padrões de Projeto Mais Usados no Início
Embora existam dezenas de padrões, alguns são especialmente úteis para quem está começando. Vamos analisar três deles e comparar suas características.
2.1 MVC (Model‑View‑Controller)
Model: Representa os dados e a lógica de persistência. View: Renderiza a interface. Controller: Recebe a requisição, invoca o Model e devolve a View.
Prós: Simples, amplamente suportado por frameworks. Contras: Pode se tornar “fat controller” se a lógica não for delegada a serviços.
2.2 MVP (Model‑View‑Presenter)
Semelhante ao MVC, mas o Presenter contém toda a lógica de UI, mantendo a View “passiva”. É comum em aplicações desktop ou mobile, mas menos usado em web tradicional.
2.3 Clean Architecture (versão simplificada)
Divide o código em círculos concêntricos: Entidades, Casos de Uso, Interface de Entrada, Interface de Saída. Para iniciantes, pode ser complexo, mas vale a pena conhecer os conceitos de dependência invertida.
Tabela Comparativa
| Padrão | Camada de UI | Onde fica a lógica de negócio | Facilidade de início |
|---|---|---|---|
| MVC | View | Controller / Service | ★★★★★ |
| MVP | View (passiva) | Presenter | ★★★★ |
| Clean (simpl.) | View | Use Cases / Interactors | ★★☆☆☆ |
Recomendação: Para o primeiro projeto, MVC oferece o melhor equilíbrio entre estrutura e simplicidade.
3️⃣ Montando um Projeto de Exemplo – Passo a Passo
Vamos criar um pequeno sistema de gerenciamento de tarefas (To‑Do List) usando:
Node.js (runtime) Express (framework web) EJS (template engine) SQLite (banco leve, sem necessidade de servidor)
3.1 Preparação do Ambiente
# Crie a pasta do projeto e entre nela
mkdir todo-mvc && cd todo-mvc
Inicialize o npm
npm init -y
Instale as dependências
npm install express ejs sqlite3
3.2 Estrutura de Pastas
todo-mvc/
├─ src/
│ ├─ controllers/
│ │ └─ taskController.js
│ ├─ models/
│ │ └─ taskModel.js
│ ├─ services/
│ │ └─ taskService.js
│ ├─ routes/
│ │ └─ taskRoutes.js
│ ├─ views/
│ │ ├─ layout.ejs
│ │ └─ tasks.ejs
│ └─ app.js
├─ data/
│ └─ tasks.db # Banco SQLite (gerado em tempo de execução)
└─ package.json
Observação: A divisão acima segue o padrão MVC + Service, facilitando a manutenção.
3.3 Código da Camada de Persistência – taskModel.js
``js
// src/models/taskModel.js
const sqlite = require('sqlite3').verbose();
const path = require('path');
const DB_PATH = path.resolve(__dirname, '../../data/tasks.db');
// Garante que o banco e a tabela existam
const initDb = () => {
const db = new sqlite.Database(DB_PATH);
db.run(
CREATE TABLE IF NOT EXISTS tasks (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL,
completed INTEGER NOT NULL DEFAULT 0,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
)
);
return db;
};
class TaskModel { constructor() { this.db = initDb(); }
all() { return new Promise((resolve, reject) => { this.db.all('SELECT FROM tasks ORDER BY created_at DESC', (err, rows) => { if (err) return reject(err); resolve(rows); }); }); }
find(id) { return new Promise((resolve, reject) => { this.db.get('SELECT FROM tasks WHERE id = ?', [id], (err, row) => { if (err) return reject(err); resolve(row); }); }); }
create(title) { return new Promise((resolve, reject) => { this.db.run( 'INSERT INTO tasks (title) VALUES (?)', [title], function (err) { if (err) return reject(err); resolve({ id: this.lastID, title, completed: 0 }); } ); }); }
update(id, fields) { const sets = Object.keys(fields) .map(k => ${k} = ?`)


