2010-04-01 17 views
6

Necesito ayuda para encontrar mi raíz y límite agregados.DDD: Agregue raíces

Tengo 3 entidades: Plan, PlannedRole y PlannedTraining. Cada Plan puede incluir muchos PlannedRoles y PlannedTrainings.

Solución 1: Al principio pensé que Plan es la raíz agregada porque PlannedRole y PlannedTraining no tienen sentido fuera del contexto de un Plan. Siempre están dentro de un plan. Además, tenemos una regla comercial que dice que cada Plan puede tener un máximo de 3 PlannedRoles y 5 PlannedTrainings. Así que pensé al nominar el Plan como la raíz agregada, puedo hacer cumplir esta invariante.

Sin embargo, tenemos una página de búsqueda en la que el usuario busca Planes. Los resultados muestran algunas propiedades del Plan mismo (y ninguno de sus PlannedRoles o PlannedTrainings). Pensé que si tenía que cargar todo el agregado, tendría muchos gastos generales. Hay casi 3000 planes y cada uno puede tener algunos niños. Cargar todos estos objetos juntos y luego ignorar PlannedRoles y PlannedTrainings en la página de búsqueda no tiene sentido para mí.

Solución 2: Me acabo de dar cuenta de que el usuario quiere 2 páginas de búsqueda más en las que pueda buscar funciones planeadas o entrenamientos planificados. Eso me hizo darme cuenta de que están tratando de acceder a estos objetos de forma independiente y "fuera del contexto" de Plan. Entonces pensé que estaba equivocado sobre mi diseño inicial y así es como se me ocurrió esta solución. Entonces, pensé tener 3 agregados aquí, 1 para cada Entidad.

Este enfoque me permite buscar cada Entidad de forma independiente y también resuelve el problema de rendimiento en la solución 1. Sin embargo, utilizando este enfoque no puedo aplicar la invariante que mencioné anteriormente.

También hay otra invariante que establece que un Plan solo se puede cambiar si tiene un determinado estado. Por lo tanto, no debería poder agregar PlannedRoles ni PlannedTrainings a un plan que no esté en ese estado. Nuevamente, no puedo hacer cumplir esta invariante con el segundo enfoque.

Cualquier consejo sería muy apreciado.

Saludos, Mosh

Respuesta

9

que estaba teniendo problemas similares con esto al diseñar mi modelo y de hecho esta pregunta que creo que podría ayudarle, especialmente en relación con el primer punto.

DDD - How to implement high-performing repositories for searching.

Cuando se trata de buscar no trabajo con el 'modelo', en cambio tengo repositorios de búsqueda especializados que devuelven objetos 'Resumen' ... es decir 'PlanSummary'. Estos no son más que objetos de información (podrían considerarse más como informes) y no se usan en sentido transaccional; ni ​​siquiera los defino en mi biblioteca de clases modelo. Al crear estos repositorios y tipos dedicados, puedo implementar consultas de búsqueda de alto rendimiento que pueden contener datos agrupados (como un recuento de formación planificada) sin cargar todas las asociaciones del agregado en la memoria. Una vez que un usuario selecciona uno de estos objetos de resumen en la interfaz de usuario, puedo usar la identificación para buscar el objeto del modelo real y realizar operaciones transaccionales y confirmar cambios.

Por lo tanto, para su situación, proporcionaría estos repositorios de búsqueda especializados para las tres entidades, y cuando un usuario desea realizar una acción contra una, siempre obtiene el agregado del Plan al que pertenece.

De esta manera usted tiene las búsquedas de rendimiento mientras mantiene su agregado individual con las invariantes requeridas.

Editar - Ejemplo:

OK, así que supongo que la implementación es subjetiva, pero esta es la forma en que he manejado en mi solicitud, mediante un agregado 'TeamMember' como un ejemplo. Ejemplo escrito en C#. Tengo dos bibliotecas de clases:

  • Modelo
  • informes

La biblioteca modelo contiene la clase agregada, con todos los invariantes cumplir, y la biblioteca de informes contiene esta clase simple:

public class TeamMemberSummary 
{ 
    public string FirstName { get; set; } 

    public string Surname { get; set; } 

    public DateTime DateOfBirth { get; set; } 

    public bool IsAvailable { get; set; } 

    public string MainProductExpertise { get; set; } 

    public int ExperienceRating { get; set; } 
} 

La biblioteca de informes también contiene la siguiente interfaz:

public interface ITeamMemberSummaryRepository : IReportRepository<TeamMemberSummary> 
{ 

} 

Esta es la interfaz que consumirá la capa de aplicación (que en mi caso pasa a ser servicios WCF) y resolverá la implementación a través de mi contenedor de IoC (Unity). El IReportRepository vive en una biblioteca Infrastructure.Interface, al igual que una base ReportRepositoryBase. Así que tienen dos tipos diferentes de repositorio en mi sistema - repositorios agregado, y repositorios de información ...

Luego, en otra biblioteca, Repositories.Sql, tengo la ejecución:

public class TeamMemberSummaryRepository : ITeamMemberSummaryRepository 
{ 
    public IList<TeamMemberSummary> FindAll<TCriteria>(TCriteria criteria) where TCriteria : ICriteria 
    { 
     //Write SQL code here 

     return new List<TeamMemberSummary>(); 
    } 

    public void Initialise() 
    { 

    } 
} 

Así pues, en mi capa de aplicación:

public IList<TeamMemberSummary> FindTeamMembers(TeamMemberCriteria criteria) 
    { 
     ITeamMemberSummaryRepository repository 
      = RepositoryFactory.GetRepository<ITeamMemberSummaryRepository>(); 

     return repository.FindAll(criteria); 

    } 

Luego, en el cliente, el usuario puede seleccionar uno de estos objetos, y llevar a cabo una acción contra uno en la capa de aplicación, por ejemplo:

public void ChangeTeamMembersExperienceRating(Guid teamMemberID, int newExperienceRating) 
    { 
     ITeamMemberRepository repository 
      = RepositoryFactory.GetRepository<ITeamMemberRepository>(); 

     using(IUnitOfWork unitOfWork = UnitOfWorkFactory.CreateUnitOfWork()) 
     { 
      TeamMember teamMember = repository.GetByID(teamMemberID); 

      teamMember.ChangeExperienceRating(newExperienceRating); 

      repository.Save(teamMember); 
     } 
    } 
+0

Hola David, Gracias Para tu respuesta. ¡Parece ser una gran idea! Sí, he leído algo similar en algún lugar antes, pero el autor no profundizó en los detalles de esto. Creo que los resultados de la búsqueda se parecen más a un informe y no vale la pena leer todo el Agregado para informar, ya que los datos son de solo lectura. No vamos a hacer ningún cambio, por lo tanto, no se deben aplicar invariantes, por lo tanto, no se requiere agregado. ¡Gran idea! :) ¿Tiene alguna implementación de muestra? ¿O conoces alguna página web que hable más sobre esto? – Mosh

+0

OK, agregué un ejemplo a mi respuesta. ¡Espero que te dé algunas ideas! Me temo que no conozco ningún enlace, ya que se me ocurrió esta implementación después de publicar esa pregunta :) P.S. Si esto le ha ayudado a resolver su problema, no olvide marcar esto como la respuesta: D –

+0

¡Gran implementación! Eres un genio David! Gracias hombre. – Mosh

4

El problema real aquí es la violación de SRP. Su parte de entrada de la aplicación entra en conflicto con la salida.

Se pega con la primera solución (Plan == raíz agregada). Artificialmente promover entidades (o incluso objetos de valor) para agregar raíces distorsiona todo el modelo de dominio y arruina todo.


Es posible que desee echa un vistazo a la llamada CQRS (consulta de comandos responsabilidad segregación), la arquitectura que encajaría perfectamente para solucionar este problema en particular. Here's an example app por Mark Nijhof. Aquí hay una buena lista de 'getting-started'.

+0

Desde entonces descubrí CQRS y sí, resuelve este problema exacto. –

3

Este es el punto central de CQRS arquitecturas: Separar Comandos - que modifican el dominio - a partir de consultas - que se limitan a dar una visión del estado de dominio, porque el requisito de Comandos y consultas son tan diferentes.

se puede encontrar una buena introducción en estos blogs:

y en muchos otros blogs (incluyendo mine)

+0

¡No es justo! ¡Yo fui primero! : P –

+0

Vaya, no había notado la segunda parte de su respuesta: -S sorry – thinkbeforecoding