Evans introduz em seu livro "Domain Driven Design" no capítulo 6 "Agregados" o conceito de Agregados. Ele define ainda regras para traduzir esse conceito em uma implementação (Evans 2009, pp. 128-129):
A ENTITY raiz pode entregar referências às ENTITIES internas a outros objetos, mas esses objetos podem usá-las apenas de forma transitória e podem não se apegar à referência.
Depois de elaborar outras regras, ele as resume neste parágrafo:
Agrupe as entidades e objetos de valor em agregados e defina limites em torno de cada um. Escolha uma Entidade para ser a raiz de cada Agregado e controle todo o acesso aos objetos dentro do limite através da raiz. Permita que objetos externos mantenham referências apenas à raiz. Referências transitórias a membros internos podem ser distribuídas para uso somente em uma única operação. Como a raiz controla o acesso, ela não pode ser surpreendida por alterações nos internos. Esse arranjo torna prático aplicar todos os invariantes a objetos no agregado e ao agregado como um todo em qualquer alteração de estado.
Então, o que exatamente o uso transitório significa?
Meu colega entende que apenas a raiz agregada expõe uma interface pública para os clientes. Os clientes não terão oportunidade de chamar qualquer operação em uma entidade que não seja a raiz agregada.
Meu entendimento das frases citadas é diferente. Entendo que, de fato, permite explicitamente clientes chamar operações em entidades internas. No entanto, somente após obtê-los da raiz.
Então, vamos dar um exemplo concreto:
Digamos que um Cart
consiste em muitos Items
. Cada Item
um tem um Quantity
. O modelo deve suportar o caso de uso "Aumentar a quantidade de um item específico". Nenhum invariável pode ser violado, o que afeta algo fora do Item.
Um modelo está violando as regras acima citadas, quando um cliente pode fazer isso chamando cart.item(itemId).increaseQuantity()
ou um cliente deve ter permissão para chamar apenas um cart.increaseItemQuantity(itemId)
? Qual seria o benefício deste último?
fonte
cart.increaseItemQuantity(itemId)
, se por nenhuma outra razão, a menos que seja uma violação da Lei de Deméter. A chamadacart.increaseItemQuantity(itemId)
permite que você faça coisas como atualizar os valores totais do carrinho.Respostas:
Enquanto
Item
não puder existir semCart
estar presente, não haverá diferença entre as duas opções. É possível manter invariantes nos dois casos.No caso de o método estar ativado
Item
, éItem
possível "notificar" seu carrinho pai para verificar o invariante quando ele precisar alterar seu próprio estado. Isso torna as coisas um pouco mais complicadas, porque existe uma dependência cíclica entreItem
eCart
(o que eu assumo não é um problema, graças à suposição na primeira frase e algo que a IMO precisa existir de qualquer maneira).No caso de o método estar
Cart
ativado, torna-o mais simples, porque não há necessidade deItem
referênciaCart
. Mas isso é complicado porque agora o método não apenas verifica a invariância e o estado muda. Mas também precisa garantir que o item (ou seu ID) seja válido para issoCart
. No outro caso, isso já é tratado pelo método que consulta determinado item do carrinho.tl; dr; Ambas as opções têm vantagens e desvantagens claras e nenhuma parece ser obviamente melhor ou pior do que outras.
fonte