DEV Community

Cover image for Um pouco sobre MVVM
Mr Punk da Silva
Mr Punk da Silva

Posted on • Edited on

Um pouco sobre MVVM

1. Análise Técnica da Estrutura e Comunicação no Padrão MVVM

O padrão Model-View-ViewModel (MVVM) é um padrão de arquitetura de software projetado para desacoplar a lógica de apresentação da lógica de negócio em aplicações de interface gráfica. Essa separação é fundamental para alcançar alta coesão, baixo acoplamento e, consequentemente, melhorar a testabilidade, manutenibilidade e o desenvolvimento paralelo de interfaces (por designers) e da lógica subjacente (por desenvolvedores).

Diagrama da Estrutura MVVM

O diagrama a seguir ilustra as relações e o fluxo de comunicação entre os três componentes principais.

 @startuml actor Usuário package "Camada de Apresentação" { rectangle View } package "Camada de Lógica de Apresentação e Negócio" { rectangle ViewModel database Model } Usuário --> View : Interage View -down-> ViewModel : Data Binding / Commands ViewModel -down-> Model : Manipula / Atualiza Model -up-> ViewModel : Retorna Dados ViewModel -up-> View : Notifica Mudanças @enduml 
Enter fullscreen mode Exit fullscreen mode

Fluxo de Interação:

  1. O Usuário interage com a View (ex: clica em um botão).
  2. A View, através de um Command Binding, notifica o ViewModel sobre a ação do usuário.
  3. O ViewModel processa a ação, o que pode envolver a manipulação de dados ou a chamada de métodos no Model.
  4. O Model executa a lógica de negócio (ex: consulta a um banco de dados) e retorna os dados para o ViewModel.
  5. O ViewModel atualiza suas propriedades com os novos dados e, através do mecanismo de INotifyPropertyChanged, notifica a View sobre as mudanças. A View, por sua vez, atualiza a UI automaticamente via Data Binding.

Análise Técnica dos Componentes

  • Model (Modelo):

    • Função: É a autoridade sobre os dados e a lógica de negócio. Não deve ter conhecimento algum sobre qual tecnologia de UI está sendo usada. É a camada mais reutilizável da aplicação.
    • Composição: Inclui entidades (POCOs), objetos de acesso a dados (DAOs, Repositórios), serviços de negócio e lógica de validação. Em aplicações modernas, é comum que os serviços do Model sejam injetados no ViewModel via Injeção de Dependência (DI).
  • View (Visão):

    • Função: Renderizar o estado do ViewModel e capturar a entrada do usuário. Deve ser o mais "passiva" possível.
    • Implementação (XAML): A View é primariamente declarativa. O DataContext é a ponte para o ViewModel. A sintaxe {Binding} é a forma como a View se inscreve para receber atualizações das propriedades do ViewModel e para invocar Commands.
  • ViewModel (Visão-Modelo):

    • Função: É o coração da lógica de apresentação. Prepara os dados do Model para serem exibidos (conversão de tipos, formatação), mantém o estado da View (qual item está selecionado, se um painel está visível) e expõe as ações que a View pode realizar.
    • Desacoplamento da View: O ViewModel não deve ter uma referência direta à View (ex: MainView view = new MainView()). Essa ausência de referência é o que permite que o ViewModel seja facilmente testado em um ambiente de teste unitário, sem a necessidade de instanciar uma UI visual.

2. Análise Técnica da Vinculação de Dados (Data Binding)

A vinculação de dados é o mecanismo que sincroniza a UI (View) com a lógica de apresentação (ViewModel). É um processo automatizado que reduz drasticamente a quantidade de código "boilerplate" necessário para manter a UI consistente com o estado da aplicação.

Diagrama do Fluxo de Notificação

sequenceDiagram participant V as View participant B as Binding Engine participant VM as ViewModel Note over V, VM: Fase de Inicialização V->>B: Analisa a expressão {Binding PropriedadeX} B->>VM: Localiza PropriedadeX e subscreve ao evento PropertyChanged Note over V, VM: Fase de Execução participant A as Ação (ex: Command) A->>VM: Altera o valor de PropriedadeX VM->>VM: Dispara OnPropertyChanged("PropriedadeX") VM-->>B: Notifica o Binding Engine sobre a mudança B->>VM: Obtém o novo valor de PropriedadeX B->>V: Atualiza o controle da UI vinculado 
Enter fullscreen mode Exit fullscreen mode

Este diagrama ilustra como o Binding Engine do framework (WPF, UWP, etc.) atua como um intermediário que observa as mudanças no ViewModel e propaga essas mudanças para a View.

Tópicos Avançados em Data Binding

  • UpdateSourceTrigger: Controla quando a fonte (ViewModel) é atualizada em um TwoWay Binding. O padrão para um TextBox.Text é LostFocus, mas frequentemente é alterado para PropertyChanged para fornecer feedback em tempo real (ex: validação instantânea).
  • Conversores de Valor (IValueConverter): Permitem a transformação de dados entre o ViewModel e a View. Um exemplo clássico é um conversor que transforma um valor booleano true em Visibility.Visible e false em Visibility.Collapsed. Isso mantém a lógica de apresentação fora do ViewModel, que não deveria conhecer tipos específicos da UI como Visibility.
  • StringFormat: Permite a formatação de strings diretamente na expressão de Binding, como Text="{Binding Preco, StringFormat=C}" para formatar um número como moeda.

3. Tópicos Avançados e Melhores Práticas

Injeção de Dependência (DI) e Serviços

Em uma aplicação real, o ViewModel não cria suas dependências (como serviços de acesso a dados) diretamente. Em vez disso, ele as recebe através de seu construtor. Isso é facilitado por um contêiner de Injeção de Dependência (ex: Microsoft.Extensions.DependencyInjection).

Exemplo:

// Interface para o serviço public interface ITarefaService { Task<List<Tarefa>> ObterTarefasAsync(); } // ViewModel consumindo o serviço via DI public class MainViewModel : ViewModelBase { private readonly ITarefaService _tarefaService; // O serviço é injetado no construtor public MainViewModel(ITarefaService tarefaService) { _tarefaService = tarefaService; CarregarTarefasCommand = new RelayCommand(async () => await CarregarTarefas()); } public ICommand CarregarTarefasCommand { get; } private async Task CarregarTarefas() { // Usa o serviço para obter os dados var tarefas = await _tarefaService.ObterTarefasAsync(); // ...lógica para popular a ObservableCollection } } 
Enter fullscreen mode Exit fullscreen mode

Benefícios:

  • Testabilidade: Nos testes, podemos passar uma implementação "fake" ou "mock" de ITarefaService para testar o MainViewModel sem depender de um banco de dados ou API real.
  • Flexibilidade: Podemos trocar a implementação do serviço (ex: de um serviço de banco de dados para um de API REST) sem alterar o ViewModel.

Operações Assíncronas e async/await

Operações de longa duração (chamadas de rede, acesso a banco de dados) nunca devem bloquear o thread da UI. O padrão async/await é usado extensivamente nos ViewModels.

  • Desafio: Como lidar com o estado de "carregando" e desabilitar botões enquanto uma operação está em andamento?
  • Solução: Usar uma propriedade booleana no ViewModel (ex: IsLoading) para controlar a visibilidade de um indicador de progresso e o CanExecute dos comandos.

Exemplo Melhorado:

private bool _isLoading; public bool IsLoading { get => _isLoading; set { _isLoading = value; OnPropertyChanged(); } } private async Task CarregarTarefas() { IsLoading = true; ((RelayCommand)CarregarTarefasCommand).RaiseCanExecuteChanged(); // Desabilita o botão try { var tarefas = await _tarefaService.ObterTarefasAsync(); // ... } finally { IsLoading = false; ((RelayCommand)CarregarTarefasCommand).RaiseCanExecuteChanged(); // Reabilita o botão } } private bool CanCarregarTarefas() { return !IsLoading; } 
Enter fullscreen mode Exit fullscreen mode

Testabilidade do ViewModel

A principal vantagem do MVVM é a testabilidade. Como o ViewModel não tem dependências da UI, podemos escrever testes unitários para ele como faríamos para qualquer outra classe C#.

Exemplo de Teste (usando um framework como xUnit e uma biblioteca de mock como Moq):

[Fact] public void AdicionarTarefaCommand_QuandoExecutado_AdicionaTarefaNaColecao() { // Arrange var mockTarefaService = new Mock<ITarefaService>(); var viewModel = new MainViewModel(mockTarefaService.Object); viewModel.NovaTarefaDescricao = "Testar a aplicação"; // Act viewModel.AdicionarTarefaCommand.Execute(null); // Assert Assert.Single(viewModel.Tarefas); Assert.Equal("Testar a aplicação", viewModel.Tarefas[0].Descricao); } 
Enter fullscreen mode Exit fullscreen mode

Referências Técnicas e Ferramentas

  • MVVM Community Toolkit: Uma biblioteca moderna e de alto desempenho da Microsoft que simplifica drasticamente a implementação do MVVM com geradores de código-fonte para INotifyPropertyChanged e ICommand.
  • Contêineres de Injeção de Dependência:
    • Microsoft.Extensions.DependencyInjection: O padrão para aplicações .NET modernas.
  • Frameworks de Teste e Mocking:
    • xUnit / NUnit: Frameworks para escrever testes.
    • Moq / NSubstitute: Bibliotecas para criar objetos mock para testes de dependências.
  • Artigo Original de John Gossman (Criador do Padrão na Microsoft):
  • MVVM com Injeção de Dependência em .NET MAUI:

Top comments (0)