DDD Toplamları bir Web Uygulamasında gerçekten iyi bir fikir midir?


40

Domain Driven Design'a dalıyorum ve karşılaştığım bazı kavramlar yüzey üzerinde çok anlamlı, ama onları daha fazla düşündüğümde bunun gerçekten iyi bir fikir olup olmadığını merak etmem gerekiyor.

Örneğin, agrega kavramı mantıklı. Etki alanı modelinin tamamı ile uğraşmanıza gerek kalmaması için küçük sahiplik alanları yaratırsınız.

Ancak, bunu bir web uygulaması bağlamında düşündüğümde, küçük veri alt kümelerini geri almak için sık sık veritabanına vuruyoruz. Örneğin, bir sayfa yalnızca sipariş sayısını listeleyebilir, siparişi açmak ve sipariş kimliğini görmek için linkler tıklayabilirsiniz.

Ben sağ anlayış Agregalar olsam, genellikle üyelerini içerecektir bir OrderAggregate dönmek için depo desen kullanmak istiyorsunuz GetAll, GetByID, Delete, ve Save. Tamam, kulağa hoş geliyor. Fakat...

Tüm siparişlerimi listelemek için GetAll'ı çağırırsam, bana bu kalıbın tüm toplu bilgiler listesinin iade edilmesini, siparişlerin, sipariş satırlarının vb. (sadece başlık bilgisi).

Bir şey mi eksik? Yoksa burada kullanacağınız bir miktar optimizasyon var mı? İhtiyacınız olmadığında, tüm bilgilerin bir araya getirilmesini savunan birisinin hayal edemeyeceğimi hayal edemiyorum.

Kuşkusuz, biri deponuzda olduğu gibi yöntemler yaratabilir GetOrderHeaders, ancak bu ilk olarak depo gibi bir kalıp kullanma amacını ortadan kaldırıyor gibi görünmektedir.

Birisi bunu benim için açıklayabilir mi?

DÜZENLE:

Çok daha fazla araştırmadan sonra, bu bağlantının kesilmesinin saf bir Depo modelinin çoğu insanın bir Depo olduğunu düşündüğünden farklı olduğunu düşünüyorum.

Fowler, veri deposunu toplama anlambilimi kullanan ve genellikle bellekte tutulan bir veri deposu olarak tanımlar. Bu, tüm bir nesne grafiğini oluşturmak anlamına gelir.

Evans, Depoyu Toplam Kökler içerecek şekilde değiştirir ve bu nedenle depo yalnızca Toplamadaki nesneleri desteklemek için depolanır.

Çoğu kişi, depoları yalnızca istediğiniz veriyi elde etmek için yöntemler oluşturduğunuz, yüceltilmiş Veri Erişim Nesneleri olarak düşünmektedir. Bu, Fowler'in Kurumsal Uygulama Mimarisi Modellerinde açıklandığı gibi niyet olarak görünmüyor.

Yine de diğerleri depoyu, öncelikle test etmeyi ve alay etmeyi kolaylaştırmak ya da ısrarı sistemin geri kalanından ayırmak için kullanılan basit bir soyutlama olarak düşünürler.

Sanırım cevap, bunun ilk düşündüğümden çok daha karmaşık bir kavram olduğu.


4
“Sanırım cevap, bunun ilk düşündüğümden çok daha karmaşık bir kavram olduğu.” Bu çok doğru.
quentin-starin

durumunuz için, yalnızca talep edildiğinde verileri seçici olarak alan ve önbelleğe alan toplu kök nesne için bir proxy oluşturabilirsiniz
Steven A. Lowe

Benim önerim, kök toplam ilişkilerinde tembel yük uygulamaktır. Böylece çok fazla nesne yüklemeden köklerin bir listesini alabilirsiniz.
Juliano

4
Neredeyse 6 yıl sonra, hala iyi bir soru. Kırmızı kitaptaki bölümü okuduktan sonra derim ki: topaklarınızı çok büyük yapmayın. Etki alanınızdan bazı üst düzey konseptler seçmek ve bunları Hepsine Hükmedecek Kök olarak ilan etmek caziptir, ancak DDD daha küçük toplamları savunur. Ve yukarıda tanımladığınız gibi verimsizlikleri azaltır.
CPP. Senkfuss

Alanlarınız, etki alanı için doğal ve etkiliyken olabildiğince küçük olmalıdır (bir meydan okuma!). Ayrıca, depolarınızın oldukça özel yöntemlere sahip olması mükemmel derecede iyi ve arzu edilir .
Timo

Yanıtlar:


30

Etki Alanı Modelinizi ve topluları sorgulama için kullanmayın.

Aslında, sormak istedikleriniz, bundan kaçınmak için bir dizi ilke ve kalıp oluşturulduğuna dair yeterince yaygın bir sorudur. Buna CQRS denir .


2
@Mystere Adam: Hayır, olduğu gereken asgari verilerin sağlanması için. Ayrılmış bir okuma modelinin en büyük amaçlarından biri budur. Aynı zamanda bazı eşzamanlılık sorunlarını çözme konusunda yardımcı olur. DDY'ye uygulandığında CQRS'nin birçok avantajı vardır.
quentin-starin

2
@ Mystere: Bağlantılı olduğum makaleyi okursanız çok özleyeceğinize şaşırdım. "Sorgu (raporlama)" başlıklı bölüme bakınız: "Bir uygulama veri talep ederken ... bu işlem Query katmanına yapılan tek bir çağrıda yapılmalı ve bunun karşılığında gerekli tüm verileri içeren tek bir DTO alacaktır. .. Bunun nedeni, verinin normal olarak etki alanı davranışından çok daha fazla sorgulanmasıdır, bu nedenle bunu optimize ederek sistemin algılanan performansını artıracaksınız. "
quentin-starin

4
Bu nedenle , bir CQRS sistemindeki verileri okumak için bir Depo kullanmıyoruz. Bir sorguyu kapsüllemek için basit bir sınıf yazardık (uygun olan veya gerekli olan teknolojiyi kullanarak, genellikle düz ADO.Net veya Linq2Sql veya SubSonic burada uygun olanları kullanarak); verileri bir DDD Deposundaki tüm normal katmanlara sürükleyerek. Etki alanına bir komut göndermek istiyorsak, yalnızca bir Toplama almak için bir Depo kullanırız.
quentin-starin

9
“İhtiyacınız olmadığında, tüm bilgilerin bir araya getirilmesini savunan birinin kimseyi hayal edemeyeceğini düşünemiyorum.” Bu ifadede tam olarak doğru olduğunuzu söylemeye çalışıyorum. İhtiyacınız olmadığında tüm bilgileri toplamı almayın. DDD'ye uygulanan CQRS'nin özü budur. Sorgulamak için bir toplamaya ihtiyacınız yok. Verileri farklı bir mekanizma ile alın ve tutarlı bir şekilde yapın.
quentin-starin

2
@qes Aslında, en iyi çözüm DDD'yi sorgular için kullanmamaktır (okunuyor) :) Fakat hala Command bölümünde DDD kullanıyorsunuz, yani veri depolamak veya güncellemek için. Bu yüzden, size bir sorum var, veri tabanındaki verileri güncellemeniz gerektiğinde, her zaman Varlıklarla Depoları kullanıyor musunuz? Diyelim ki sütunda yalnızca bir küçük değeri (bir tür anahtarla) değiştirmek zorundasınız, hala Uygulama katmanında tüm Varlık'ı yüklüyorsunuz, bir değeri (özellik) değiştiriyor ve ardından tüm Varlığı tekrar DB'ye kaydediyor musunuz? Biraz fazla overkill?
Andrew,

8

Domain Driven Design'daki depo modelini en iyi şekilde kullanmak için mücadele ettim ve hala mücadele ediyorum. Şimdi ilk kez kullandıktan sonra, aşağıdaki uygulamaları gördüm:

  1. Bir havuz basit olmalı; yalnızca etki alanı nesnelerini depolamak ve bunları almaktan sorumludur. Diğer tüm mantık fabrikalar ve alan hizmetleri gibi diğer nesnelerde olmalıdır.

  2. Bir havuz, toplama kökleri hafızasında bulunan bir koleksiyon gibi davranır.

  3. Havuz genel bir DAO değil, her havuzun kendine özgü ve dar bir arayüzü var. Bir havuzda genellikle koleksiyona etki alanı bakımından arama yapmanızı sağlayan belirli bulma yöntemleri bulunur (örneğin: bana X kullanıcısı için tüm açık siparişleri verin). Havuzun kendisi genel bir DAO yardımı ile uygulanabilir.

  4. İdeal olarak, bulucu yöntemleri yalnızca toplam kökleri döndürür. Bu verimsizse, tam olarak ihtiyacınız olanı içermesinden ziyade salt okunur değer nesnelerini döndürebilir (bu değer nesneleri de etki alanı cinsinden ifade edilebilirse bir artı olsa da). Son çare olarak, depo alt kümeleri veya toplu bir kökün alt kümelerini toplamak için de kullanılabilir.

  5. Bunun gibi seçenekler, kullanılan teknolojilere bağlıdır, çünkü alan modelinizi kullanılan teknolojilerle en verimli şekilde ifade etmenin bir yolunu bulmanız gerekir.


Kesinlikle kesin bir konu. Özellikle iki ayrı ve ayrı teoriyi tek bir pratiğe birleştirdiğinde teoriyi pratiğe dönüştürmek zor.
Sebastian Patten

6

GetOrderHeaders yönteminizin, deponun amacını hiç yitirdiğini sanmıyorum.

DDD, (diğer şeylerin yanı sıra) toplam kök yoluyla ihtiyacınız olanı almanızı sağlamakla ilgilenir (örneğin, bir OrderDetailsRepository'nüz olmaz), ancak sözünü ettiğiniz şekilde sizi sınırlamaz.

OrderHeader bir Etki Alanı kavramıysa, bu şekilde tanımlamanız ve bunları almak için uygun depo yöntemlerine sahip olmanız gerekir. Yaparken doğru toplam kökten geçtiğinden emin olman yeterli.


Belki buradaki kavramları karıştırıyorum, ancak depo modeline ilişkin anlayışım kalıcılık alandan ayırmak, kalıcılık için standart bir arayüz kullanarak. Belirli bir özellik için özel yöntemler eklemeniz gerekiyorsa, bu işleri tekrar birleştirmek gibi görünüyor.
Erik Funkenbusch

1
Kalıcılık mekanizması etki alanından ayrıştırılır, ancak devam ettirilen şey değil. "Sipariş Başlıklarını burada listelemeliyiz" gibi şeyler söylerseniz, Etki Alanınızdaki OrderHeader'ı modellemeniz ve bunları depodan almanız için bir yol sağlamanız gerekir.
Eric King,

Ayrıca, “ısrar için standart arayüz” e takılmayın. Tüm olası uygulamalar için yeterli olacak genel bir depo deseni diye bir şey yoktur. Her uygulama standart "GetById", "Save", vb. Ötesinde birçok depo yöntemine sahip olacaktır. Bu yöntemler başlangıç ​​noktasıdır, bitiş noktası değil.
Eric King,

4

DDD'yi kullanmam "saf" DDD olarak kabul edilmeyebilir, ancak DDD'yi kullanarak bir DB veri deposuna karşı aşağıdaki gerçek dünya stratejilerini uyarladım.

  • Bir toplu kökün ilişkili bir havuzu var
  • İlişkilendirilmiş havuz yalnızca bu toplu kök tarafından kullanılır (halka açık değildir)
  • Havuz sorgu çağrıları içerebilir (örneğin GetAllActiveOrders, GetOrderItemsForOrder)
  • Hizmet, havuzun ve diğer karmaşık olmayan işlemlerin halka açık bir alt kümesini gösterir (örn. Parayı bir banka hesabından diğerine aktarma, LoadById, Search / Find, CreateEntity, vb.).
  • Kök -> Servis -> Havuz yığınını kullanıyorum. Bir DDD hizmetinin yalnızca bir Kurumun kendi kendine cevap veremediği herhangi bir şey için kullanılması gerektiğini varsayalım (örneğin, LoadById, TransferMoneyFromAccountToAccount), ama gerçek dünyada, diğer CRUD ile ilgili servislere (Kaydet, Sil, Sorgu) da dahil olma eğilimindeyim. Kök, bunlara "cevap ver / gerçekleştir" yapabilmelidir. Varlığa başka bir toplam kök hizmetine erişim sağlama konusunda yanlış bir şey olmadığını unutmayın! Bununla birlikte, bir servise dahil edilmeyeceğinizi (GetOrderItemsForOrder) eklemeyeceğinizi, ancak Toplama Kökü'nün bunu kullanabilmesi için depoya dahil edeceğini unutmayın. Bir hizmetin, depo gibi açık sorguları açığa çıkarmaması gerektiğini unutmayın.
  • Genellikle etki alanı modelinde (arabirim aracılığıyla) Özet Havuzu tanımlarım ve ayrı bir somut uygulama sağlarım. Etki alanı modelinde bir hizmeti tam olarak kullanım için somut bir havuza enjekte ediyorum.

** Bir toplamı geri getirmek zorunda değilsiniz. Ancak, daha fazlasını istiyorsanız, başka bir hizmet veya havuza değil, köke sormanız gerekir. Bu, tembel yüklemedir ve zavallı tembel erkek yükleme ile (uygun depoya / servisi köke enjekte ederek) ya da bunu destekleyen ve ORM kullanarak elle yapılabilir.

Örneğinizde, eğer detayları ayrı bir görüşmeye yüklemek istersem, sadece sipariş başlıklarını getiren bir depo araması yapacağım. Bir "OrderHeader" alarak, aslında etki alanına ek bir konsept getirdiğimizi unutmayın.


3

Etki alanı modeliniz, işletme mantığınızı en saf haliyle içerir. Ticari işlemleri destekleyen tüm ilişkiler ve işlemler. Kavramsal haritanızdan kaçırdığınız şey, Uygulama Hizmeti Katmanı fikri , hizmet katmanı, etki alanı modelinin etrafına sarılır ve Etki alanı modelinin gerektiği gibi değişmesine olanak tanıyan iş etki alanının (eğer istersen bir projeksiyon) basitleştirilmiş bir görünümünü sağlar. Servis katmanını kullanarak uygulamaları doğrudan etkilemeden.

Daha ileri gidiyor. Topluluğun fikri, topluluğun tutarlılığını korumaktan sorumlu tek bir nesne olan toplu kökün olmasıdır. Örneğinizde, sipariş satırlarını değiştirmekle sipariş sorumlu olacaktır.

Örneğin, servis katmanı GetOrdersForCustomer gibi bir işlemi yalnızca siparişlerin özet bir listesini görüntülemek için gerekenleri döndüren (onlara OrderHeaders dediğiniz gibi) geri dönüş yapacaktı.

Son olarak, Havuz modeli SADECE bir koleksiyon değil, aynı zamanda bildirimsel sorgulamalara da olanak sağlar. C # içinde LINQ'yu Sorgu Nesnesi olarak kullanabilirsiniz veya çoğu diğer O / RM, bir Sorgu Nesnesi belirtimi de sağlar.

Bir Havuz, alan ile veri eşleme katmanları arasında bir ara bellek alanı nesne koleksiyonu gibi davranarak aracılık eder. İstemci nesneleri sorgu özelliklerini bildirimsel olarak oluşturur ve bunları memnuniyet için depoya gönderir. ( Fowler's Repository sayfasından )

Depoya karşı sorgular oluşturabileceğinizi görünce, genel sorguları işleyen kolaylık yöntemleri sağlamak da mantıklıdır. Yani, yalnızca siparişinizin başlıklarını istiyorsanız, sadece başlığını döndüren bir sorgu oluşturabilir ve bunu depolarınızdaki kolaylık yönteminden açığa çıkarabilirsiniz.

Umarım bu işleri netleştirmeye yardımcı olur.


0

Bunun eski bir soru olduğunu biliyorum ama farklı bir cevaba ulaştım.

Bir havuz oluştururken genellikle bazı önbelleğe alınmış sorguları sarar.

Fowler, veri deposunu toplama anlambilimi kullanan ve genellikle bellekte tutulan bir veri deposu olarak tanımlar. Bu, tüm bir nesne grafiğini oluşturmak anlamına gelir.

Bu havuzları sunucularınızın koçunda tutun. Sadece nesnelerden veri tabanına geçmiyorlar!

Sipariş listeleyen bir sayfadayım, ayrıntıları görmek için tıklayabiliyorsanız, şans listeleme sayfamın siparişler hakkında ayrıntılara sahip olmasını isteyeceğim (Kimlik, Ad, Miktar, Tarih) Bir kullanıcının hangisine bakmak istediğine karar vermesine yardımcı olmak için.

Bu noktada iki seçeneğiniz var.

  1. Veritabanını sorgulayabilir ve listeleme yapmak için tam olarak neye ihtiyacınız olduğunu geri çekebilir, daha sonra ayrıntı sayfasında görmek istediğiniz bireysel ayrıntıları çekmek için tekrar sorgulayabilirsiniz.

  2. Tüm bilgileri geri çeken ve önbelleğe alan 1 sorgu yapabilirsiniz. Bir sonraki sayfada, veritabanı yerine ram sunucularını okudunuz. Kullanıcı geri vurursa veya bir sonraki sayfayı seçerse, yine de veritabanına sıfır yolculuk yaparsınız.

Gerçekte, onu nasıl uyguladığın sadece, ve uygulama detayıdır. Eğer en büyük kullanıcımın 10 siparişi varsa, muhtemelen seçenek 2 ile gitmek istiyorum. 10.000 siparişten bahsediyorsam, seçenek 1 gerekli. Yukarıdaki davaların ikisinde de ve diğer birçok durumda, havuzun bu uygulama detayını gizlemesini istiyorum.

Kullanıcıya , sipariş listesi sayfasında geçen ayki siparişler ( toplu veriler ) için ne kadar harcadıklarını bildirmek için bir bilet alırsam ileride , bunu hesaplamak ve başka bir gezi daha yapmak için mantığı yazmayı tercih ederim. DB mi yoksa sunucu ramında bulunan verileri kullanarak mı hesaplamak istiyorsunuz?

Tecrübelerime göre, etki alanları büyük avantajlar sunmaktadır.

  • Onlar gerçekten çalışan bir büyük parça kodu yeniden kullanımdır.
  • Sql sunucusunun bunu yapması için bir altyapı katmanını delmek yerine iş mantığınızı temel katmanında doğru tutarak kodu basitleştirir.
  • Ayrıca, kolayca önbellekleyebildiğiniz için yapmanız gereken sorgu sayısını azaltarak yanıt sürenizi önemli ölçüde hızlandırabilirler.
  • Yazdığım SQL genellikle daha fazla bakım gerektiriyor çünkü genelde her şeyi soruyorum ve sunucu tarafını hesaplıyorum.
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.