Differenza tra deposito e livello di servizio?

Nei modelli di progettazione OOP, qual è la differenza tra il modello di deposito e un livello di servizio?

Sto lavorando su un’app ASP.NET MVC 3 e sto cercando di capire questi schemi di progettazione, ma il mio cervello non lo sta ancora … ancora !!

Il livello del repository ti offre un ulteriore livello di astrazione sull’accesso ai dati. Invece di scrivere

var context = new DatabaseContext(); return CreateObjectQuery().Where(t => t.ID == param).First(); 

per ottenere un singolo elemento dal database, si utilizza l’interfaccia del repository

 public interface IRepository { IQueryable List(); bool Create(T item); bool Delete(int id); T Get(int id); bool SaveChanges(); } 

e chiama Get(id) . Il livello del repository espone le operazioni CRUD di base.

Il livello di servizio espone la logica aziendale, che utilizza il repository. Il servizio di esempio potrebbe essere simile a:

 public interface IUserService { User GetByUserName(string userName); string GetUserNameByEmail(string email); bool EditBasicUserData(User user); User GetUserByID(int id); bool DeleteUser(int id); IQueryable ListUsers(); bool ChangePassword(string userName, string newPassword); bool SendPasswordReminder(string userName); bool RegisterNewUser(RegisterNewUserModel model); } 

Mentre il metodo List() del repository restituisce tutti gli utenti, ListUsers() di IUserService potrebbe restituire solo quelli a cui l’utente ha accesso.

In ASP.NET MVC + EF + SQL SERVER, ho questo stream di comunicazione:

Viste <- Controller -> Livello servizio -> Livello repository -> EF -> SQL Server

Livello di servizio -> Livello di deposito -> EF Questa parte opera sui modelli.

Viste <- Controller -> Livello di servizio Questa parte opera sui modelli di vista.

MODIFICARE:

Esempio di stream per / Orders / ByClient / 5 (vogliamo vedere l’ordine per un cliente specifico):

 public class OrderController { private IOrderService _orderService; public OrderController(IOrderService orderService) { _orderService = orderService; // injected by IOC container } public ActionResult ByClient(int id) { var model = _orderService.GetByClient(id); return View(model); } } 

Questa è l’interfaccia per il servizio ordini:

 public interface IOrderService { OrdersByClientViewModel GetByClient(int id); } 

Questa interfaccia restituisce il modello di vista:

 public class OrdersByClientViewModel { CientViewModel Client { get; set; } //instead of ClientView, in simple project EF Client class could be used IEnumerable Orders { get; set; } } 

Questa è l’implementazione dell’interfaccia. Utilizza le classi di modelli e il repository per creare il modello di visualizzazione:

 public class OrderService : IOrderService { IRepository _clientRepository; public OrderService(IRepository clientRepository) { _clientRepository = clientRepository; //injected } public OrdersByClientViewModel GetByClient(int id) { return _clientRepository.Get(id).Select(c => new OrdersByClientViewModel { Cient = new ClientViewModel { ...init with values from c...} Orders = c.Orders.Select(o => new OrderViewModel { ...init with values from o...} } ); } } 

Come ha detto Carnotaurus, il repository è responsabile della mapping dei dati dal formato di archiviazione ai tuoi oggetti di business. Dovrebbe gestire sia la modalità di lettura e scrittura dei dati (eliminazione, aggiornamento) da e verso l’archiviazione.

L’objective del livello di servizio è invece quello di incapsulare la logica aziendale in un unico luogo per promuovere il riutilizzo del codice e le separazioni delle preoccupazioni. Ciò che questo in genere significa per me nella pratica quando si costruiscono siti ASP.net MVC è che ho questa struttura

[Controller] chiama [servizio (s)] chi chiama [repository (ies)]

Un principio che ho trovato utile è mantenere la logica al minimo nei controller e negli archivi.

Nei controller è perché mi aiuta a mantenermi ASCIUTTO. È molto comune che ho bisogno di usare lo stesso filtro o logica da qualche altra parte e se lo metto nel controller non posso riutilizzarlo.

Nei repository è perché voglio essere in grado di sostituire il mio storage (o ORM) quando qualcosa di meglio arriva. E se ho una logica nel repository ho bisogno di riscrivere questa logica quando cambio il repository. Se il mio repository restituisce solo IQueryable e il servizio esegue il filtro d’altro canto, dovrò solo sostituire i mapping.

Ad esempio, recentemente ho sostituito alcuni dei miei repository Linq-To-Sql con EF4 e quelli in cui ero rimasto fedele a questo principio potevano essere sostituiti in pochi minuti. Dove avevo una logica, invece, era questione di ore.

Solitamente un repository viene utilizzato come scaffolding per popolare le tue quadro: un livello di servizio si spegne e genera una richiesta. È probabile che tu inserisca un repository sotto il tuo livello di servizio.

La risposta accettata (e moltiplicata per centinaia di volte) ha un grosso difetto. Volevo farlo notare nel commento, ma verrà solo seppellito lì sotto in 30 commenti, quindi sottolineo qui.

Ho rilevato un’applicazione aziendale che è stata costruita in quel modo e la mia reazione iniziale è stata WTH ? ViewModels nel livello di servizio? Non volevo cambiare la convenzione perché erano passati anni di sviluppo, quindi ho continuato a restituire ViewModels. Boy è diventato un incubo quando abbiamo iniziato a utilizzare WPF. Noi (il team di sviluppatori) dicevamo sempre: quale ViewModel? Quello vero (quello che abbiamo scritto per il WPF) o quello dei servizi? Sono stati scritti per un’applicazione Web e avevano persino il flag IsReadOnly per disabilitare la modifica nell’interfaccia utente. Maggiore, grande difetto e tutto a causa di una sola parola: ViewModel !!

Prima di commettere lo stesso errore, ecco alcuni altri motivi oltre alla mia precedente storia:

Restituire un ViewModel dal livello di servizio è un enorme no no. È come dire:

  1. Se si desidera utilizzare questi servizi è meglio utilizzare MVVM e qui è il ViewModel che è necessario utilizzare. Ahia!

  2. I servizi stanno supponendo che verranno visualizzati in un’interfaccia utente da qualche parte. Che cosa succede se viene utilizzato da un’applicazione non dell’interfaccia utente come servizi Web o servizi Windows?

  3. Questo non è nemmeno un vero ViewModel. Un vero ViewModel ha osservabilità, comandi ecc. Questo è solo un POCO con un brutto nome. (Vedi la mia storia sopra per il motivo per cui i nomi contano.)

  4. L’applicazione che consuma meglio è un livello di presentazione (i ViewModel sono usati da questo livello) e capisce meglio C #. Un altro Ouch!

Per favore, non farlo!