Depoya ya da Depoya değil


10

Etki Alanına Dayalı Tasarım'ı ilk öğrendiğimde, bir zamanlar veritabanlarına karşı cavemans gibi SQL sorguları atan havalı çocuklar için bir çentik gibi görünen depo ve çalışma desenleri ile tanıştım. Bu konuya ne kadar derin girdiğimde, EF ve NHibernate gibi hem iş birimini hem de depoları oturum veya bağlam adı verilen tek bir API'ye uygulayan ORM'ler nedeniyle artık gerekli görünmediklerini öğrendim .

Şimdi ne yapacağımdan emin değilim. Depoya ya da depoya değil. Veri erişimini basitleştirebilecek kesinlikle hiçbir şey eklemeden bu tür sızıntılı soyutlamaların sadece işleri aşırı derecede karmaşıklaştırdığı iddiasını gerçekten anlıyorum, ancak uygulamamın olası her yönünü örneğin Entity Framework ile birleştirmek doğru gelmiyor . Genellikle birkaç basit yönerge izlerim:

  1. Etki alanı katmanı, varlıklar, hizmetler, depolar içeren sistemin kalbidir ...
  2. Altyapı katmanı, dosya, veritabanı, protokoller gibi altyapı ile ilgili bir alan adı arabirimi uygulamaları sağlar.
  3. Uygulama katmanı, işleri birbirine bağlayan ve her şeyi düzenleyen bir kompozisyon köküne sahiptir.

Çözümlerim genellikle şöyle görünür:

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

IRepository<'T>Verilere nasıl erişeceğimi söyleyen başka bir şeye bağlı olmayan bir alan adı kaygısı olan alan adı katmanımı temiz tutarım . Şimdi IModule2Serviceveri erişimi gerektiren bunun somut bir uygulamasını yapacağım zaman , bunu enjekte etmek zorunda kalacaktım DbContextve bununla, bunu doğrudan altyapı katmanına bağlayacağım. ( Visual Studio projesine gelince, bu dairesel bağımlılıklar nedeniyle gerçekten zor olabilir! )

Buna ek olarak , işlerin depolarına ve fucktonlarına alternatif ne olabilir ? CQRS? Nasıl saf bir altyapı çerçevesi soyutlanır?


1
Bir kenara, DDD'de açıklandığı gibi 'Depo' dan bahsediyorsanız, EF ve nHibernate depolar değildir. Elbette, kısmen veri erişim mekanizmasını soyutlaştırıyorlar, ancak bir havuzda bundan çok daha fazlası var .
Eric King

Yanıtlar:


4

Mühendislik tamamen ödün vermek demektir. Yazılım geliştirme de öyle. Şu anda, sadece daha basit olan diğer seçeneğin doğrudan ORM ile çalışmak olduğuna inanıyorum. Ama dediğin gibi, bu seni belirli bir kalıcılık çerçevesine kilitleyebilir.

Bu yüzden kendinize şunu sormalısınız: "Ek karmaşıklık kodunuzun kalıcılıktan ayrılmasına değer mi?" İnsanların kodlarını kalıcılıktan ayırmak istediklerini her duyduğumda, "Kariyerinizde kaç kez kalıcılık çerçevenizi değiştirdiniz?"

Depolarda gördüğüm sorunlar, ortak değişiklikleri zorlaştırmalarıdır. Örneğin. bir toplama sorgulamak için yeni bir yol ekleme. Ve nadir görülen değişiklikleri (sözde) kolaylaştırırlar. Örneğin. depoların değişen uygulaması.

Sonra da "Kalıcılık çerçevesi bir veritabanını bellekte veya yerel olarak taklit etmenize izin vermiyorsa, o zaman çerçeveyi kullanmaya değmez" dediğim birim sınama argümanı da vardır.


1
Deponun amacı, uygulamayı kalıcılıktan ayırmaktır ve karmaşıklık yoktur. Ve kalıcılık ayrıntılarını en az bir kez değiştirin, çünkü db erişimi, bellek depolarında kullandığım noktaya kadar kodladığım son şey. Ayrıca, doğrudan bir kalıcılık detayı kullandığınızda çok ince bir tuzak var. İş nesneleriniz agnostik yerine onunla uyumlu olacak şekilde tasarlanır. Bunun nedeni, zengin, düzgün bir şekilde kapsüllenmiş bir varlığın (bazı çirkin) geçici çözümleri olmadan herhangi bir depolama alanından (bellek hariç) doğrudan geri yüklenememesidir
MikeSW

Toplam kökü veya herhangi bir varlığı sorgulamak için yeni bir yol ekleme hakkında CQRS bu yüzden ortaya çıktı. Şahsen, Depoyu etki alanı amaçları için saklıyorum ve gerçek sorgulama için sorgu işleyicileri kullanıyorum. Ve bu işleyiciler db'ye sıkı sıkıya bağlıdır ve yaptıkları işte çok etkilidirler.
MikeSW

3

Genel veri havuzuyum, ancak genel veri havuzu kalıplarından uzaklaştım. Bunun yerine depolarımı hizmet ettikleri iş işleviyle hizalıyorum. Depolar ORM'i soyutlamayı amaçlamamaktadır, çünkü bu değiştirmeyi beklediğim bir şey değildir ve aynı zamanda bir havuzu çok ayrıntılı yapmaktan kaçınırım. (Yani CRUD) Depolarım iki ila üç temel amaca hizmet ediyor:

  • Veri alma
  • Veri oluşturma
  • Zor silme

Veri alımı için, havuz her zaman geri döner IQueryable<TEntity>. Veri oluşturmak için TEntity döndürür. Havuz, yumuşak silme kalıpları kullanan sistemler için yetkilendirme "etkin" durumu ve geçmiş verileri kullanan sistemler için "geçerli" durumu gibi temel düzey filtrelerimi işler. Veri oluşturma sadece gerekli referansların çözülmesini ve ilişkilendirilmesini ve varlığın kurulmuş ve kullanıma hazır olmasını sağlamaktan sorumludur.

Veri güncelleme, söz konusu varlıklar ile çalışan iş mantığının sorumluluğundadır. Bu, doğrulama kuralları gibi şeyleri içerebilir. Bunu depo yöntemiyle kapsüllemeye çalışmıyorum.

Sistemlerimin çoğundaki silme işlemi yumuşak silinir, bu nedenle veri güncellemesi altına girer. (IsActive = false) Sıkı silmeler durumunda bu, Depodaki tek satırlık olacaktır.

Neden depolar? Test-yeteneği. Elbette, DbContext alay edilebilir, ancak dönen bir sınıfı alay etmek daha kolaydırIQueryable<TEntity>. Ayrıca UoW deseni ile de güzel oynuyorlar, şahsen Mehdime'in DbContextScope modelini istediğim seviyede (MVC'deki Kontrolörler) kapsamak için kullanıyorum ve denetleyicilerim ve yardımcı servis sınıflarımın referansları geçmesine gerek kalmadan depoları kullanmasına izin veriyorum etrafında UoW / dbContext. IQueryable kullanmak, depoda çok fazla sarma yöntemine ihtiyacınız olmadığı anlamına gelir ve kodunuz verilerin nasıl kullanılacağını optimize edebilir. Örneğin, deponun "Var" veya "Say" gibi yöntemleri göstermesi veya alt veri kümeleri istediğiniz durumlarda varlıkları diğer POCO'larla sarmaya çalışması gerekmez. İhtiyaç duyabileceğiniz veya ihtiyaç duymayabileceğiniz ilgili veriler için istekli yükleme seçeneklerini kullanmaları bile gerekmez. IQueryable'ı geçerek, çağrı kodu şunları yapabilir:

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

Çok esnek ve test edilmiş bir PoV'dan alaycı depomun yalnızca doldurulmuş varlık nesneleri listelerini döndürmesi gerekiyor.

Genel depolara gelince, çoğu zaman bunlardan uzaklaştım çünkü tablo başına bir havuz bulduğunuzda, denetleyicileriniz / hizmetleriniz bir iş eylemi yapmak için birkaç havuza referanslarla sonuçlanır. Çoğu durumda, bu depolardan yalnızca bir veya iki tanesi gerçekten yazma işlemleri yapıyor (navigasyon özelliklerini düzgün kullanmanız şartıyla), geri kalanı Okumaları destekliyorsa. 5 veya 6 farklı depoya çarpmak yerine, bir sipariş oluştururken referans için ilgili siparişleri okuyabilen ve ilgili aramaları (hafif müşteri nesneleri, ürünler, vb.) Okuyabilen OrdersRepository gibi bir şey olmasını tercih ederim. DNRY saflarını ihlal edebilir, ancak bununla ilgili iddiam, deponun amacının ilgili referansları içeren Emirlerin yaratılmasına hizmet etmektir.Repository<Product>sipariş bazında sadece birkaç avuç dolusu alana sahip bir işletmeye ihtiyacım olduğu ürünleri almak için. Benim OrderRepository, daha iyi denemek ve uygulama ihtiyaçlarının farklı alanlarına hizmet ve / veya bazı karmaşık geçiş filtreleme ifadesi hizmet için birkaç "Get" yöntemleri olan bir daha güzel bulmak .GetProducts()dönen bir yöntem olabilir .IQueryable<ProductSummary>Repository<Product>

Takip etmesi, test etmesi ve ayarlaması kolay basit kodları tercih ederim. Kötü şekillerde istismar edilebilir, ancak kötüye kullanımın tespit edilmesi ve düzeltilmesi kolay olan bir şeyi, kodu kötüye kullanılamayacak, başarısız olamayacak ve " müşterinin sonunda yapması için ödediği şeyi yapmak için bir kabus. :)

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.