Devo usare il repository nell'oggetto Domain o reinserire l'oggetto Domain nel livello di servizio?


10

Vengo da un mondo di script di transazione e sto appena iniziando a dare un'occhiata a DDD. Non sono sicuro del modo corretto di integrare un progetto DDD con la persistenza del database. Questo è quello che ho:

Una classe di servizio denominata OrganisationService la cui interfaccia contiene metodi per il recupero e il salvataggio di istanze di oggetti di dominio dell'organizzazione. L'organizzazione è una radice aggregata e ha altri dati ad essa correlati: membri e licenze. Prima DBContext del database EF6 viene utilizzato all'interno di OrganisationService per recuperare le entità OrganisationDB e le entità MemberDB e LicenseDB correlate. Tutti questi vengono trasformati nei loro equivalenti di classe di oggetti di dominio quando vengono recuperati da OrganisationService e caricati nell'oggetto di dominio Organizzazione. Questo oggetto si presenta così:

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

Non sto usando il modello di repository in OrganisationService ... Sto usando EF stesso come repository in quanto sembra che EF6 abbia ampiamente ridondato il repository ora.

A questo punto nella progettazione l'oggetto di dominio Organizzazione è anemico: sembra la classe Organizzazione EF POCO. La classe OrganisationService assomiglia molto a un repository!

Ora devo iniziare ad aggiungere la logica. Questa logica include la gestione di licenze e membri di un'organizzazione. Ora nei giorni degli script di transazione aggiungerei metodi in OrganisationService per gestire queste operazioni e chiamare in un repository per interagire con il DB, ma con DDD credo che questa logica dovrebbe essere incapsulata all'interno dell'oggetto di dominio dell'Organizzazione stesso ...

È qui che non sono sicuro di cosa dovrei fare: dovrò conservare questi dati nel database come parte della logica. Questo significa che dovrei usare DbContext all'interno dell'oggetto del dominio Organizzazione per fare questo? L'utilizzo del repository / EF all'interno dell'oggetto dominio è una cattiva pratica? In tal caso, dove appartiene questa persistenza?

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

Dovrei invece semplicemente mutare l'oggetto di dominio dell'organizzazione in memoria e quindi rimandarlo a OrganisationService per la persistenza? Quindi devo tenere traccia di ciò che è effettivamente cambiato sull'oggetto (che è ciò che EF fa ai suoi POCO! Sto pensando che EF non è solo un sostituto del repository, ma potrebbe anche essere il livello di dominio!)

Qualsiasi consiglio qui è apprezzato.

Risposte:


2

Puoi adottare entrambi gli approcci e farlo funzionare bene, ovviamente ci sono pro e contro.

Entity Framework è sicuramente destinato a soffocare le entità del tuo dominio. Funziona bene quando le entità del dominio e le entità dati sono le stesse classi. È molto più bello se puoi fare affidamento su EF per tenere traccia delle modifiche per te e chiamare solo context.SaveChanges()quando hai finito con il tuo lavoro transazionale. Significa anche che gli attributi di convalida non devono essere impostati due volte, una volta sui modelli di dominio e una volta sulle entità persistenti: elementi come [Required]o [StringLength(x)]possono essere controllati nella logica aziendale , consentendo di rilevare stati di dati non validi prima di provare a fare una transazione DB e ottenere unEntityValidationException. Infine, è veloce da programmare: non è necessario scrivere un layer di mappatura o un repository, ma è invece possibile lavorare direttamente con il contesto EF. È già un repository e un'unità di lavoro, quindi ulteriori livelli di astrazione non realizzano nulla.

Un aspetto negativo della combinazione del dominio e delle entità persistenti è che si ottiene un mucchio di [NotMapped]attributi sparsi in tutte le proprietà. Spesso, si vorranno proprietà specifiche del dominio che sono filtri di sola ricezione su dati persistenti, oppure sono impostati successivamente nella logica aziendale e non persistono nel database. Alcune volte, vorrai esprimere i tuoi dati nei tuoi modelli di dominio in un modo che non funziona molto bene con un database - ad esempio, quando usi enum, Entity li mapperà su una intcolonna - ma forse vuoi mappare ad un livello leggibile dall'uomo string, quindi non è necessario consultare una ricerca quando si esamina il database. Quindi si finisce con una stringproprietà che è mappata, unenumproprietà che non lo è (ma ottiene la stringa e le mappe per l'enum) e un'API che espone entrambi! Allo stesso modo, se si desidera combinare tipi complessi (tabelle) in contesti diversi, si può finire con un mappedOtherTableId e una ComplexTypeproprietà non mappata , entrambi esposti come pubblici nella propria classe. Questo può essere fonte di confusione per qualcuno che non ha familiarità con il progetto e gonfia inutilmente i tuoi modelli di dominio.

Più complessa è la mia logica / dominio aziendale, più restrittiva o ingombrante trovo che sia la combinazione del mio dominio con entità persistenti. Per progetti con scadenze brevi o che non esprimono un livello aziendale complesso, ritengo che l'utilizzo di entità EF per entrambi gli scopi sia appropriato e non è necessario sottrarre il dominio alla persistenza. Per i progetti che richiedono la massima facilità di estensione o che devono esprimere una logica molto complicata, penso che sia meglio separare i due e gestire la complessità di persistenza aggiuntiva.

Un trucco per evitare il problema di tracciare manualmente le modifiche dell'entità è quello di memorizzare l'ID entità persistente corrispondente nel modello di dominio. Questo può essere riempito automaticamente dal tuo livello di mappatura. Quindi, quando è necessario persistere una modifica a EF, recuperare l'entità persistente pertinente prima di eseguire qualsiasi mapping. Quindi, quando si mappano le modifiche, EF le rileverà automaticamente e sarà possibile chiamare context.SaveChanges()senza doverle rintracciare manualmente.

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

C'è qualche problema quando si combinano approcci diversi con diversa complessità dei dati? se hai qualcosa che si adatta perfettamente a DB, usa EF direttamente. Se hai qualcosa che non ha, quindi introdurre una mappatura / astrazione prima di utilizzare EF.
Euforico,

@Euforiche le differenze di dominio e db possono essere gestite nella mappatura. Non riesco a pensare a un caso d'uso in cui aggiungere due volte il dominio (non persistente e persistente) e gestire manualmente il rilevamento delle modifiche è meno lavoro dell'aggiunta di mappature per casi speciali. Puoi indicarmene uno? (Suppongo che usando NHibernate, forse EF è più limitato?)
Firo,

Risposta molto utile, pratica e istruttiva. Contrassegnare come risposta. Grazie!
MrLane,

3

non conosco EF6 ma altri ORM gestiscono questo in modo trasparente, quindi non dovrai aggiungere esplicitamente le licenze al contesto, le rileverà quando salveranno le modifiche. Quindi il metodo sarà fino a

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

e il codice che lo circonda

var someOrg = Load(orgId);

someOrg.AddLicenses(someLicences);

SaveChanges();

I miei oggetti di dominio non sono le Entità, quindi aggiungerli alla raccolta Licenze, che è solo un Elenco, non lo taglierà. La domanda non riguarda tanto EF ma in quanto è dove ha luogo la persistenza effettiva. Tutta la persistenza in EF deve avvenire nell'ambito di un contesto. La domanda è: a che cosa appartiene?
MrLane,

2
le entità di dominio e le entità persistenti possono / dovrebbero essere le stesse altrimenti si introduce un altro livello di astrazione che il 99% delle volte non aggiunge valore e il rilevamento delle modifiche viene perso.
Firo,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.