Os DDD Aggregates são realmente uma boa ideia em um aplicativo da Web?

37
Estou mergulhando no Domain Driven Design e alguns dos conceitos que estou encontrando fazem muito sentido na superfície, mas quando penso neles mais, tenho que me perguntar se isso é realmente uma boa idéia. / p>

O conceito de agregados, por exemplo, faz sentido. Você cria pequenos domínios de propriedade para não ter que lidar com todo o modelo de domínio.

No entanto, quando penso sobre isso no contexto de um aplicativo da web, estamos frequentemente acessando o banco de dados para recuperar pequenos subconjuntos de dados. Por exemplo, uma página pode listar apenas o número de pedidos, com links para clicar para abrir o pedido e ver seus IDs de pedidos.

Se eu estiver entendendo o direito de Agregados, normalmente usaria o padrão de repositório para retornar um OrderAggregate que conteria os membros GetAll , GetByID , Delete e Save . OK isso parece bom. Mas ...

Se eu chamar GetAll para listar todos os meus pedidos, pareceria que esse padrão exigiria que toda a lista de informações agregadas fosse retornada, pedidos completos, linhas de pedidos, etc ... Quando eu preciso apenas de um pequeno subconjunto dessas informações (apenas informações do cabeçalho).

Estou faltando alguma coisa? Ou há algum nível de otimização que você usaria aqui? Não posso imaginar que alguém defenda o retorno de agregados inteiros de informações quando você não precisa.

Certamente, pode-se criar métodos em seu repositório como GetOrderHeaders , mas isso parece frustrar o propósito de usar um padrão como repositório em primeiro lugar.

Alguém pode esclarecer isso para mim?

EDITAR:

Depois de muito mais pesquisas, acho que a desconexão aqui é que um padrão puro de Repositório é diferente do que a maioria das pessoas pensa de um Repositório como sendo.

Fowler define um repositório como um armazenamento de dados que usa semântica de coleta e é geralmente mantido na memória. Isso significa criar um gráfico inteiro de objetos.

Evans altera o Repositório para incluir Aggregate Roots e, assim, o repositório é amputado para suportar apenas os objetos em um Aggregate.

A maioria das pessoas parece pensar em repositórios como objetos de acesso a dados glorificados, onde você cria métodos para obter os dados desejados. Essa não parece ser a intenção descrita nos Padrões de Arquitetura de Aplicativos Corporativos da Fowler.

Outros ainda pensam em um repositório como uma simples abstração usada principalmente para facilitar o teste e o escárnio, ou para desacoplar a persistência do resto do sistema.

Eu acho que a resposta é que este é um conceito muito mais complexo do que eu pensava.

    
por Erik Funkenbusch 13.02.2011 / 22:04
fonte

6 respostas

29

Não use seu modelo de domínio e agregações para consultas.

Na verdade, o que você está perguntando é uma questão bastante comum de que um conjunto de princípios e padrões foi estabelecido para evitar exatamente isso. Chama-se CQRS .

    
por 14.02.2011 / 02:02
fonte
8

Lutei, e ainda estou lutando, com a melhor forma de usar o padrão de repositório em um Design Dirigido por Domínio. Depois de usá-lo pela primeira vez, desenvolvi as seguintes práticas:

  1. Um repositório deve ser simples; é responsável apenas por armazenar objetos do domínio e recuperá-los. Todas as outras lógicas devem estar em outros objetos, como fábricas e serviços de domínio.

  2. Um repositório se comporta como uma coleção como se fosse uma coleção de raízes agregadas na memória.

  3. Um repositório não é um DAO genérico, cada repositório tem sua interface exclusiva e restrita. Um repositório geralmente possui métodos finder específicos que permitem pesquisar a coleção em termos do domínio (por exemplo: me dê todos os pedidos abertos para o usuário X). O repositório em si pode ser implementado com a ajuda de um DAO genérico.

  4. Idealmente, os métodos localizadores retornarão somente raízes agregadas. Se isso for ineficiente, ele também pode retornar objetos de valor somente leitura que contêm exatamente o que você precisa (embora seja uma vantagem se esses objetos de valor também puderem ser expressos em termos do domínio). Como último recurso, o repositório também pode ser usado para retornar subconjuntos ou conjuntos de subconjuntos de uma raiz agregada.

  5. Escolhas como essas dependem das tecnologias usadas, já que você precisa encontrar uma maneira de expressar com mais eficiência seu modelo de domínio com as tecnologias usadas.

por 24.04.2011 / 23:52
fonte
6

Eu não acho que o seu método GetOrderHeaders derrote o propósito do repositório.

O DDD está preocupado (entre outras coisas) em garantir que você obtenha o que precisa por meio da raiz agregada (você não teria um OrderDetailsRepository, por exemplo), mas isso não o limita na maneira como você é mencionando.

Se um OrderHeader for um conceito de Domínio, você deverá defini-lo como tal e ter os métodos de repositório apropriados para recuperá-los. Apenas certifique-se de que você está passando pela raiz agregada correta quando fizer isso.

    
por 14.02.2011 / 03:48
fonte
4

Meu uso do DDD pode não ser considerado DDD "puro", mas adaptei as seguintes estratégias do mundo real usando o DDD em um armazenamento de dados do banco de dados.

  • Uma raiz agregada tem um associado repositório
  • O repositório associado é usado apenas por essa raiz agregada (não está publicamente disponível)
  • Um repositório pode conter chamadas de consulta (por exemplo, GetAllActiveOrders, GetOrderItemsForOrder)
  • Um serviço expõe um subconjunto público do repositório e outras operações não convencionais (por exemplo, Transferir dinheiro de uma conta bancária para outra, LoadById, Pesquisar / Encontrar, CriarEntidade, etc.).
  • eu uso o Root - > Serviço - > Pilha de repositórios. Um serviço DDD só deve ser usado para qualquer coisa que uma Entidade não possa responder (por exemplo, LoadById, TransferMoneyFromAccountToAccount), mas no mundo real eu costumo ficar em outros serviços relacionados a CRUD (Save, Delete, Query) mesmo O root deve ser capaz de "responder / executar" estes. Note que não há nada de errado em dar a uma entidade acesso a outro serviço de raiz agregado! No entanto, lembre-se de que você não incluiria em um serviço (GetOrderItemsForOrder), mas incluiria isso no repositório para que a Raiz Agregada possa utilizá-lo. Observe que um serviço não deve expor nenhuma consulta aberta, como o repositório pode.
  • Eu geralmente defino um Repository abstratamente no modelo de domínio (via interface) e forneço uma implementação concreta separada. Eu defino completamente um serviço no modelo de domínio injetando em um repositório concreto para seu uso.

** Você não precisa recuperar um agregado inteiro. No entanto, se você quiser mais, você precisa perguntar ao root, não a algum outro serviço ou repositório. Isso é um carregamento lento e pode ser feito manualmente com o carregamento lento do homem pobre (injetando o repositório / serviço apropriado na raiz) ou usando o ORM que suporta isso.

Em seu exemplo, eu provavelmente forneceria uma chamada de repositório que trouxesse apenas os cabeçalhos de pedido se eu quisesse carregar os detalhes em uma chamada separada. Observe que, ao ter um "OrderHeader", estamos introduzindo um conceito adicional no domínio.

    
por 07.03.2011 / 06:16
fonte
3

Seu modelo de domínio contém sua lógica de negócios em sua forma mais pura. Todos os relacionamentos e operações que suportam operações de negócios. O que está faltando em seu mapa conceitual é a idéia da Camada de serviço de aplicativo na camada de serviço que envolve o modelo de domínio e fornece uma visão simplificada do domínio de negócios (uma projeção, se preferir) que permite que o modelo de domínio seja alterado conforme necessário, sem afetar diretamente os aplicativos que usam a camada de serviço.

Indo mais longe. A ideia do agregado é que existe um objeto, a raiz agregada, responsável por manter a consistência do agregado. Em seu exemplo, o pedido seria responsável por manipular suas linhas de pedido.

Para o seu exemplo, a camada de serviço exporia uma operação como GetOrdersForCustomer que retornaria apenas o necessário para exibir uma listagem de resumo dos pedidos (como você os chama OrderHeaders).

Por fim, o padrão Repositório NÃO é APENAS uma coleção, mas também permite consultas declarativas. Em C # você pode usar o LINQ como o Objeto de consulta , ou a maioria dos outros O / RMs também fornece uma especificação de objeto de consulta.

A Repository mediates between the domain and data mapping layers, acting like an in-memory domain object collection. Client objects construct query specifications declaratively and submit them to Repository for satisfaction. (from Fowler's Repository page)

Vendo que você pode criar consultas no repositório, também faz sentido fornecer métodos convenientes para lidar com consultas comuns. Ou seja Se você quer apenas os cabeçalhos do seu pedido, você pode criar uma consulta que retorna apenas o cabeçalho e expô-lo de um método de conveniência em seus repositórios.

Espero que isso ajude a esclarecer as coisas.

    
por 31.10.2014 / 16:27
fonte
0

Eu sei que esta é uma pergunta antiga, mas parece que cheguei a uma resposta diferente.

Quando eu faço um repositório, ele geralmente envolve algumas consultas armazenadas em cache .

Fowler defines a repository as a data store that uses collection semantics, and is generally kept in-memory. This means creating an entire object graph.

Mantenha esses repositórios em seus servidores. Eles não são apenas passar por objetos para o banco de dados!

Se eu estiver em um aplicativo Web com uma página listando pedidos, que você pode clicar para ver detalhes, é provável que eu queira que minha página de listagem de pedidos tenha detalhes sobre os pedidos (ID, nome, valor , Data) para ajudar um usuário a decidir qual delas deseja visualizar.

Neste ponto, você tem duas opções.

  1. Você pode consultar o banco de dados e recuperar exatamente o que precisa para fazer a listagem e, em seguida, consultar novamente para obter os detalhes individuais que precisaria ver na página de detalhes.

  2. Você pode fazer 1 consulta que recupera todas as informações e armazena em cache. Na próxima página, solicite a leitura dos servidores em vez do banco de dados. Se o usuário acertar ou selecionar a próxima página, você ainda fará zero viagens ao banco de dados.

Na realidade, como você implementa é apenas isso e detalhes de implementação. Se o meu maior usuário tiver 10 pedidos, eu provavelmente gostaria de ir com a opção 2. Se estou falando de 10.000 pedidos, a opção 1 é necessária. Em ambos os casos acima e em muitos outros casos, quero que o repositório esconda esse detalhe de implementação.

Se eu receber um tíquete para informar ao usuário quanto eles gastaram em pedidos ( dados agregados ) no último mês na página de listagem de pedidos, prefiro escrever a lógica para calcular que em SQL e fazer mais uma viagem de ida e volta ao banco de dados ou prefere calculá-lo usando os dados que já estão nos servidores ram?

Na minha experiência, os agregados de domínio oferecem enormes benefícios.

  • Eles são uma enorme reutilização de código de peça que realmente funciona.
  • Eles simplificam o código, mantendo sua lógica de negócios diretamente na camada principal, em vez de precisar perfurar uma camada de infraestrutura para fazer com que o SQL Server faça isso.
  • Eles também podem acelerar drasticamente os tempos de resposta, reduzindo o número de consultas que você precisa fazer, já que pode armazená-los em cache facilmente.
  • O SQL que estou escrevendo é muitas vezes mais fácil de manter, já que muitas vezes estou apenas pedindo tudo e calculando o lado do servidor.
por 05.11.2018 / 23:00
fonte