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