A “Inversão de Controle” promove o “Modelo de Domínio Anêmico”?

32

Quando usei o IoC Container no meu último projeto, acabei com entidades anêmicas e com a maior parte da minha lógica de negócios no Stateless Services.

Eu já vi projetos escritos por outros desenvolvedores que utilizam "Inversion of Control" e eles são sempre "Anêmicos".

Como o "Modelo de Domínio Anêmico" é antipadrão, é possível usar IoC e Rich Domain? São alguns bons exemplos, projetos de código aberto que fazem isso?

Mag20
fonte
Acho que precisaríamos ver alguns exemplos específicos do seu caso em particular para ajudar.
Martijn Verburg
1
Desculpe, eu quis dizer trechos de código :)
Martijn Verburg

Respostas:

11

Para iniciantes: DI e IoC não são sinônimos. Sinto muito, mas devo salientar isso (parece-me que você pensa que são).

Quanto à sua pergunta ... Bem, injeção de dependência é apenas uma ferramenta. Como você vai usar essa ferramenta é uma coisa completamente separada. Existem também outras ferramentas (padrões de design) que podem resultar no problema. Por exemplo, acho que a ampla adoção do padrão MVC é um dos principais ingredientes para formar o antipadrão do Modelo de Domínio Anêmico: Controladores (em aplicativos mais simples, em aplicativos mais complicados que seriam Camada de Serviço adicional) assumem a responsabilidade de validar regras de negócios , aplicando-as e transformando entidades de banco de dados em algo útil, enquanto a Camada de negócios se transforma em uma Camada de acesso a dados simples que é ORM simples com mapeamento individual para entidades de banco de dados.

Certamente, é assim que você cria seu aplicativo - você pode criar um Modelo de Domínio correto, se quiser, e todos esses IoC, DI, MVC não o impedem. O que poderia parar você é sua equipe. De alguma forma, você precisa convencê-los a usar o caminho certo e pode ser difícil, pois muitos desenvolvedores de software não possuem um forte histórico arquitetural.

Paweł Dyda
fonte
Acrescentarei a isso que talvez você possa dar uma olhada na abordagem DDD adotada por Eric Evans et al.
Martijn Verburg
1
Eu li o livro de Eric Evans. É bom para metodologia geral e linguagem onipresente, mas falta um pouco nos exemplos do mundo real.
Mag20
Obrigado por apontar a diferença entre DI e IoC. Acho que o problema tinha mais a ver com IoC do que com DI. A pergunta foi alterada para refletir isso.
Mag20
Na minha experiência com frameworks de DI / contentores (Primavera DI, CDI, Unidade), eles realmente não impedi-lo de criar um "modelo de domínio correto", o que para mim significa que os desenvolvedores não devem ser restringidas de usar verdadeiros (ou seja, stateful) objetos . Mas o DI realmente não suporta isso.
Rogério
8

A maioria dos aplicativos (se não todos) é uma mistura de preocupações de infraestrutura e domínio. Quando você alcança um certo nível de complexidade, facilita o gerenciamento se o domínio estiver separado da infraestrutura, para que seja mais fácil raciocinar e possa evoluir independentemente.

É claro que o modelo de domínio ainda precisa se comunicar com o restante do sistema e, geralmente, isso ocorrerá com serviços sem estado (que fazem parte do domínio) que têm problemas de infraestrutura (como acesso ao banco de dados) injetados neles. O uso de um contêiner de IoC não remove essa dependência, ele move sua configuração para uma área separada - novamente facilitando o raciocínio e a manutenção.

As entidades estão armazenando o estado e devem ser responsáveis ​​pelas regras de negócios. Se seus serviços estão aplicando todos os invariantes e outras regras de negócios, é provável que a lógica esteja no lugar errado.

Agora, se você tem a lógica nos lugares certos e ainda assim acabou com serviços que não passam de invólucros em torno de coisas e entidades de infraestrutura que são apenas itens de propriedade, é muito provável que o domínio não seja complexo o suficiente para justificar a sobrecarga de seu próprio modelo. Qualquer coisa que você ler sobre o DDD conterá um aviso de isenção de responsabilidade que realmente é destinado apenas a domínios complexos, mas isso parece ser esquecido com muita frequência.

FinnNk
fonte
7

Vá para a fonte. Comece com a peça de Fowler sobre os modelos de domínio anêmico . Ele refere o Domain Driven Design de Eric Evan como um exemplo de boas práticas. O código fonte para isso está aqui . Baixe.

Observe que ele usa Inversion of Control (procure por @Autowired) e possui classes de serviço (BookingService) e classes de "processo de negócios" (por exemplo, ItineraryUpdater).

O artigo original de Fowler inicia a trilha para o exemplo que você está procurando.

jamie
fonte
Na verdade, esse aplicativo de exemplo não está de acordo com o DDD, conforme descrito no livro. Uma contradição específica com o livro é que ele viola totalmente o conceito de "infraestrutura", permitindo que ele contenha código específico do domínio; por exemplo, a VoyageRepositoryHibernateclasse, que foi colocada na camada de infraestrutura, mas na verdade depende da camada de domínio.
Rogério
Sim, o livro diz na página 73 que a camada de infraestrutura está "abaixo" da camada de domínio e "não deve ter nenhum conhecimento especial do domínio que está servindo". Isso nunca fez sentido para mim. Considere um projeto que possui duas implementações VoyageRepository: VoyageRepositoryHibernate e uma classe VoyageRepositoryJDBC. Suas implementações são necessariamente muito diferentes e específicas da tecnologia. Eles pertencem à camada de domínio? Ou a camada de infraestrutura? Em nosso código, de acordo com o livro, fazemos isso ao contrário: a camada de infraestrutura pode fazer referência à camada de domínio, mas não vice-versa.
51316 jamie
Eles pertencem à camada de domínio, sim. A implementação baseada em JDBC conteria código SQL vinculado a tabelas e colunas no banco de dados do aplicativo, que são específicas para o domínio. Colocar qualquer código específico de domínio ou aplicativo na camada de infraestrutura está errado, pois o "código de infraestrutura" deve ser usado apenas para solucionar problemas técnicos e deve (idealmente) ser totalmente reutilizável entre aplicativos e domínios diferentes. A solução para ter código de "baixo nível" (por exemplo, SQL) na camada de domínio não é movê-lo completamente, mas implementá-lo sobre uma infraestrutura melhor, como ORM.
Rogério
Para mim, a implementação do save (MyDomainObject foo) é uma preocupação puramente técnica. YMMV.
Jamie
Somente se isso não o levar a violar a regra fundamental de uma arquitetura em camadas: uma camada inferior não poderá depender de uma camada superior. Portanto, se você implementou save(foo)com código que está sujeito a alterações quando o modelo de domínio é alterado (por exemplo, se um novo atributo é adicionado a MyDomainObject), ele deve (por definição) pertencer à camada de domínio; caso contrário, você não poderá mais falar em ter "camadas".
Rogério
7

é possível usar IoC e Rich Domain? São alguns bons exemplos, projetos de código aberto que fazem isso?

Suponho que você queira dizer DI em vez de IoC, e o projeto em que você trabalhou usa um contêiner DI como Spring. A IoC possui dois sabores principais: padrão DI e Locator. Não vejo por que o padrão Locator deve ser um problema, então vamos nos concentrar no DI.

Não acho que seja possível, ou pelo menos seria muito impraticável. O principal aspecto dos contêineres DI é que eles controlam a criação de objetos quando os injetam em outros ("objetos gerenciados"). O conjunto de objetos gerenciados ativos quando os projetos são executados é independente de quais itens de domínio existem no seu projeto, mas depende de como os objetos são conectados e quais escopos (singleton, protótipo) são atribuídos a eles.

É por isso que você não deseja permitir que o contêiner de DI gerencie seus objetos de domínio. Mas se você criar objetos manualmente (com o novo), não poderá injetar outros objetos nos objetos do seu domínio. (Deixando possíveis soluções alternativas com a fiação manual de lado.) Como você precisa dessas injeções para substituir implementações por outras, não é possível substituir a funcionalidade de objetos de domínio avançado usando DI. Portanto, você não desejará colocar a funcionalidade em objetos de domínio ou perderá os recursos do DI.

Não vejo como poderia funcionar um contêiner de DI hipotético que não gerencia seus objetos, e nenhuma das implementações existentes permite isso. Portanto, é justo afirmar que o DI depende do gerenciamento de objetos. Portanto, sempre tentará que você divida objetos em potencial do Rich Domain em uma classe anêmica e em uma ou várias classes de scripts de transação.

Wolfgang
fonte
Essa resposta realmente atinge a cabeça quando se trata da tensão entre um Modelo de Domínio Rico e a Injeção de Dependência.
precisa saber é