🇧🇷 Guia para modelagem de domĂnios ricos
Vou iniciar esse artigo com um exemplo de classe e fazer a pergunta: VocĂŞ acha que o cĂłdigo abaixo Ă© um bom domain model ?
Obs.: Não há nada de errado em usar classes assim quando não há complexidade de negócio, mas você tem que ter em mente que isso não é um domain model. Isso é mais parecido com um data transfer object (DTO).
Faz um tempo que eu vejo implementações de “domain models” da maneira demonstrada acima e toda lógica de manipulação dessas classes é colocada em outro lugar, muitas vezes chamado de “XyzService”, no nosso exemplo “OrderService”:
Ambos exemplos mostram o que se chama de modelos de domĂnio anĂŞmico, ou em inglĂŞs anemic domain model. Assunto muito bem argumentado por Marin Fowler. TambĂ©m de acordo com ele, um modelo de domĂnio Ă© um modelo de objeto do domĂnio que incorpora comportamento e dados.
Se refatorarmos o cĂłdigo e colocarmos comportamento e dados na mesma classe, o modelo Order se torna mais claro e mostra o seu objetivo:
Agora parece melhor.
Mas agora, o que aconteceria ao construir a classe e adicionar um item ao pedido ?
BOOM! Null Reference Exception, porque o nosso modelo de domĂnio ainda precisa de um gerenciamento de estado e a lista de itens está nula.
O objeto deve encapsular a maneira que ele Ă© construĂdo, modificado e se manter em um estado válido.
Vamos modificar a classe com private setters e construtores privados, evitando que algum código externo construa/altere o objeto em um estado inválido.
Precisamos de uma maneira de criar o pedido. Vamos adicionar um método que é capaz de criar um pedido em um estado válido:
O mesmo para a classe OrderItem:
Se a classe Order for consumida por um cĂłdigo externo, a Ăşnica opção que Ă© exposta para criá-la Ă© usando o mĂ©todo “New”. NĂŁo será possĂvel alterar o estado em um cĂłdigo externo, pois suas propriedades sĂŁo somente leitura do ponto de vista externo.
É possĂvel notar que a nossa classe Order tem uma propriedade que armazena uma lista de itens de um pedido e um mĂ©todo chamado AddItem() usado para adicionar itens validos ao pedido. Mesmo assim, um cĂłdigo externo pode acessar a propriedade de lista e adicionar um item sem validação, uma vez que o objeto List do framework Ă© mutável.
Uma maneira de solucionar essa questão é sempre expor uma cópia imutável dos itens e encapsular a lista original:
Com essa alteração, a entidade pode modificar a lista e códigos externos podem ver a lista sem modificá-la.
Aplicando esses passos, temos uma classe completamente independente, incorruptĂvel e auto gerenciável.
Para ver o resultado final das classes, acesse o gist.
Este artigo tambĂ©m está disponĂvel em InglĂŞs.