Módulo 10: Paradigmas de Programação & Padrões de Projeto • Bloco 5: Arquitetura e Enterprise Patterns

🏛️ Separar o que o Usuário Vê do que o Sistema Sabe: Toda interface gráfica mistura três preocupações que mudam por motivos diferentes — os dados e regras do domínio, a aparência da tela e a reação às ações do usuário. Quando elas vivem no mesmo arquivo, qualquer ajuste visual ameaça a regra de negócio e nada pode ser testado sem abrir a tela. Neste capítulo comparamos as três grandes famílias de padrões de apresentação — MVC, MVP e MVVM — e mostramos por que frameworks modernos (Android Jetpack, SwiftUI, Vue, WPF, Flutter) convergiram para variações do MVVM.


🗺️ Mapa Conceitual do Capítulo

flowchart TD
    UI_ARCH["Padrões de Apresentação e UI"]

    UI_ARCH --> MVC["1. MVC (Model-View-Controller)<br/>• Reenskaug, Xerox PARC (1979)<br/>• Controller recebe a entrada e atualiza o Model<br/>• View observa o Model"]

    UI_ARCH --> MVP["2. MVP (Model-View-Presenter)<br/>• Taligent/IBM (anos 1990), Fowler: Passive View<br/>• View passiva atrás de uma interface<br/>• Presenter 100% testável sem GUI"]

    UI_ARCH --> MVVM["3. MVVM (Model-View-ViewModel)<br/>• Microsoft WPF (2005)<br/>• ViewModel expõe estado observável<br/>• Data Binding elimina código de atualização"]

    MVC -->|"evolução: View sem lógica"| MVP
    MVP -->|"evolução: binding automático"| MVVM

    style UI_ARCH fill:#ede7f6,stroke:#512da8,stroke-width:2px
    style MVC fill:#e8f5e9,stroke:#4caf50,stroke-width:1.5px
    style MVP fill:#fff3e0,stroke:#ff9800,stroke-width:1.5px
    style MVVM fill:#e1f5fe,stroke:#03a9f4,stroke-width:1.5px

🧩 1. O Ponto de Partida: Separated Presentation

Os três padrões compartilham a mesma premissa, que Martin Fowler chamou de Separated Presentation: o Model (entidades, regras, persistência) não deve saber que existe uma interface gráfica. Isso permite:

  • Trocar a tela (web → mobile → CLI) sem reescrever regras de negócio;
  • Testar o domínio com testes unitários rápidos;
  • Ter vários “rostos” para os mesmos dados (um painel e um relatório lendo o mesmo Model).

O que muda entre MVC, MVP e MVVM é quem conversa com quem do lado da apresentação — e isso define o quanto a lógica de tela pode ser testada.


🏛️ 2. O Padrão MVC (Model-View-Controller)

Criado por Trygve Reenskaug na Xerox PARC (1979), para o Smalltalk-80:

  • Model: estado do domínio e regras de negócio puras (validações, cálculos, persistência).
  • View: renderiza os dados do Model para o usuário.
  • Controller: captura a entrada (clique, tecla, requisição HTTP), aciona o Model e escolhe a View.
flowchart LR
    U(("Usuário")) -->|"ação"| C["Controller"]
    C -->|"atualiza"| M["Model"]
    M -.->|"notifica mudança (Observer)"| V["View"]
    V -->|"lê dados"| M
    C -->|"seleciona"| V
    V -->|"exibe"| U

2.1 Os Dois “MVCs” que Você Vai Encontrar

  1. MVC clássico (desktop): a View observa o Model (padrão Observer do Capítulo 14) e se redesenha sozinha quando ele muda.
  2. MVC web (Model 2): usado por Rails, Django, Spring MVC e Laravel. Não há observação contínua: a requisição HTTP chega ao Controller, que consulta o Model e entrega os dados a um template (a View), gerando HTML uma vez por requisição.

⚠️ Armadilha — Massive View Controller: no iOS/UIKit, o UIViewController acumulava papéis de View e Controller ao mesmo tempo, virando classes de milhares de linhas impossíveis de testar. Esse sintoma motivou a migração para MVP e MVVM.


🎭 3. O Padrão MVP (Model-View-Presenter)

No MVP, a View torna-se passiva: ela apenas repassa eventos ao Presenter e executa ordens de exibição. Toda decisão de apresentação sai da View:

classDiagram
    class ICarrinhoView {
        <<interface>>
        +codigo_digitado() str
        +mostrar_total(texto)
        +mostrar_erro(texto)
        +habilitar_botao_aplicar(bool)
    }
    class TelaAndroid
    class ViewEspia
    class CarrinhoPresenter {
        -view: ICarrinhoView
        -catalogo: CatalogoCupons
        +ao_digitar_codigo()
        +ao_clicar_aplicar()
    }
    ICarrinhoView <|.. TelaAndroid : produção
    ICarrinhoView <|.. ViewEspia : testes
    CarrinhoPresenter --> ICarrinhoView
    CarrinhoPresenter --> CatalogoCupons

3.1 Por que a Interface da View é o Segredo

Como o Presenter depende de ICarrinhoView (uma abstração — DIP do Capítulo 6), podemos substituí-la por uma View espiã nos testes e verificar exatamente quais comandos o Presenter enviou:

view = ViewEspia()
presenter = CarrinhoPresenter(view, CatalogoCupons(), subtotal=150.0)
view.codigo = "bemvindo10"
presenter.ao_clicar_aplicar()
assert view.chamadas[-1] == "total='Total: R$ 135.00 (10% off)'"

3.2 Custos do MVP

  • A relação Presenter ↔ View é 1:1 e bidirecional: o Presenter precisa chamar cada método de atualização manualmente.
  • Telas complexas geram interfaces de View enormes (dezenas de métodos mostrar_x, esconder_y).
  • O ciclo de vida (rotação de tela no Android, por exemplo) exige cuidado para não manter referência a uma View destruída.

⚡ 4. O Padrão MVVM (Model-View-ViewModel)

Proposto por John Gossman (Microsoft, 2005) para o WPF, o MVVM resolve o principal custo do MVP: o código de atualização manual. O ViewModel não conhece a View — ele apenas expõe estado observável e comandos. Um mecanismo de Data Binding mantém a tela sincronizada.

flowchart LR
    V["View<br/>(XAML, Compose, template Vue)"] <-->|"Data Binding"| VM["ViewModel<br/>• propriedades observáveis<br/>• comandos"]
    VM -->|"chama"| M["Model / Repositório"]
    M -->|"retorna dados"| VM

4.1 Binding Unidirecional vs. Bidirecional

  • One-way (ViewModel → View): a tela reflete o estado (StateFlow + collectAsState no Android, @Published no SwiftUI).
  • Two-way (View ↔ ViewModel): a digitação atualiza o estado e o estado atualiza o campo (v-model no Vue, {Binding Mode=TwoWay} no WPF).
  • Estado derivado: propriedades como pode_converter se recalculam automaticamente quando as de origem mudam — sem nenhum if na View.

4.2 MVVM e Arquitetura Unidirecional (UDF)

Frameworks declarativos modernos (Jetpack Compose, SwiftUI, React, Flutter) combinam o ViewModel com o fluxo unidirecional: eventos sobem da tela para o ViewModel, estado desce do ViewModel para a tela. Variações como MVI (Model-View-Intent) e Redux levam essa ideia ao extremo, com um único objeto de estado imutável.

⚠️ Armadilha: um ViewModel que importa classes de UI (Context, Button, document) deixou de ser ViewModel. Se ele não roda num teste unitário puro, a separação foi quebrada.


📊 5. Matriz Comparativa Sistemática: MVC vs. MVP vs. MVVM

Dimensão de Análise MVC MVP MVVM
Quem recebe a entrada do usuário Controller View, que delega ao Presenter View, ligada a comandos do ViewModel
Como a View é atualizada Observa o Model (desktop) ou é renderizada por requisição (web) Presenter chama métodos da interface da View Data Binding automático
Presentation conhece a View? Controller conhece a View Sim, via interface (1:1) Não
Testabilidade da lógica de tela Média Alta (View falsa) Alta (sem nenhuma View)
Boilerplate Baixo Alto (interfaces de View) Médio (infraestrutura de binding)
Ecossistemas típicos Rails, Django, Spring MVC, Laravel Android clássico, WinForms, GWT WPF, Jetpack, SwiftUI, Vue, Angular