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:
C# - Tasks x Threads. Qual a diferença
VB .NET - Datas, horas: conceitos e operações
C# - Programação Assíncrona como : Asycn e Task
O tratamento de datas no VB.NET
C# - Obtendo a data e a hora por TimeZone
C# - O Struct Guid - Macoratti.net
DateTime - Macoratti.net
ASP .NET Core - Usando conceitos do DDD
Domain Drive Design - Resumo do Livro de Eric Evans
Formatação de data e hora para uma cultura ...
.NET - Conceitos DDD para iniciantes
Domain Driven Design - Área de Aplicação
Domain Driven Design: Entity e Value Object