C# - Comparando o Modelo Anêmico e Modelo Rico


 Hoje vamos tratar novamente com o modelo anêmico e rico e hoje faremos uma comparação entre esses duas abordagens.

Você sabe o que significa um modelo anêmico ? E um modelo Rico ?

Hoje veremos as definições e vamos fazer uma comparação entre esses modelos

O cenário atual

Após você iniciar com a utilização de uma linguagem orientada a objetos como o C# você vai ser incentivado a desenvolver separando as responsabilidades em seu projeto de forma a ter uma interação entre as classes no seu projeto de forma bem definida e clara.

Levando isso adiante você vai encontrar toneladas de artigos mostrando a arquitetura em camadas, seja com duas, três ou n camadas, e, pregando seus benefícios : separar as responsabilidades em camadas de negócio, de acesso a dados e da interface. E realmente parece que faz sentido.

Seguindo nesta orientação seremos encorajados a separar as regras de negócio dos atributos das entidades e do acesso a dados. Afinal, isso facilita os testes, a manutenção, etc., etc.

Neste contexto o modelo de domínio dos sistemas em geral ficam reduzidos a classes que possuem apenas propriedades e nenhum comportamento funcionamento como um DTO - Data Transfer Object,  sendo que os comportamentos relacionados com validações e regras de negócio estão separados em serviços, utilitários, repositórios, etc.

E então nem percebemos que voltamos a programar de forma procedural e temos classes imensas e as regras de negócio estão repetidas e espalhadas no projeto em outras 'camadas'. E isso leva a um código difícil de manter, de testar e de entender.

Pera aí !!! não era isso mesmo que estávamos tentando evitar usando o paradigma da POO ?

Bem-vindo ao modelo anêmico.

Apresentando o modelo Anêmico

Um modelo anêmico se expressa por classes que armazenam apenas dados possuindo apenas um conjunto de gets e sets sem nenhuma lógica ou validação.

Dessa forma não há como não negar que perdeu-se o conceito da Programação orientada a objetos e acabamos tendo atributos e métodos separados no projeto em diversas classes e/ou camadas, o que leva a regras de negócio repetidas.

É muito fácil dar um exemplo de um modelo anêmico (já usou o Entity FrameworkCore hoje...), basta criar uma classe de domínio com apenas gets e sets.

Abaixo temos a classe Item que para existir depende da classe Pedido. Isso esta expresso na propriedade PedidoId:

    public class Item
    {
        public int ItemId { get; set; }
        public string ItemNome { get; set; }
        public decimal ItemPreco { get; set; }
        public int PedidoId { get; set; }
    }

O modelo anêmico foi primeiramente definido por Martin Fowler que o considera um anti-pattern.

No entanto para objetos DTO e para realizar um simples CRUD básico usar esse modelo pode até ser justificável.

Apresentando o modelo Rico

O modelo de domínio Rico é o posto do anêmico e possui dados e comportamentos definidos na mesma classe incluindo validações e regras de negócio quando necessário.

Para mostrar a diferença vamos modelar a classe Item, que representa um modelo de um domínio como um modelo rico:

Veja como ficou o código:

    public class Item
    {
        public Item(int itemId, int pedidoId, string itemNome, decimal itemPreco)
        {
            if (pedidoId <= 0)
            {
                throw new ArgumentException("O código do Pedido deve ser informado");
            }
            if (itemId <= 0)
            {
                throw new ArgumentException("O número do Item é obrigatório");
            }
            this.PedidoId = pedidoId;
            this.ItemId = itemId;
            this.ItemNome = itemNome;
            this.ItemPreco = itemPreco;
        }
        public int ItemId { get; private set; }
        public int PedidoId { get; private set; }
        private string _itemNome;
        public string ItemNome {
            get => this._itemNome;
            private set {
                this._itemNome = (value.Length > 100) ? throw 
                                  new ArgumentOutOfRangeException(nameof(ItemNome),
                                  "O nome do item não pode ter mais que 100 caracteres.") : value;
            }
        }
        private decimal _itemPreco;
        public decimal ItemPreco {
            get => this._itemPreco;
            private set {
                if (_itemPreco <= 0)
                    throw new ArgumentNullException(nameof(ItemPreco),
                          "O preço do item deve ser maior que zero"): value;
            }
        }
    }

Acima temos o código da classe Item onde fizemos as seguintes alterações:

Agora nossa classe Item esta mais robusta e somente poderá receber valores via construtor da classe com uma validação de entrada feita usando o encapsulamento.

Para não aprofundar o artigo eu não estou usando conceitos do Domain Drive Design como Value Object, Entity e  Aggregate e também não estou fazendo uma validação do domínio usando uma abordagem mais robusta como usar o pattern Notification conforme orienta Martin Fowler no seu artigo : Replacing Throwing Exceptions with Notifications in Validations

Conclusão

O modelo anêmico desrespeita os conceitos do paradigma da POO - Programação Orientada a Objetos.

Os objetos no paradigma da OOP  possuem dados e comportamento, e, o objeto deve modelar a entidade em tempo real.

Ao separar o comportamento, não estamos seguindo o paradigma da POO, e, com comportamentos residindo em uma classe separada, será difícil herdar, aplicar polimorfismo, abstração e assim por diante.

Além disso Modelos anêmicos podem ter estados inconsistentes a qualquer momento.

Dessa forma as únicas opções justificáveis para usar um modelo Anêmico seria para DTOs e para fazer um CRUD básico.

E estamos conversados...

"E em nada vos espanteis dos que resistem, o que para eles, na verdade, é indício de perdição, mas para vós de salvação, e isto de Deus. Porque a vós vos foi concedido, em relação a Cristo, não somente crer nele, como também padecer por ele"
Filipenses 1:28,29

Referências:


José Carlos Macoratti