Domanda

Ho bisogno di aiuto con trovare la mia radice di aggregazione e di confine.

Ho 3 Entità: Piano, PlannedRole e PlannedTraining. Ogni piano può includere molti PlannedRoles e PlannedTrainings.

Soluzione 1: In un primo momento ho pensato che piano è la radice di aggregazione perché PlannedRole e PlannedTraining non hanno senso fuori dal contesto di un piano. Sono sempre all'interno di un piano. Inoltre, abbiamo una regola aziendale che dice che ogni piano può avere un massimo di 3 e 5 PlannedRoles PlannedTrainings. Così ho pensato nominando il Piano come la radice di aggregazione, posso rispettare questa invariante.

Tuttavia, abbiamo una pagina di ricerca in cui l'utente cerca piani. I risultati mostrano alcune proprietà del piano stesso (e nessuna delle sue PlannedRoles o PlannedTrainings). Ho pensato che se devo caricare dell'intero aggregato, si avrebbe un sacco di spese generali. Ci sono quasi 3000 i piani e ognuno può avere un paio di bambini. Caricamento tutti questi oggetti insieme e poi ignorando PlannedRoles e PlannedTrainings nella pagina di ricerca non ha senso per me.

Soluzione 2: Ho appena realizzato che l'utente vuole altre 2 pagine di ricerca dove possono cercare per i ruoli pianificate o previste Training. Questo mi ha fatto capire che stanno cercando di accedere a questi oggetti in modo indipendente e "fuori" il contesto del Piano. Così ho pensato che era sbagliato sul mio progetto iniziale ed è così che mi si avvicinò con questa soluzione. Così, ho pensato di avere 3 aggregati qui, 1 per ogni entità.

Questo approccio mi permette di cercare per ogni entità indipendente e risolve anche il problema di prestazioni in soluzione 1. Tuttavia, utilizzando questo approccio non riesco a far rispettare l'invariante ho menzionato in precedenza.

C'è anche un altro invariante che indica un piano può essere modificato solo se è di un certo status. Quindi, non dovrei essere in grado di aggiungere qualsiasi PlannedRoles o PlannedTrainings ad un piano che non è in quello stato. Ancora una volta, non posso applicare tale invariante con il secondo approccio.

Qualche consiglio sarebbe molto apprezzato.

Saluti, Mosh

È stato utile?

Soluzione

ho avuto problemi simili con questo quando si progetta il mio modello e questa domanda che credo potrebbe aiutare, soprattutto per quanto riguarda il primo punto.

DDD - Come implementare repository prestazioni elevate per la ricerca .

Quando si tratta di cercare io non lavoro con il 'modello', invece mi sono specializzato repository di ricerca che il ritorno 'Sintesi' oggetti ... vale a dire 'PlanSummary'. Questi non sono altro che oggetti informativi (potrebbe essere pensato più come reporting) e non sono utilizzati in un certo senso transazionale - Non ho nemmeno li definisco nella mia libreria di classi del modello. Creando questi archivi e tipologia posso implementare alte prestazioni query di ricerca che possono contenere raggruppati dati (ad esempio un conteggio PlannedTraining) senza caricare tutte le associazioni dell'aggregato in memoria. Una volta che un utente seleziona una di queste sintesi oggetti nell'interfaccia utente, posso quindi utilizzare l'ID per recuperare l'oggetto modello reale ed eseguire operazioni di transazione e impegnarsi modifiche.

Quindi per la vostra situazione che sarebbe fornire questi repository di ricerca specializzati per tutti e tre i soggetti, e quando un utente desidera eseguire e azioni contro uno, prendere sempre l'aggregato Piano che appartiene a.

In questo modo si hanno le ricerche performanti pur mantenendo il vostro unico aggregato con le invarianti richiesti.

Modifica - Esempio:

OK, quindi credo che l'attuazione è soggettiva, ma questo è come ho affrontato la situazione nella mia richiesta, utilizzando un 'membro del team' aggregato come esempio. Esempio scritto in C #. Ho due librerie di classi:

  • Modello
  • Segnalazione

La biblioteca modello contiene la classe di aggregazione, con tutte le invarianti applicate, e la biblioteca di riferimento contiene questo semplice classe:

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 di riferimento contiene anche la seguente interfaccia:

public interface ITeamMemberSummaryRepository : IReportRepository<TeamMemberSummary>
{

}

Questa è l'interfaccia che il livello di applicazione (che nel mio caso sembra essere servizi WCF) consumerà e risolverà l'attuazione tramite il mio contenitore CIO (Unità). L'IReportRepository vive in una libreria Infrastructure.Interface, come fa un ReportRepositoryBase base. Così ho due diversi tipi di repository nel mio sistema - repository aggregati, e la segnalazione repository ...

Poi in un'altra libreria, Repositories.Sql, ho l'attuazione:

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()
    {

    }
}

E allora, nel mio livello di applicazione:

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

        return repository.FindAll(criteria);

    }

Poi nel client, l'utente può selezionare uno di questi oggetti, ed eseguire un'azione contro uno nel livello di applicazione, ad esempio:

    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);
        }
    }

Altri suggerimenti

problema reale qui è violazione SRP. La vostra parte di ingresso di applicazione va in conflitto con uscita.

Stick con prima soluzione (piano == radice aggregato). Artificialmente promuovere imprese (o gli oggetti di valore pari) a radici di aggregazione distorce tutto il modello di dominio e rovina tutto.


Si potrebbe voler controllare i cosiddetti CQRS ( interrogazione responsabilità di comando segregazione) architettura, che si adatterebbe perfettamente per risolvere questo problema particolare. Ecco un esempio di app da Mark Nijhof. Qui è bello 'getting-iniziato' lista.

Questo è il punto centrale di CQRS le architetture:. Segregare i comandi - che modificano il dominio - da query - che si limitano a dare una visione di stato di dominio, in quanto requisito per comandi e le richieste sono così diversi

è possibile trovare una buona introduzione su questi blog:

e su molti altri blog (tra cui miniera )

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top