¿Por qué no utilizar un contenedor de IoC para resolver dependencias de entidades / objetos comerciales?

82

Entiendo el concepto detrás de DI, pero estoy aprendiendo lo que pueden hacer los diferentes contenedores de IoC. Parece que la mayoría de la gente aboga por el uso de contenedores de IoC para conectar servicios sin estado, pero ¿qué pasa con su uso para objetos con estado como entidades?

Ya sea correcto o incorrecto, normalmente lleno mis entidades con comportamiento, incluso si ese comportamiento requiere una clase externa. Ejemplo:

public class Order : IOrder
{

    private string _ShipAddress;
    private IShipQuoter _ShipQuoter;

    public Order(IOrderData OrderData, IShipQuoter ShipQuoter)
    {
        // OrderData comes from a repository and has the data needed 
        // to construct order
        _ShipAddress = OrderData.ShipAddress;  // etc.
        _ShipQuoter = ShipQuoter;

    }

    private decimal GetShippingRate()
    {
        return _ShipQuoter.GetRate(this);
    }
}

Como puede ver, las dependencias son Constructor Injected. Ahora para un par de preguntas.

  1. ¿Se considera una mala práctica que sus entidades dependan de clases externas como ShipQuoter? Eliminar estas dependencias parece llevarme hacia un dominio anémico, si entiendo correctamente la definición.

  2. ¿Es una mala práctica utilizar un contenedor de IoC para resolver estas dependencias y construir una entidad cuando sea necesario? ¿Es posible hacer esto?

Gracias por cualquier información.

Casey Wilkins
fuente
3
simplemente haga las cosas que necesita porque facilita su trabajo, no porque probablemente así es como debería hacerlo
Omu
28
Como programador autodidacta, tomé ese camino y me llevó al software que actualmente utiliza mi empresa. Lo que surgió fue un software basado en secuencias de comandos de procedimiento / transacciones, en parte porque era más fácil y divertido porque no sabía nada mejor. Es absolutamente doloroso mantenerlo y expandirlo, por eso me tomo el tiempo para reescribirlo y busco consejo de personas que ya han superado estos problemas sobre cómo no cometer los mismos errores.
Casey Wilkins
1
relacionado: stackoverflow.com/questions/827670/…
Ruben Bartelink

Respuestas:

90

La primera pregunta es la más difícil de responder. ¿Es una mala práctica que las Entidades dependan de clases externas? Ciertamente no es lo más común que se puede hacer.

Si, por ejemplo, inyecta un repositorio en sus entidades, efectivamente tiene una implementación del patrón de registro activo . A algunas personas les gusta este patrón por la conveniencia que brinda, mientras que otras (como yo) lo consideran un olor a código o un anti-patrón porque viola el Principio de Responsabilidad Única (SRP).

Se podría argumentar que inyectar otras dependencias en Entidades lo llevaría en la misma dirección (lejos de SRP). Por otro lado, ciertamente tiene razón en que si no hace esto, la atracción es hacia un modelo de dominio anémico .

Luché con todo esto durante mucho tiempo hasta que encontré el artículo (abandonado) de Greg Young sobre DDDD donde explica por qué la arquitectura estereotipada de n niveles / n capas siempre será CRUDY (y por lo tanto bastante anémica).

Mover nuestro enfoque a modelar objetos de dominio como comandos y eventos en lugar de sustantivos parece permitirnos construir un modelo de dominio orientado a objetos adecuado.

La segunda pregunta es más fácil de responder. Siempre puede utilizar una fábrica abstracta para crear instancias en tiempo de ejecución . Con Castle Windsor, incluso puede utilizar la instalación de fábrica con tipo, lo que le libera de la carga de implementar las fábricas manualmente.

Mark Seemann
fuente
Gracias Mark. He visto Typed Factory y leí sus otras publicaciones sobre el método Abstract Factory, pero nunca he visto ningún ejemplo de que se utilicen para resolver entidades. ¿Esto se debe a que la mayoría de las personas diseñan sus entidades sin dependencias excepto un repositorio? ¿Me estoy metiendo en problemas en el futuro si hago un uso riguroso de algo como Typed Factory para resolver mis entidades que tienen dependencias externas?
Casey Wilkins
Lo que estaba tratando de decir es que si sus Entidades contienen otros colaboradores que pueden acceder a otras Entidades, etc., puede encontrarse con todo tipo de problemas de mantenimiento, sin mencionar las violaciones de SRP y los problemas N + 1. Es por eso que Evans recomienda tratar cada entidad como una raíz agregada.
Mark Seemann
En mi ejemplo, ShipQuoter extrae las tarifas de envío de un pedido desde un servicio web (por ejemplo, UPS). ¿Haría de este un servicio que acepta IOrder, o lo haría parte del objeto de dominio como Order.GetRates?
Casey Wilkins
Pasaría mucho tiempo averiguando cómo podría evitar un tirón sincrónico en primer lugar. Cuanto más extraiga datos de forma sincronizada y bloqueada, más frágil se volverá su diseño. Es por eso que CQRS es una alternativa tan atractiva.
Mark Seemann
4
Tu enlace al periódico de Greg está muerto. Pero todavía está disponible aquí . Y parece que esta es una versión más nueva.
BornToCode
1

Sé que esta es una publicación antigua, pero quería agregarla. La entidad de dominio no debería persistir incluso si pasa un repositorio abstraído en ctor. La razón por la que sugiero que esto no es simplemente porque viola SRP, también es contrario a la agregación de DDD. Permítame explicarle, DDD es adecuado para aplicaciones complejas con gráficos intrínsecamente profundos, por lo tanto, usamos raíces agregadas o compuestas para persistir los cambios en los "niños" subyacentes, por lo que cuando inyectamos persistencia en los niños individuales, violamos la relación que los niños tienen con el Raíz compuesta o agregada que debe estar "a cargo" del ciclo de vida o agregación. Por supuesto, la raíz compuesta o el agregado tampoco persiste en su propio gráfico. Otro es con la inyección de dependencias de objetos DDD y es que un objeto de dominio inyectado efectivamente no tiene estado hasta que se produce algún otro evento para hidratar su estado. Cualquier consumidor del código se verá obligado a iniciar o configurar el objeto de dominio antes de poder invocar un comportamiento comercial que viole la encapsulación.

usuario1538467
fuente