domain-driven-design - significado - domain driven design pdf
Patrón de repositorio: cómo lazy Lazy Load? o, ¿debería dividir este Agregado? (4)
¿Qué tal dividir las responsabilidades en un editor propietario y un editor miembro?
Sin conocer su dominio, me imagino que tendrían diferentes responsabilidades; por ejemplo, EditorOwner podría ser bastante rico (y podría ser la raíz agregada), pero es posible que el Proyecto solo necesite conocer una cantidad limitada de sus miembros, por lo que el objeto EditorMiembro puede ser bastante liviano.
Estos objetos de dominio también pueden estar relacionados con los usuarios, pero eso estaría en otro contexto.
¿Eso ayuda a las cosas, o simplemente lo hace más complicado?
Tengo un modelo de dominio que tiene el concepto de Editor y Proyecto.
Un Editor posee varios Proyectos, y un Proyecto no solo tiene un propietario del Editor, sino también un número de miembros del Editor. Por lo tanto, un Editor también tiene una serie de Proyectos "unidos".
Estoy tomando un enfoque DDD para modelar esto y usar el patrón Repositorio para la persistencia. Sin embargo, todavía no logro entender el patrón lo suficiente como para determinar cómo debo hacer esto.
Estoy trabajando en la suposición de que el Editor y el Proyecto están potencialmente en el mismo agregado, con la raíz como Editor. Por lo tanto, puedo obtener un Editor y luego enumerar sus Proyectos, y desde allí enumerar a los Editores miembros de los Proyectos.
Sin embargo, si solo puedo recuperar Editores de mi repositorio, ¿no significa que tengo que cargar todos los Proyectos del repositorio cuando obtengo el Editor que los posee? Y si quiero cargar perezosamente los Editores miembros, ¿el Proyecto también necesita una referencia al repositorio?
Alternativamente, si divido el agregado y tengo un repositorio de Editor y un repositorio de proyecto, ¿cómo debo manejar una transacción entre los dos, como cuando un nuevo proyecto se agrega a un editor? Por ejemplo:
Editor e = new Editor("Editor Name");
editorRepository.Add(e);
Project p = e.CreateProject("Project Name");
projectRepository.Add(p); // These two lines
editorRepository.Save(e); // should be atomic
¿Estoy malinterpretando la intención del patrón Repositorio?
Depende de las necesidades de su aplicación. Si es un gran problema cargar todos los Proyectos para un Editor dado, entonces pruebe un patrón de carga diferido como un Proxy Virtual .
En cuanto a la carga lenta de los editores miembros de un proyecto, si usa el Proxy virtual, no veo ningún problema para inyectar el proxy con el EditorRepositorio ya que no considero que el proxy sea parte del dominio.
Si divide el Agregado, puede investigar el patrón Unidad de trabajo como una solución a la atomicidad. Sin embargo, este problema no es exclusivo de DDD y estoy seguro de que existen otras soluciones para el comportamiento transaccional.
Aquí tienes 2 relaciones diferentes, una para la propiedad y otra para la membresía.
La relación de propiedad es simple de uno a muchos (un propietario para cada proyecto). La relación de membresía es de muchos a muchos (muchos Editores por proyecto, muchos proyectos por editor).
Puede proporcionar una propiedad Owner en la clase Project y proporcionar un método en ProjectRepository para que todos los proyectos sean propiedad de un Editor específico.
Para las muchas relaciones, proporcione una propiedad Members en la clase Project y un método en ProjectRepository para obtener todos los proyectos que contengan el Editor especificado como miembro.
También parece que los editores y los proyectos son entidades, probablemente dividiría el agregado, pero tal vez esos términos tengan un significado específico en su contexto que los haga subentidades de un agregado.
¿Estoy malinterpretando la intención del patrón Repositorio?
Voy a decir "sí", pero debes saber que tanto yo como todas las personas con las que he trabajado hemos pedido lo mismo por la misma razón ... "No estás pensando en la cuarta dimensión, Marty".
Simplifiquemos un poco y nos apeguemos primero a los constructores en lugar de a los métodos Crear:
Editor e = new Editor("Editor Name");
e = editorRepository.Add(e);
Project p = new Project("Project Name", e);
p = projectRepository.Add(p);
Debajo, el repositorio de su proyecto siempre está almacenando un propietario válido ( p.EditorId
) en los datos del proyecto a medida que se crea, y como sea que vuelva a poblar los proyectos de un editor, estará allí. Es por eso que es una buena práctica poner todas las propiedades requeridas en los constructores. Si no quiere pasar todo el objeto, solo lo hará e.Id
Y si quiero cargar perezosamente los Editores miembros, ¿el Proyecto también necesita una referencia al repositorio?
Ahora, en cuanto a cómo volver a llenar los proyectos de un editor bajo demanda, tiene un par de opciones según lo que está buscando. Straight Repository dice que quieres:
IEnumerable<Project> list = projectRepository.GetAllProjects()
.Where(x => x.editorId == e.Id);
¿Pero dónde ponerlo? No dentro de Project, o Editor, tienes razón, o tendrán que acceder a repositorios y eso no es bueno. El fragmento anterior está ligeramente acoplado, pero no es reutilizable por sí mismo. Acaba de llegar a los límites del Patrón de repositorio.
El siguiente es un Adapter Layer para su aplicación, con una fuente compartida de repositorios ( StaticServiceWrapper
) y algún tipo de objeto EditorAdapter (o Aggregate o lo que sea que los llame) o ahora puede mezclar en métodos de extensión que pueden hablar con cualquier y todos los repositorios necesarios con fluidez. No lo hice exactamente de esta manera en un sistema de producción, sino para mostrarle un ejemplo conciso:
public static class Aggregators
{
// one to one, easy
public static Editor GetOwner(this Project p)
{
return StaticServiceWrapper.editorRep.GetEditorById(p.editorId);
}
// one to many, medium
public static IEnumerable<Project> GetProjects(this Editor e)
{
return StaticServiceWrapper.projectRep.GetAllProjects()
.Where(x => x.editorId == e.Id);
}
// many to many, harder
public static IEnumerable<Editor> GetMembers(this Project p)
{
var list = StaticServiceWrapper.projectMemberMap.GetAllMemberMaps()
.Where(x => x.projectId == p.projectId);
foreach ( var item in list )
yield return StaticServiceWrapper.editorRep.GetEditorById(item.editorId);
}
}
Básicamente, una vez que termine GetAll, GetById, Add, Update, Remove Object Repository, debe dejar las asociaciones por sí solas y avanzar en la jerarquía de objetos / capas a las partes divertidas como Adapters and Caches and Business Logic ( "Oh , mi! " ).