MVC: Model, View e Controller na prática
1. Fundamentos do Padrão MVC
1.1. Origem histórica e motivação
O padrão MVC (Model-View-Controller) foi introduzido por Trygve Reenskaug durante sua visita ao Xerox PARC no final dos anos 1970, sendo implementado pela primeira vez na linguagem Smalltalk-80. A motivação central era resolver um problema fundamental: como organizar aplicações interativas que misturavam dados, lógica e interface de forma caótica, tornando-as difíceis de manter e evoluir.
1.2. Separação de responsabilidades: o tripé arquitetural
O MVC propõe uma tríade de componentes com responsabilidades bem definidas:
- Model: gerencia dados e regras de negócio
- View: exibe informações ao usuário
- Controller: processa entradas e coordena as ações
1.3. Fluxo de comunicação entre Model, View e Controller
O fluxo típico ocorre da seguinte forma: o usuário interage com a View, que delega a ação ao Controller. O Controller interpreta a entrada, atualiza o Model e, em seguida, a View é notificada para refletir o novo estado.
Usuário → View → Controller → Model → View (atualizada)
2. O Model: Camada de Dados e Regras de Negócio
2.1. Responsabilidades: estado, validação e lógica de domínio
O Model é o coração da aplicação. Ele contém:
- O estado atual dos dados
- Regras de validação e transformação
- Comportamentos de negócio específicos
2.2. Independência da interface: o Model não conhece View nem Controller
Uma regra fundamental: o Model jamais deve referenciar View ou Controller. Essa independência permite testar regras de negócio isoladamente e reutilizar o Model em diferentes interfaces.
2.3. Estruturação do Model: entidades, repositórios e serviços
Organizamos o Model em camadas internas:
- Entidades: objetos de domínio com identidade e comportamento
- Repositórios: abstrações para persistência e recuperação de dados
- Serviços: operações que envolvem múltiplas entidades
3. A View: Apresentação e Interface com o Usuário
3.1. Responsabilidades: renderização e captura de eventos
A View é responsável exclusivamente por:
- Renderizar dados do Model em formato adequado ao usuário
- Capturar eventos de interação (cliques, teclas, toques)
- Delegar ações ao Controller
3.2. Estratégias de atualização: push vs. pull
Existem duas abordagens principais:
- Push (Observer): o Model notifica a View automaticamente sobre mudanças
- Pull: a View consulta periodicamente o estado do Model
3.3. Variações: View passiva vs. View ativa
Na View passiva, o Controller controla completamente a lógica de apresentação. Na View ativa (Supervising Controller), a View possui lógica simples de formatação, enquanto o Controller gerencia apenas lógica complexa.
4. O Controller: Orquestração e Lógica de Aplicação
4.1. Responsabilidades: interpretar entradas e coordenar Model e View
O Controller atua como intermediário:
- Recebe requisições da View
- Valida e transforma dados de entrada
- Invoca operações no Model
- Seleciona e prepara dados para a View
4.2. Tipos de Controller: Front Controller vs. Page Controller
- Front Controller: ponto único de entrada que centraliza decisões (ex: Spring DispatcherServlet)
- Page Controller: um controller por página ou recurso (ex: ASP.NET Web Forms)
4.3. Limites do Controller: o que NÃO deve estar nele
O Controller não deve conter:
- Regras de negócio (pertencem ao Model)
- Lógica de persistência
- Código de renderização HTML
- Validações complexas de domínio
5. Implementação Prática com Exemplo em Código
5.1. Estrutura de diretórios e classes (exemplo: sistema de tarefas)
task-manager/
├── model/
│ ├── Task.java
│ ├── TaskRepository.java
│ └── TaskService.java
├── view/
│ ├── TaskView.java
│ └── TaskConsoleView.java
└── controller/
└── TaskController.java
5.2. Código do Model (entidade Task e regras de negócio)
// model/Task.java
public class Task {
private Long id;
private String title;
private boolean completed;
public void markAsCompleted() {
if (this.completed) {
throw new IllegalStateException("Task já está concluída");
}
this.completed = true;
}
public void updateTitle(String newTitle) {
if (newTitle == null || newTitle.trim().isEmpty()) {
throw new IllegalArgumentException("Título não pode ser vazio");
}
this.title = newTitle.trim();
}
}
// model/TaskRepository.java
public interface TaskRepository {
void save(Task task);
Task findById(Long id);
List<Task> findAll();
void delete(Long id);
}
5.3. Código da View e Controller (interação e atualização)
// view/TaskView.java
public interface TaskView {
void displayTask(Task task);
void displayAllTasks(List<Task> tasks);
void showError(String message);
}
// controller/TaskController.java
public class TaskController {
private final TaskService taskService;
private final TaskView view;
public TaskController(TaskService taskService, TaskView view) {
this.taskService = taskService;
this.view = view;
}
public void createTask(String title) {
try {
Task task = taskService.createTask(title);
view.displayTask(task);
} catch (Exception e) {
view.showError("Erro ao criar tarefa: " + e.getMessage());
}
}
public void completeTask(Long taskId) {
try {
Task task = taskService.completeTask(taskId);
view.displayTask(task);
} catch (Exception e) {
view.showError("Erro ao completar tarefa: " + e.getMessage());
}
}
public void listAllTasks() {
List<Task> tasks = taskService.findAll();
view.displayAllTasks(tasks);
}
}
6. Variações e Evoluções do MVC
6.1. MVC no frontend web (React, Angular, Vue) vs. backend
No frontend moderno, frameworks como React adotam variações do MVC:
- React: unidirecional (Flux/Redux) - similar ao MVC com fluxo controlado
- Angular: MVC tradicional com services e componentes
- Vue: MVVM (Model-View-ViewModel) com two-way binding
6.2. MVC no backend (Spring MVC, ASP.NET MVC, Rails)
- Spring MVC: utiliza Front Controller (DispatcherServlet), controllers anotados e views JSP/Thymeleaf
- ASP.NET MVC: similar ao Spring, com Razor Views e controllers baseados em ações
- Ruby on Rails: MVC completo com scaffolding e convenção sobre configuração
6.3. Comparação com MVVM e MVP: quando escolher cada um
| Padrão | Melhor para |
|---|---|
| MVC | Aplicações CRUD, protótipos, times pequenos |
| MVP | Testabilidade máxima, interfaces complexas |
| MVVM | Data binding intenso, aplicações ricas (WPF, Angular) |
7. Armadilhas e Boas Práticas
7.1. O "God Controller": como evitar lógica excessiva
Sintomas do God Controller:
- Métodos com mais de 20 linhas
- Injeção de múltiplos serviços diferentes
- Lógica condicional complexa
Solução: extrair serviços de aplicação e use cases.
7.2. Acoplamento entre View e Controller: mitigação com interfaces
Sempre dependa de interfaces para View e Controller. Isso permite:
- Trocar implementações de View (console para web)
- Testar Controller com mocks de View
- Isolar testes unitários
7.3. Testabilidade: como testar cada camada isoladamente
// Teste do Model (sem dependências externas)
public class TaskTest {
@Test
void shouldMarkTaskAsCompleted() {
Task task = new Task("Estudar MVC");
task.markAsCompleted();
assertTrue(task.isCompleted());
}
}
// Teste do Controller (com View mockada)
public class TaskControllerTest {
@Test
void shouldCreateTaskSuccessfully() {
TaskView mockView = mock(TaskView.class);
TaskService service = new TaskService(mockRepository);
TaskController controller = new TaskController(service, mockView);
controller.createTask("Nova tarefa");
verify(mockView).displayTask(any(Task.class));
}
}
8. Conclusão: Quando o MVC é a Escolha Certa?
8.1. Cenários ideais para MVC
O MVC brilha em:
- Aplicações CRUD tradicionais
- Protótipos e MVPs (Minimum Viable Product)
- Projetos com equipes pequenas a médias
- Sistemas onde a interface muda menos que as regras de negócio
8.2. Limitações: complexidade de estado e escalabilidade
O MVC apresenta limitações quando:
- O estado da aplicação é altamente complexo e compartilhado
- Múltiplas Views precisam refletir o mesmo Model simultaneamente
- A aplicação cresce além de dezenas de controllers
8.3. Relação com DDD e Onion Architecture
O MVC é compatível com arquiteturas mais robustas:
- DDD (Domain-Driven Design): o Model do MVC se torna o Domain Model
- Onion Architecture: o Controller vira um adapter de entrada, o Model ocupa o centro
- Clean Architecture: MVC pode ser implementado como um padrão de apresentação
Nos próximos artigos desta série, exploraremos como evoluir do MVC para arquiteturas mais modulares e resilientes.
Referências
- Model-View-Controller (MVC) - Martin Fowler — Artigo clássico que analisa a evolução das arquiteturas de interface, incluindo MVC, MVP e MVVM.
- Spring MVC Documentation — Documentação oficial do Spring MVC, com exemplos práticos de implementação do padrão no backend Java.
- MVC Architecture in Ruby on Rails — Tutorial oficial do Rails que demonstra o MVC na prática com scaffolding e convenções.
- MDN Web Docs: MVC Architecture — Explicação concisa do padrão MVC no contexto de desenvolvimento web moderno.
- Trygve Reenskaug: The Original MVC Paper — O artigo original de 1979 que introduziu o padrão MVC no Smalltalk-80.
- Refactoring Guru: MVC Pattern — Guia visual com exemplos em múltiplas linguagens sobre a implementação do MVC.