Creazione di un livello di astrazione sul livello ORM


12

Credo che se hai i tuoi repository usi un ORM che è già abbastanza estratto dal database.

Tuttavia, dove sto lavorando ora, qualcuno crede che dovremmo avere un livello che astragga l'ORM nel caso in cui vorremmo cambiare l'ORM in seguito.

È davvero necessario o è semplicemente un sacco di costi generali per creare un livello che funzionerà su molti ORM?

modificare

Giusto per dare maggiori dettagli:

  1. Abbiamo classe POCO ed Entity Class mappate con AutoMapper. Le classi di entità vengono utilizzate dal livello Repository. Il livello del repository utilizza quindi il livello aggiuntivo di astrazione per comunicare con Entity Framework.
  2. Il livello aziendale non ha in alcun modo un accesso diretto a Entity Framework. Anche senza il livello aggiuntivo di astrazione sull'ORM, questo deve utilizzare il livello di servizio che utilizza il livello repository. In entrambi i casi, il livello aziendale è totalmente separato dall'ORM.
  3. L'argomento principale è di poter cambiare ORM in futuro. Dal momento che è veramente localizzato all'interno del livello Repository, per me è già ben separato e non vedo perché è necessario un ulteriore livello di astrazione per avere un codice di "qualità".

3
livello aggiuntivo necessita di giustificazione, altrimenti viola YAGNI. In altre parole, qualcuno che crede di averne bisogno, ha l'onere di dimostrarlo
moscerino del

2
Posso capire il desiderio di un livello di dominio che espone solo un sottoinsieme desiderato di operazioni: gli ORM tendono ad essere un po 'troppo ampi di una superficie (diciamo che non si desidera consentire gli aggiornamenti a un'entità non diretta da un'altra entità contenente). Avere un tale strato di astrazione aiuta in questo.
Oded,

4
Probabilmente avrai bisogno di un secondo strato di astrazione per il primo strato di astrazione sopra l'ORM nel caso in cui desideri cambiare anche il primo strato.
David Peterman,

1
@David Mentre stiamo aggiungendo ridondanza, cambia tutti i tuoi if (booleani) in if (boolean == true) e se desideri rigurgitare più degli stessi, if (boolean == true == true ...) e così via
Brian

Risposte:


12

In questo modo sta la follia. È altamente improbabile che tu abbia mai bisogno di cambiare ORM. E se decidi di cambiare l'ORM, il costo della riscrittura delle mappature sarà una piccola frazione del costo per sviluppare e mantenere il tuo meta-ORM. Mi aspetto che tu possa scrivere alcuni script per fare il 95% del lavoro necessario per cambiare ORM.

I framework interni sono quasi sempre un disastro. Costruirne uno in previsione delle esigenze future è quasi un disastro garantito. I quadri di successo vengono estratti da progetti di successo, non costruiti in anticipo per soddisfare esigenze immaginarie.


12

L'ORM fornisce un'astrazione per il proprio livello dati in modo che sia indipendente dal proprio RDBMS, ma potrebbe non essere sufficiente per "sciogliere" il livello aziendale dal livello dati. In particolare, non si deve consentire agli oggetti associati alle tabelle RDBMS di "filtrare" direttamente nel livello aziendale.

Per lo meno, il tuo livello aziendale deve programmare a interfacce che i tuoi oggetti gestiti da ORM e mappati da tabella dal livello dati potrebbero potenzialmente implementare. Inoltre, potrebbe essere necessario creare un livello basato su interfaccia di creazione di query astratte per nascondere le funzionalità di query native del tuo ORM. L'obiettivo principale è quello di evitare di "inserire" qualsiasi particolare ORM nella soluzione oltre il suo livello dati. Ad esempio, potrebbe essere allettante creare stringhe HQL ( Hibernate Query Language ) nel livello aziendale. Tuttavia, questa decisione apparentemente innocente legherebbe il tuo livello aziendale a Hibernate, inchiodando così i livelli di business e di accesso ai dati; dovresti cercare di evitare questa situazione il più possibile.

EDIT: nel tuo caso, il livello aggiuntivo all'interno del repository è una perdita di tempo: in base al punto numero due, il tuo livello aziendale è sufficientemente isolato dal tuo repository. Fornire un isolamento aggiuntivo comporterebbe inutili complessità, senza ulteriori vantaggi.

Il problema con la costruzione di un ulteriore livello di astrazione all'interno del repository è che il particolare "marchio" di ORM determina il modo in cui interagisci con esso. Se costruisci un wrapper sottile che assomiglia al tuo ORM, ma che è sotto il tuo controllo, la sostituzione dell'ORM sottostante sarà all'incirca dura come sarebbe senza quel livello aggiuntivo. Se, d'altra parte, costruisci un livello che non assomiglia affatto al tuo ORM, allora dovresti mettere in discussione la tua scelta della tecnologia di mappatura relazionale agli oggetti.


Sono così felice che .NET abbia risolto questo problema inserendo l'oggetto query nella piattaforma. La porta .NET di Hibernate lo supporta anche.
Michael Brown,

2
@MikeBrown Sì, e .NET ha anche fornito due tecnologie ORM concorrenti, entrambe utilizzando la tecnologia LINQ!
dasblinkenlight,

@dasblinkenlight Ho aggiornato la domanda per darti ulteriori informazioni.
Patrick Desjardins,

Ultimamente, ho adottato l'approccio di far dipendere il livello aziendale da un livello dati basato su interfacce simili a mappe e set-like per memorizzare lo stato. Ora credo che quelle interfacce rappresentino abbastanza bene la preoccupazione per lo stato (ispirata allo stile funzionale della programmazione) e forniscano una buona separazione dall'ORM di scelta attraverso un'implementazione piuttosto sottile.
Beluchin,

2

UnitOfWork solitamente fornisce questa astrazione. È un posto che deve cambiare, i tuoi repository dipendono da esso tramite un'interfaccia. Se hai mai bisogno di cambiare O / RM, basta implementare un nuovo UoW su di esso. Uno e fatto.

A proposito, va oltre la semplice commutazione di O / RM, si pensi ai test unitari. Ho tre implementazioni UnitOfWork, una per EF, una per NH (perché in realtà ho DID per passare a metà progetto O / RM per un client che voleva il supporto Oracle) e una per la persistenza InMemory. La persistenza di InMemory era perfetta per i test unitari o anche per la prototipazione rapida prima che fossi pronto a mettere dietro un database.

Il framework è semplice da implementare. Per prima cosa hai la tua interfaccia IRepository generica

public interface IRepository<T>
  where T:class
{
  IQueryable<T> FindBy(Expression<Func<T,Bool>>filter);
  IQueryable<T> GetAll();
  T FindSingle(Expression<Func<T,Bool>> filter);
  void Add(T item);
  void Remove(T item);

}

E l'interfaccia IUnitOfWork

public interface IUnitOfWork
{
   IQueryable<T> GetSet<T>();
   void Save();
   void Add<T>(T item) where T:class;
   void Remove<T>(T item) where T:class;
}

Il prossimo è il repository di base (la tua scelta sull'opportunità o meno di essere astratta

public abstract class RepositoryBase<T>:IRepository<T>
  where T:class
{
   protected readonly IUnitOfWork _uow;

   protected RepositoryBase(IUnitOfWork uow)
   { 
      _uow=uow;
   }

   public IQueryable<T> FindBy(Expression<Func<T,Bool>>filter)
   {
      return _uow.GetSet<T>().Where(filter);
   }

   public IQueryable<T> GetAll()
   {
      return _uow.GetSet<T>();
   }

   public T FindSingle(Expression<Func<T,Bool>> filter)
   {
      return _uow.GetSet<T>().SingleOrDefault(filter);
   }

   public void Add(T item)
   {
      return _uow.Add(item);
   }

   public void Remove(T item)
   {
      return _uow.Remove(item);
   }
}

L'implementazione di IUnitOfWork è un gioco da ragazzi per EF, NH e In Memory. Il motivo per cui restituisco IQueryable, è per lo stesso motivo, ha ricordato Ayende nel suo post, il client può ulteriormente filtrare, ordinare, raggruppare e persino proiettare il risultato usando LINQ e si ottiene comunque il vantaggio di tutto ciò che viene fatto sul lato server.


1
Ma la domanda qui è determinare se quel layer sopra è utile o meno e dovrebbe essere il gatekeeper a tutti gli accessi ai dati.
Brian

Vorrei poter indicare il mio post sul blog sull'implementazione di Unit Of Work / Repository. Discute le preoccupazioni esatte dell'OP.
Michael Brown,

Dare un nome al layer non significa che sia necessario o utile.
Kevin Cline,

Nota secondo il PO, ha una mappatura aggiuntiva tra accesso ai dati e livello aziendale. Per me, i miei oggetti business e oggetti entità sono gli stessi. EF e NH forniscono incredibili API di mappatura in modo tale che la mappatura dei dati raramente (se mai) diventi un problema.
Michael Brown,

Come si traduce un'espressione arbitraria in una chiamata ORM efficiente? Non puoi semplicemente recuperare tutto e buttare via le righe che non corrispondono al filtro.
Kevin Cline,
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.