Onde traçamos a linha entre delegação e encapsulamento da lógica de negócios? Parece-me que quanto mais delegamos, mais anêmicos nos tornamos. No entanto, a delegação também promove a reutilização e o diretor DRY. Então, o que é apropriado delegar e o que deve permanecer em nossos modelos de domínio?
Tome as seguintes preocupações como exemplos:
Autorização . O objeto de domínio deve ser responsável por manter suas regras de controle de acesso (como uma propriedade CanEdit) ou deve ser delegado a outro componente / serviço exclusivamente responsável pelo gerenciamento do acesso, por exemplo, IAuthorizationService.CanEdit (objeto)? Ou deveria ser uma combinação dos dois? Talvez o objeto de domínio tenha uma propriedade CanEdit que delegue para um IAuthorizationService interno para executar o trabalho real?
Validação . A mesma discussão acima se refere à validação. Quem mantém as regras e quem é responsável por avaliá-las? Por um lado, o estado do objeto deve pertencer a esse objeto e a validade é um estado, mas não queremos reescrever o código usado para avaliar regras para cada objeto de domínio. Nós poderia usar a herança neste caso ...
Criação de Objetos . Classe de fábrica versus métodos de fábrica versus 'atualização' de uma instância. Se usarmos uma classe de fábrica separada, somos capazes de isolar e encapsular a lógica de criação, mas à custa de abrir o estado do nosso objeto para a fábrica. Isso pode ser gerenciado se nossa camada de domínio estiver em um assembly separado, expondo um construtor interno usado pela fábrica, mas isso se tornará um problema se houver vários padrões de criação. E, se tudo o que a fábrica está fazendo é chamar o construtor certo, qual é o sentido de ter a fábrica?
Os métodos de fábrica na classe eliminam o problema com a abertura do estado interno do objeto, mas, como são estáticos, não podemos quebrar dependências através da injeção de uma interface de fábrica, como podemos com uma classe de fábrica separada.
Persistência . Alguém poderia argumentar que, se nosso objeto de domínio vai expor o CanEdit ao delegar a responsabilidade de executar a verificação de autorização para outra parte (IAuthorizationService), por que não ter um método Save no objeto de domínio que faça a mesma coisa? Isso nos permitiria avaliar o estado interno do objeto para determinar se a operação pode ser executada sem interromper o encapsulamento. É claro que exige que injetemos a instância do repositório em nosso objeto de domínio, o que me cheira um pouco, então aumentamos um evento de domínio e permitimos que um manipulador executasse a operação de persistência?
Veja para onde estou indo com isso?
Rockford Lhotka tem uma ótima discussão sobre suas razões para seguir a rota Class-in-Charge para sua estrutura CSLA, e eu tenho um pouco de história com essa estrutura e posso ver sua ideia de objetos de negócios paralelos a objetos de domínio de várias maneiras. Mas, tentando me tornar mais aderente aos bons ideais de DDD, estou me perguntando quando a colaboração se torna demais.
Se eu terminar com um IAuthorizationService, IValidator, IFactory e IRepository para minha raiz agregada, o que resta? Ter um método Publish que altera o estado do objeto de Rascunho para Publicado é suficiente para considerar a classe como um objeto de domínio não anêmico?
Seus pensamentos?
fonte
Respostas:
A maior parte da confusão parece estar relacionada à funcionalidade que não deveria existir no modelo de domínio:
A persistência nunca deve estar no modelo de domínio. Jamais. Essa é a razão pela qual você confia em tipos abstratos, como
IRepository
se parte do modelo precisar fazer algo como recuperar uma parte diferente do modelo e usar injeção de dependência ou alguma técnica semelhante para conectar a implementação. Então, considere isso a partir do registro.A autorização geralmente não faz parte do seu modelo de domínio, a menos que faça parte do domínio, por exemplo, se você estiver escrevendo um software de segurança. A mecânica de quem tem permissão para executar o que em um aplicativo é normalmente tratada na "borda" da camada de negócios / domínio, as partes públicas com as quais a interface do usuário e as partes de integração realmente podem conversar - o Controller no MVC, os Serviços ou o próprio sistema de mensagens em uma SOA ... você entendeu.
Fábricas (e suponho que você queira dizer fábricas abstratas aqui) não são exatamente ruins para ter em um modelo de domínio, mas são quase sempre desnecessárias. Normalmente, você só tem uma fábrica quando a mecânica interna da criação de objetos pode mudar. Mas você só tem uma implementação do modelo de domínio, o que significa que sempre haverá um tipo de fábrica que sempre chama os mesmos construtores e outro código de inicialização.
Você pode ter fábricas de "conveniência", se quiser - classes que encapsulam combinações comuns de parâmetros de construtores e assim por diante - mas, honestamente, de um modo geral, se você tiver muitas fábricas no seu modelo de domínio, estará apenas desperdiçando linhas de código.
Então, depois de definir tudo isso, isso deixa a validação. Esse é o único que é meio complicado.
A validação faz parte do seu modelo de domínio, mas também faz parte de todos os outros componentes do aplicativo. Sua interface do usuário e banco de dados terão suas próprias regras de validação semelhantes, porém diferentes, com base em um modelo conceitual semelhante, porém diferente. Não está realmente especificado se os objetos precisam ou não ter um
Validate
método, mas mesmo se o fizerem, eles geralmente o delegarão para uma classe validadora (não interface - a validação não é abstrata no modelo de domínio, é fundamental).Lembre-se de que o validador ainda faz parte tecnicamente do modelo; ele não precisa ser anexado a uma raiz agregada porque não contém nenhum dado ou estado. Modelos de domínio são coisas conceituais, geralmente traduzindo fisicamente para uma montagem ou uma coleção de montagens. Não se estresse com o problema "anêmico" se o código de delegação reside muito próximo ao modelo de objeto; ainda conta.
O que isso tudo vem realmente para baixo é que se você está indo fazer DDD, você tem que entender o que o domínio é . Se você ainda está falando sobre coisas como persistência e autorização, está no caminho errado. O domínio representa o estado de execução de um sistema - os objetos e atributos físicos e conceituais. Qualquer coisa que não seja diretamente relevante para os objetos e relacionamentos em si não pertence ao modelo de domínio, ponto final.
Como regra geral, ao considerar se algo pertence ou não ao modelo de domínio, faça a si mesmo a seguinte pergunta:
"Essa funcionalidade pode mudar por razões puramente técnicas?" Em outras palavras, não devido a qualquer alteração observável nos negócios ou no domínio do mundo real?
Se a resposta for "sim", ela não pertence ao modelo de domínio. Não faz parte do domínio.
Há uma chance muito boa de que, algum dia, você altere suas infraestruturas de persistência e autorização. Portanto, eles não fazem parte do domínio, fazem parte do aplicativo. Isso também se aplica a algoritmos, como classificação e pesquisa; você não deve inserir uma implementação de código de pesquisa binária em seu modelo de domínio, porque seu domínio se preocupa apenas com o conceito abstrato de uma pesquisa, não como ela funciona.
Se, depois de remover todas as coisas que não importam, você descobrir que o modelo de domínio é realmente anêmico , isso deve servir como uma boa indicação de que o DDD é simplesmente o paradigma errado para o seu projeto.
Alguns domínios são realmente anêmicos. Os aplicativos de bookmarking social realmente não têm muito "domínio" para falar; todos os seus objetos são basicamente apenas dados sem funcionalidade. Um sistema de vendas e CRM, por outro lado, tem um domínio bastante pesado; quando você carrega uma
Rate
entidade, existe uma expectativa razoável de que você possa realmente executar tarefas com essa taxa, como aplicá-las a uma quantidade do pedido e descobrir os descontos por volume e os códigos promocionais e todas essas coisas divertidas.Objetos de domínio que só armazenam dados normalmente não significa que você tem um modelo de domínio anêmico, mas isso não necessariamente significa que você criou um design ruim - ele só poderia significar que o domínio em si é anêmico e que você deve estar usando um metodologia diferente.
fonte
Não. A autorização é uma preocupação em si mesma. Os comandos que não seriam válidos devido à falta de permissões devem ser rejeitados antes do domínio, o mais cedo possível - o que significa que muitas vezes queremos verificar a autorização de um comando em potencial para criar a interface do usuário (para não até mostrar ao usuário a opção de edição).
Compartilhar estratégias de autorização entre camadas (na interface do usuário e posteriormente em um manipulador de serviço ou comando) é mais fácil quando a autorização é componente separada do modelo de domínio.
Uma parte complicada que pode ser encontrada é a autorização contextual, em que um comando pode ou não ser permitido não apenas com base nas funções do usuário, mas também nos dados / regras de negócios.
Eu também diria que não, não no domínio (principalmente). A validação ocorre em diferentes contextos, e as regras de validação geralmente diferem entre os contextos. Raramente existe um senso simples e absoluto de válido ou não válido ao considerar os dados encapsulados por um agregado.
Além disso, como a autorização, utilizamos a lógica de validação em todas as camadas - na interface do usuário, no manipulador de serviço ou comando, etc. Mais uma vez, é mais fácil empregar DRY com validação se for um componente separado. Do ponto de vista prático, a validação (principalmente ao usar estruturas) requer a exposição de dados que, de outra forma, deveriam ser encapsulados e geralmente requer que atributos personalizados sejam anexados a campos e propriedades. Eu prefiro que elas estejam em outras classes além dos meus modelos de domínio.
Prefiro duplicar algumas propriedades em algumas classes semelhantes do que tentar forçar as exigências de uma estrutura de validação para minhas entidades. Isso inevitavelmente acaba atrapalhando as classes da entidade.
Eu uso uma camada de indireção. Nos meus projetos mais recentes, este é um comando + manipulador para criar algo, ou seja
CreateNewAccountCommand
. Uma alternativa seria sempre usar uma fábrica (embora isso possa ser estranho se o restante da operação de uma entidade for exposto por uma classe de serviço separada da classe da fábrica).Em geral, porém, tento ser mais flexível com as opções de design para criação de objetos.
new
é fácil e familiar, mas nem sempre é suficiente. Penso que é importante usar o julgamento aqui e permitir que diferentes partes de um sistema usem estratégias diferentes, conforme necessário.Isso raramente é uma boa ideia; Eu acho que há muita experiência compartilhada para apoiar isso.
Talvez um modelo de domínio não seja a escolha correta para esta parte do seu aplicativo.
fonte
OK, aqui vai para mim. Vou evitar isso dizendo o seguinte:
A otimização prematura (e isso inclui o design) geralmente pode causar problemas.
IANMF (não sou Martin Fowler);)
Um pequeno segredo sujo é que, em projetos de tamanho pequeno (mesmo os de tamanho médio), é a consistência da sua abordagem que importa.
Autorização
Para mim, autenticação e autorização são sempre uma preocupação transversal. No meu feliz e pequeno mundo Java, isso é delegado à segurança Spring ou à estrutura Apache Shiro.
Validação Para mim, a validação faz parte do objeto, como eu vejo como definindo o que é o objeto.
por exemplo, um objeto Car tem 4 rodas (OK, existem algumas exceções estranhas, mas vamos ignorar o carro de 3 rodas estranho por enquanto). Um carro simplesmente não é válido, a menos que tenha 4 (no meu mundo), de modo que a validação faz parte do que é a definição de um carro. Isso não significa que você não pode ter aulas de validação auxiliar.
No meu feliz mundo Java, uso estruturas de validação de Bean e uso anotações simples na maioria dos meus campos de Bean. É fácil validar seu objeto, independentemente da camada em que você está.
Criação de Objetos
Eu vejo aulas de fábrica com cautela. Muitas vezes eu tenho visto a
xyxFactoryFactory
classe;)Costumo criar apenas um
new
objeto, conforme necessário, até encontrar um caso em que a Injeção de Dependência é justificada (e como tento seguir uma abordagem de TDD, isso ocorre com mais frequência).No meu feliz mundo Java, que é cada vez mais Guice, mas a primavera ainda é o rei aqui.
Persistência
Então esse é um debate que continua em círculos e rotatórias e eu sempre estou em dúvida sobre isso.
Alguns dizem que se você olhar para o objeto de uma maneira 'pura', a persistência não é uma propriedade essencial, é apenas uma preocupação externa.
Outros consideram que os objetos do seu domínio implementam implicitamente uma interface 'persistente' (sim, eu sei que estou estendendo aqui). Portanto, não há problema em ter vários métodos
save
,delete
etc. Isso é visto como uma abordagem pragmática e muitas tecnologias ORM (JPA no meu feliz mundo Java) lidam com objetos dessa maneira.Por uma questão de segurança transversal, certifico-me de que as permissões de edição / exclusão / adição / qualquer que seja sejam definidas corretamente no serviço que chama o método de salvar / atualizar / excluir no objeto. Se eu sou realmente paranóico, posso até definir as permissões no próprio objeto de domínio.
HTH!
fonte
Jimmy Nilsson aborda esse tópico em seu livro sobre DDD. Ele começou com um modelo anêmico, passou a modelos não anêmicos em um projeto posterior e finalmente se estabeleceu em modelos anêmicos. Seu raciocínio era que os modelos anêmicos poderiam ser reutilizados em vários serviços com lógica de negócios diferente.
O trade-off é a falta de capacidade de descoberta. Os métodos que você pode usar para operar em seus modelos anêmicos estão espalhados por um conjunto de serviços localizados em outros lugares.
fonte
Esta pergunta foi feita há muito tempo, mas está marcada com Design Orientado a Domínio. Eu acho que a pergunta em si contém um mal-entendido fundamental de toda a prática e as respostas, incluindo a resposta aceita, perpetuam um mal-entendido fundamental.
Não há "o modelo de domínio" em uma arquitetura DDD.
Vamos usar a Autorização como exemplo. Deixe-me pedir que você pense em uma pergunta: imagine dois usuários diferentes se autenticando no seu sistema. Um usuário tem permissão para alterar uma determinada entidade, mas o outro não. Por que não?
Eu odeio exemplos simples e inventados, porque muitas vezes confundem mais do que esclarecem. Mas vamos fingir que temos dois domínios diferentes. Primeiro, é uma plataforma CMS para uma agência de marketing. Esta agência tem muitos clientes, todos com conteúdo on-line que precisa ser gerenciado por redatores e artistas gráficos. O conteúdo inclui postagens em blogs e páginas de destino para diferentes clientes.
O outro domínio é o gerenciamento de inventário de uma empresa de calçados. O sistema gerencia o estoque a partir de quando chega do fabricante na França, aos centros de distribuição nos EUA continentais, às lojas de varejo nos mercados locais e, finalmente, ao cliente que compra os sapatos no varejo.
Se você acha que as regras de autorização são as mesmas para as duas empresas, sim, isso seria um bom candidato para um serviço fora do domínio. Mas duvido que as regras de autorização sejam as mesmas. Até os conceitos por trás dos usuários seriam diferentes. Certamente o idioma seria diferente. A agência de marketing provavelmente tem funções como autor do post e proprietário do ativo, enquanto a empresa de calçados provavelmente tem funções como auxiliar de expedição ou gerente de armazém ou gerente de loja.
Esses conceitos provavelmente têm todos os tipos de regras de permissão associadas a eles que precisam ser modelados no domínio. Mas isso não significa que todos fazem parte do mesmo modelo, mesmo dentro do mesmo aplicativo. Porque lembre-se de que existem diferentes contextos limitados.
Portanto, talvez se possa considerar um modelo de domínio não anêmico no contexto da Autorização diferente do contexto de encaminhamento de remessas de calçados para lojas com pouco estoque ou encaminhamento de visitantes do site para a página de destino apropriada, dependendo do anúncio em que clicaram.
Se você se deparar com modelos de domínio anêmicos, talvez precise gastar mais tempo no mapeamento de contexto antes de começar a escrever o código.
fonte