¿Debo usar el repositorio en el Objeto de dominio o devolver el Objeto de dominio a la Capa de servicio?

10

Vengo de un mundo de script de transacción y estoy empezando a echar un vistazo a DDD. No estoy seguro de la forma correcta de integrar un diseño DDD con la persistencia de la base de datos. Esto es lo que tengo:

Una clase de servicio llamada OrganisationService cuya interfaz contiene métodos para recuperar y guardar instancias de objetos de dominio de la Organización. La organización es una raíz agregada y tiene otros datos relacionados con ella: miembros y licencias. La primera base de datos EF6 DBContext se usa dentro del OrganisationService para recuperar las entidades OrganisationDB y las entidades relacionadas MemberDB y LicenseDB. Todos estos se transforman en sus equivalentes de clase de objeto de dominio cuando son recuperados por OrganisationService y se cargan en el objeto de dominio de Organización. Este objeto se ve así:

public class Organisation
{
    public IList<Member> Members { get; set; }
    public IList<License> Licenses { get; set; }
}

No estoy usando el patrón Repository en OrganisationService ... Estoy usando EF en sí como Repository ya que parece que EF6 ha hecho que el repositorio sea redundante ahora.

En este punto del diseño, el objeto de dominio de Organización está anémico: se parece a la clase de Organización EF POCO. ¡La clase OrganisationService se parece mucho a un repositorio!

Ahora necesito comenzar a agregar lógica. Esta lógica incluye administrar las licencias y los miembros de una organización. Ahora, en los días del script de transacción, agregaría métodos al OrganisationService para manejar estas operaciones y llamaría a un Repositorio para interactuar con la base de datos, pero con DDD creo que esta lógica debería encapsularse dentro del objeto de dominio de la Organización ...

Aquí es donde no estoy seguro de lo que debería estar haciendo: tendré que conservar estos datos en la base de datos como parte de la lógica. ¿Esto significa que debo usar DbContext dentro del objeto de dominio de la Organización para hacer esto? ¿Está mal utilizar el repositorio / EF dentro del objeto de dominio? Si es así, ¿a dónde pertenece esta persistencia?

public class Organisation
{
    public IList<Member> Members { get; set; }
    public IList<License> Licenses { get; set; }

    public void AddLicensesToOrganisation(IList<License> licensesToAdd)
    {
        // Do validation/other stuff/

        // And now Save to the DB???
        using(var context = new EFContext())
        {
            context.Licenses.Add(...
        }

        // Add to the Licenses collection in Memory
        Licenses.AddRange(licensesToAdd);
    }
}

¿Debería simplemente mutar el objeto de dominio de la Organización en la memoria y luego devolverlo al OrganisationService para persistencia? Luego tengo que rastrear lo que realmente cambió en el objeto (¡que es lo que EF le hace a sus propios POCO! ¡Estoy sintiendo que EF no es solo un reemplazo del repositorio, sino que también podría ser la capa de dominio!)

Cualquier orientación aquí es apreciada.

MrLane
fuente

Respuestas:

2

Puede adoptar cualquier enfoque y hacer que funcione bien, por supuesto, hay ventajas y desventajas.

Entity Framework definitivamente tiene la intención de cubrir tus entidades de dominio. Funciona bien cuando sus entidades de dominio y entidades de datos son las mismas clases. Es mucho mejor si puede confiar en EF para realizar un seguimiento de los cambios por usted y simplemente llamar context.SaveChanges()cuando haya terminado su trabajo transaccional. También significa que sus atributos de validación no tienen que establecerse dos veces, una vez en sus modelos de dominio y una vez en sus entidades persistentes, cosas como [Required]o [StringLength(x)]pueden verificarse en su lógica de negocios , lo que le permite detectar estados de datos no válidos antes de intentar hacer una transacción de base de datos y obtener unEntityValidationException. Finalmente, es rápido de codificar: no necesita escribir una capa de mapeo o repositorio, sino que puede trabajar directamente con el contexto EF. Ya es un repositorio y una unidad de trabajo, por lo que capas adicionales de abstracción no logran nada.

Una desventaja de combinar tu dominio y entidades persistentes es que terminas con un montón de [NotMapped]atributos dispersos por todas tus propiedades. A menudo, querrá propiedades específicas del dominio que sean filtros de solo obtención en datos persistentes, o que se establezcan más adelante en su lógica de negocios y que no persistan nuevamente en su base de datos. Algunas veces, querrá expresar sus datos en sus modelos de dominio de una manera que no funcione muy bien con una base de datos, por ejemplo, cuando use enumeraciones, la entidad los asignará a una intcolumna, pero tal vez desee asignarlos para que sean legibles por humanos string, por lo que no necesita consultar una búsqueda al examinar la base de datos. Luego terminas con una stringpropiedad que está mapeada, unenumpropiedad que no lo es (pero obtiene la cadena y se asigna a la enumeración), y una API que expone ambos. Del mismo modo, si desea combinar tipos complejos (tablas) en contextos, puede terminar con un mappedOtherTableId y una ComplexTypepropiedad no asignada , los cuales están expuestos como públicos en su clase. Esto puede ser confuso para alguien que no está familiarizado con el proyecto e hincha innecesariamente sus modelos de dominio.

Cuanto más complejo es mi dominio / lógica de negocios, más restrictivo o engorroso me resulta combinar mi dominio y las entidades persistentes. Para proyectos con plazos cortos, o que no expresan una capa empresarial compleja, creo que usar entidades EF para ambos propósitos es apropiado, y no hay necesidad de abstraer su dominio de su persistencia. Para proyectos que necesitan la máxima facilidad de extensión, o que necesitan expresar una lógica muy complicada, creo que es mejor separarlos y lidiar con la complejidad de persistencia adicional.

Un truco para evitar el problema de realizar un seguimiento manual de los cambios de su entidad es almacenar la ID de entidad persistente correspondiente en su modelo de dominio. Esto puede ser llenado automáticamente por su capa de mapeo. Luego, cuando necesite persistir un cambio de nuevo a EF, recupere la entidad persistente relevante antes de realizar cualquier mapeo. Luego, cuando asigne los cambios, EF los detectará automáticamente y podrá llamar context.SaveChanges()sin tener que rastrearlos a mano.

public class OrganisationService
{
    public void PersistLicenses(IList<DomainLicenses> licenses) 
    {
        using (var context = new EFContext()) 
        {
            foreach (DomainLicense license in licenses) 
            {
                var persistedLicense = context.Licenses.Find(pl => pl.Id == license.PersistedId);
                MappingService.Map(persistedLicense, license); //Right-left mapping
                context.Update(persistedLicense);
            }
            context.SaveChanges();
        }
    }
}
NWard
fuente
¿Hay algún problema al combinar diferentes enfoques con diferente complejidad de datos? Si tiene algo que se correlaciona bien con DB, simplemente use EF directamente. Si tiene algo que no es así, introduzca un mapeo / abstracción antes de usar EF.
Eufórico
@Euphoric las diferencias de dominio y db se pueden manejar en la asignación. No puedo pensar en un caso de uso en el que agregar el dominio dos veces (no persistente y persistente) y manejar el seguimiento de cambios a mano sea menos trabajo que agregar asignaciones para casos especiales. ¿Puedes señalarme uno? (Supongo que usando NHibernate, ¿quizás EF es más limitado?)
Firo
Respuesta muy útil, práctica e informativa. Marcado como la respuesta. ¡Gracias!
MrLane
3

No sé EF6, pero otros ORM manejan esto de manera transparente, por lo que no tendrá que agregar las licencias al contexto explícitamente, las detectará al guardar los cambios. Entonces el método se reducirá a

public void AddLicenses(IEnumerable<License> licensesToAdd)
{
    // Do validation/other stuff/
    // Add to the Licenses collection in Memory
    Licenses.AddRange(licensesToAdd);
}

y el código a su alrededor

var someOrg = Load(orgId);

someOrg.AddLicenses(someLicences);

SaveChanges();
Firo
fuente
Mis objetos de dominio no son las Entidades, por lo que agregarlos a la colección de Licencias, que es solo una Lista, no lo cortará. Sin embargo, la pregunta no se trata tanto de EF como de dónde tiene lugar la persistencia real. Toda persistencia en EF debe tener lugar dentro de un ámbito de contexto. La pregunta es ¿a dónde pertenece esto?
MrLane
2
las entidades de dominio y las entidades persistentes pueden / deberían ser las mismas, de lo contrario, se introduce otra capa de abstracción que el 99% de las veces no agrega valor y se pierde el seguimiento de cambios.
Firo