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)