al deposito o non al deposito

9

Quando ho appreso per la prima volta di Domain Driven Design, sono stato anche introdotto nel repository e nell'unità di modelli di lavoro che una volta sembravano di prim'ordine per i ragazzi alla moda che lanciavano query SQL come cavemans contro i database. Più approfondisco l'argomento, più ho imparato che non sembrano più necessari a causa di ORM come EF e NHibernate che implementano sia l'unità di lavoro che i repository in un'unica API, chiamata sessione o contesto.

Ora non sono sicuro di cosa fare. Al repository o non al repository. Capisco davvero l'argomento secondo cui tali astrazioni che perdono solo complicano eccessivamente le cose mentre non aggiungono assolutamente nulla che possa semplificare l'accesso ai dati, tuttavia, non sembra giusto accoppiare ogni possibile aspetto della mia applicazione ad es. Entity Framework . Di solito, seguo alcune semplici linee guida:

  1. Il livello dominio è il cuore del sistema, contenente entità, servizi, repository ...
  2. Il livello infrastruttura fornisce implementazioni di interfacce di dominio di un problema infrastrutturale, ad es. file, database, protocolli ..
  3. Il livello applicazione ospita una root di composizione che collega le cose e orchestra tutto.

Le mie soluzioni di solito assomigliano a questo:

Domain.Module1
Domain.Module2
    IModule2Repo
    IModule2Service
    Module2
Infrastructure.Persistence
    Repositories
        EntityFrameworkRepositoryBase
MyApp
    Boostrapper
        -> inject EntityFrameworkRepositoryBase into IRepository etc.

Mantengo il mio livello di dominio pulito utilizzando un IRepository<'T> che è anche un problema relativo al dominio, non dipendente da qualcos'altro che mi dice come accedere ai dati. Quando ora realizzerei un'implementazione concreta di IModule2Service che richiede l'accesso ai dati, dovrei iniettare DbContext e con ciò accoppiarlo direttamente al livello dell'infrastruttura. ( Venendo al progetto di Visual Studio, questo può risultare davvero complicato a causa delle dipendenze circolari! )

Inoltre che cosa può essere un'alternativa ai depositari e fucktons of works ? CQRS? Come si può astrarre un puro quadro infrastrutturale?

    
posta Acrotygma 29.04.2014 - 13:06
fonte

2 risposte

2

L'ingegneria è tutta una questione di compromessi. E così lo sviluppo del software. In questo momento, credo che solo l'altra opzione, che sarebbe più semplice, sia quella di lavorare direttamente con ORM. Ma come hai detto tu, questo potrebbe bloccarti in un quadro di persistenza specifico.

Quindi devi chiederti "La complessità aggiuntiva vale il disaccoppiamento del tuo codice dalla persistenza". Ogni volta che sento dire che vogliono separare il loro codice dalla persistenza, chiedo "Quante volte nella tua carriera hai cambiato il tuo quadro di persistenza?"

I problemi che vedo con i repository sono che rendono più difficili le modifiche comuni. Per esempio. aggiungendo un nuovo modo di interrogare un aggregato. E fanno cambiamenti non comuni (presumibilmente) più facili. Per esempio. modifica dell'implementazione dei repository.

Poi c'è anche l'argomento di test unitario, al quale dico "Se il framework di persistenza non ti permette di prendere in giro un database, sia in memoria che localmente, allora non vale la pena usare il framework."

    
risposta data 01.05.2014 - 09:14
fonte
2

Sono pro-repository, anche se mi sono allontanato dai modelli di repository generici. Invece allineare i miei repository con la funzione aziendale che servono. I repository non sono finalizzati ad astrarre via l'ORM, in quanto non è qualcosa che mi aspetto di cambiare, e allo stesso tempo evito di rendere un repository troppo granulare. (I.e CRUD) Invece i miei repository servono da due a tre scopi chiave:

  • Recupero dati
  • Creazione dati
  • Cancellazione difficile

Per il recupero dei dati, il repository restituisce sempre IQueryable<TEntity> . Per la creazione dei dati restituisce TEntity. Il repository gestisce il mio filtro di livello base come lo stato di "attivazione" dell'autorizzazione per i sistemi che utilizzano pattern di eliminazione software e lo stato "corrente" per i sistemi che utilizzano dati storici. La creazione dei dati è responsabile solo per garantire che i riferimenti richiesti siano risolti e associati e che l'entità sia configurata e pronta per l'uso.

L'aggiornamento dei dati è responsabilità della logica aziendale che funziona con le entità in questione. Questo può includere cose come le regole di validazione. Non cerco di incapsularlo in un metodo di repository.

La cancellazione nella maggior parte dei miei sistemi è soft-delete quindi ricadrebbe sotto l'aggiornamento dei dati. (IsActive = false) Nel caso delle eliminazioni definitive questo sarebbe un one-liner nel repository.

Perché repository? Test-capacità. Certo, DbContext può essere deriso, ma è più semplice prendere in giro una classe che restituisce IQueryable<TEntity> . Inoltre suonano bene con il pattern UoW, personalmente utilizzo il pattern DbContextScope di Mehdime per estendere l'unità di lavoro al livello desiderato (Ie Controller in MVC) e consentire ai miei controller e classi di servizio helper di utilizzare i repository senza dover passare i riferimenti a il UoW / dbContext in giro. L'utilizzo di IQueryable significa che non è necessario utilizzare molti metodi wrapper nel repository e il codice può ottimizzare il modo in cui i dati verranno utilizzati. Ad esempio il repository non ha bisogno di esporre metodi come "Exists" o "Count", o tenta di avvolgere le entità con altri POCO nei casi in cui si vogliono sottogruppi di dati. Non hanno nemmeno bisogno di gestire le opzioni di caricamento impaziente per i dati correlati di cui potresti avere bisogno o meno. Passando IQueryable, il codice chiamante può:

.Any()
.Count()
.Include() // Generally avoided, instead I use .Select()
.Where()
.Select(x => new ViewModel or Anon. Type)
.Skip().Take()
.FirstOrDefault() / .SingleOrDefault() / .ToList()

Molto flessibile e da un test P.o.V. il mio repository mocked deve semplicemente restituire liste di oggetti entità popolate.

Come per i repository generici, mi sono spostato da questi per la maggior parte perché quando si finisce con un repository per tabella i controller / servizi finiscono con riferimenti a diversi repository per eseguire un'azione aziendale. Nella maggior parte dei casi solo uno o due di questi repository stanno effettivamente facendo operazioni di scrittura (a condizione che si stiano utilizzando le proprietà di navigazione correttamente) mentre il resto supporta quelli con Reads. Preferirei avere qualcosa come un repository di ordini che sia in grado di leggere e creare ordini e leggere qualsiasi ricerca pertinente, ecc. (Oggetti cliente leggeri, prodotti, ecc.) Come riferimento durante la creazione di un ordine, piuttosto che colpire 5 o 6 diversi repository. Potrebbe violare i puristi di DNRY, ma il mio argomento è che lo scopo del repository è quello di servire la creazione di ordini che include i riferimenti correlati. Non ho bisogno di andare a un Repository<Product> per ottenere prodotti dove per la base di un ordine ho solo bisogno di un'entità con una manciata di campi. Il mio OrderRepository potrebbe avere un metodo .GetProducts() che restituisce IQueryable<ProductSummary> che trovo più bello di un Repository<Product> che finisce con diversi metodi "Get" per provare e servire diverse aree delle esigenze dell'applicazione e / o qualche pass-in complesso espressione filtrante.

Scelgo un codice più semplice che sia facile da seguire, testare e ottimizzare. Può essere abusato in modi sbagliati, ma preferirei avere qualcosa in cui gli abusi siano facili da individuare e correggere piuttosto che cercare di "bloccare" il codice in un modo che non può essere abusato, fallire e poi avere qualcosa che è un incubo per arrivare a fare effettivamente ciò che il cliente paga per farlo fare alla fine. :)

    
risposta data 19.06.2017 - 15:15
fonte