Depo ve Hizmet Katmanı arasındaki fark nedir?


191

OOP Tasarım Desenlerinde, Depo Deseni ile Hizmet Katmanı arasındaki fark nedir?

Bir ASP.NET MVC 3 uygulaması üzerinde çalışıyorum ve bu tasarım desenleri anlamaya çalışıyorum, ama beynim henüz elde değil ... !!

Yanıtlar:


330

Havuz Katmanı, veri erişimi üzerinde ek düzeyde soyutlama sağlar. Yazmak yerine

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

veritabanından tek bir öğe almak için depo arabirimini kullanırsınız

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

ve arayın Get(id). Havuz katmanı temel CRUD işlemlerini ortaya koyar .

Hizmet katmanı, depo kullanan iş mantığını ortaya çıkarır. Örnek hizmet şöyle görünebilir:

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

İken List()tüm kullanıcılar, depo getiri yöntemi ListUsers()IUserService sadece olanları geri dönebilirler, kullanıcı erişimi vardır.

ASP.NET MVC + EF + SQL SUNUCU, ben bu iletişim akışı var:

Görünümler <- Denetleyiciler -> Hizmet katmanı -> Depo katmanı -> EF -> SQL Server

Servis katmanı -> Depo katmanı -> EF Bu bölüm modellerde çalışır.

Görünümler <- Kontrolörler -> Servis katmanı Bu bölüm görünüm modellerinde çalışır.

DÜZENLE:

/ Orders / ByClient / 5 için akış örneği (belirli bir müşteri için siparişi görmek istiyoruz):

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

Bu sipariş servisi için arayüz:

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

Bu arayüz görünüm modelini döndürür:

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

Bu arayüz uygulamasıdır. Görünüm modeli oluşturmak için model sınıflarını ve havuzu kullanır:

public class OrderService : IOrderService
{
     IRepository<Client> _clientRepository;
     public OrderService(IRepository<Client> 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...}     
             }
         );
     }
}

2
@Sam Striano: Yukarıda gördüğünüz gibi, IRepository'm IQueryable döndürüyor. Bu, daha sonra değil, hizmet katmanında ek durumlarda koşulların ve ertelenmiş yürütmenin eklenmesine izin verir. Evet, bir derleme kullanıyorum, ancak tüm bu sınıflar farklı ad alanlarına yerleştiriliyor. Küçük projelerde çok sayıda meclis oluşturmak için hiçbir neden yoktur. Ad alanı ve klasör ayırma iyi çalışıyor.
LukLed

81
Neden görünüm modellerini hizmete iade etmeliyim? birden fazla istemciniz (mobil / web) olması gerekiyorsa hizmetin taklit edeceği varsayılmıyor mu? Bu durumda, görünüm modeli farklı platformlardan farklı olabilir
Ryan

12
@Ryan ile kabul edilen hizmet katmanı, varlık nesnesini veya varlık nesneleri koleksiyonunu döndürmelidir (IQueryable değil). Sonra ui varlık Automapper tarafından SomeViewModel ile eşler.
Eldar

5
@Duffp: Her varlık için depo oluşturmanız gerekmez. Sen jenerik uygulanmasını ve bağlama kullanabilirsiniz IRepository<>etmek GenericRepository<>için IOC kütüphanede. Bu cevap çok eski. Bence en iyi çözüm, tüm depoları bir sınıfta birleştirmektir UnitOfWork. Her türden depo ve denilen bir yöntem içermelidir SaveChanges. Tüm depolar bir EF bağlamını paylaşmalıdır.
LukLed

2
viewmodel'i servis katmanından döndürmek yerine, DTO'yu döndürmeli ve bunu automapper kullanarak viewModels'e dönüştürmelisiniz .. bazen aynıdırlar, teşekkür etmediklerinde YGTNI uyguladınız. Gerek "
hanzolo

41

Carnotaurus'un dediği gibi, depo, verilerinizi depolama biçiminden iş nesnelerinize eşlemekten sorumludur. Depolama biriminden hem depolama alanına hem de veri okuma ve yazma (silme, güncelleme) işlemlerini gerçekleştirmelidir.

Hizmet katmanının amacı ise, kodun yeniden kullanımını ve endişelerin ayrılmasını teşvik etmek için iş mantığını tek bir yerde kapsüllemektir. Asp.net MVC siteleri inşa ederken bu benim için pratikte benim için ne anlama geliyor

[Denetleyici], [depoyu] arayan [Hizmetleri] çağırır

Yararlı bulduğum bir ilke, kontrolörlerde ve depolarda mantığı minimumda tutmaktır.

Kontrolörlerde beni KURU tutmaya yardımcı olduğu için. Aynı filtreyi veya mantığı başka bir yerde kullanmam çok yaygındır ve denetleyiciye yerleştirirsem yeniden kullanamam.

Depolarda, daha iyi bir şey ortaya çıktığında depolama alanımı (veya ORM) değiştirebilmek istiyorum. Havuzda mantığım varsa, depoyu değiştirdiğimde bu mantığı yeniden yazmam gerekiyor. Depom yalnızca IQueryable döndürürse ve hizmet filtrelemeyi diğer yandan yaparsa, yalnızca eşlemeleri değiştirmem gerekir.

Örneğin, kısa bir süre önce Linq-To-Sql depolarımın birkaçını EF4 ile değiştirdim ve bu ilkeye sadık kaldığım dakikalar birkaç dakika içinde değiştirilebilir. Biraz mantığım olduğunda bunun yerine saatler meselesiydi.


Sana katılıyorum Mikael. Aslında, aynı senaryo benim teknik blog freecodebase.com uyguladım ve bu uygulamada ilk kod yaklaşımını kullandım. Kaynak kodu buradan da indirilebilir.
Toffee

Mevcut bir MVC uygulamasında bir havuz modeli uygulama genel konusunu araştırıyorum. Aktif Kayıt benzeri bir ORM ve diğer Rails / Laravel sözleşmeleriyle ısmarlama bir çerçevedir ve şu anda yaptığım iş için bazı mimari problemleri vardır. Karşılaştığım bir şey, depoların "ViewModel'leri, DTO'ları veya sorgu nesnelerini döndürmemesi" yerine depo nesnelerini döndürmesi gerektiğidir. Hizmetleri gibi yöntemler ile depo nesneleri ile etkileşim nerede onBeforeBuildBrowseQueryve sorguyu değiştirmek için sorgu oluşturucu kullanabilirsiniz düşünüyorum.
calligraphic-io

@ Toffee, bağlantınız koptu, lütfen güncelleyin, bu uygulama için kaynak koduna ihtiyacım var.
Hamza Khanzada

24

Kabul edilen cevap (ve yüzlerce kez onaylanmış) büyük bir kusura sahiptir. Ben yorumda bu noktalamak istedim ama sadece orada 30 şey yorum kadar burada işaret gömülü alacak.

Bu şekilde oluşturulan bir kurumsal uygulamayı devraldım ve ilk tepkim WTH miydi? Servis katmanındaki ViewModels? Sözleşmeyi değiştirmek istemedim çünkü yıllar süren kalkınma süreci içine girmişti, bu yüzden ViewModels'e geri dönmeye devam ettim. Çocuk biz WPF kullanmaya başladığında bir kabusa dönüştü. Biz (devs takımı) hep şunu söylüyordu: Hangi ViewModel? Gerçek olan (WPF için yazdığımız) veya hizmetler mi? Bir web uygulaması için yazıldılar ve hatta kullanıcı arayüzünde düzenlemeyi devre dışı bırakmak için IsReadOnly bayrağı vardı . Büyük, büyük kusur ve hepsi tek bir kelime yüzünden: ViewModel !!

Aynı hatayı yapmadan önce, yukarıdaki hikayeme ek olarak başka nedenler de var:

Bir ViewModel'i hizmet katmanından döndürmek çok hayır. Bu demek oluyor ki:

  1. Bu hizmetleri kullanmak istiyorsanız, MVVM kullanıyor olmanız daha iyi ve burada kullanmanız gereken ViewModel. Ah!

  2. Hizmetler, bir yerde bir kullanıcı arayüzünde görüntüleneceklerini varsayıyorlar. Web hizmetleri veya windows hizmetleri gibi UI olmayan bir uygulama tarafından kullanılırsa ne olur?

  3. Bu gerçek bir ViewModel bile değil. Gerçek bir ViewModel'de gözlemlenebilirlik, komutlar vb. Vardır. Bu sadece kötü bir ada sahip bir POCO'dur. (İsimlerin neden önemli olduğuna dair yukarıdaki hikayeme bakın.)

  4. Tüketici uygulama daha iyi bir sunum katmanı (ViewModels bu katman tarafından kullanılır) ve C # daha iyi anlamak. Başka Ah!

Lütfen, yapma!


3
Tartışmaya eklenmediğini bilsem de bu konuda yorum yapmak zorunda kaldım: "Bu sadece kötü bir isme sahip bir POCO." << - Bu bir T-Shirt'te güzel görünecekti! :) :)
Mephisztoe

8

Genellikle varlıklarınızı doldurmak için iskele olarak bir havuz kullanılır - bir hizmet katmanı dışarı çıkar ve bir istek kaynağı oluşturur. Hizmet katmanınızın altına bir depo yerleştirmeniz olasıdır.


Yani EF4 kullanan bir ASP.NET MVC uygulamasında, böyle bir şey olabilir: SQL Server -> EF4 -> Depo -> Hizmet Katmanı -> Model -> Denetleyici ve tersi?
Sam

1
Evet, deponuz EF4'ten hafif varlıklar almak için kullanılabilir; ve hizmet katmanınız bunları özel bir model yöneticisine (senaryonuzdaki Model) geri göndermek için kullanılabilir. Denetleyici bunu yapmak için özel model yöneticinize çağrı yapar ... Mvc 2/3 için bloguma hızlı bir şekilde bakın. Şemalarım var.
CarneyCode

Sadece açıklama için: Senaryonuzdaki EF4, Modelin benim diyagramlarımda olduğu ve Senaryonuzdaki Modelin benim diyagramlarımdaki özel model yöneticileri olduğu
CarneyCode

5

Veri havuzu katmanı veritabanına erişmek için uygulanır ve veritabanındaki CRUD işlemlerini genişletmeye yardımcı olur. Hizmet katmanı uygulamanın iş mantığından oluşur ve veri havuzu ile ilgili belirli mantığı uygulamak için depo katmanını kullanabilir. Bir uygulamada, ayrı bir havuz katmanına ve hizmet katmanına sahip olmak daha iyidir. Ayrı depo ve hizmet katmanlarına sahip olmak kodu daha modüler hale getirir ve veritabanını iş mantığından ayırır.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.