Zengin Etki Alanı Modelleri - davranış tam olarak nasıl uyuyor?


84

Rich'e karşı Anemik etki alanı modelleri tartışmasında, internet felsefi tavsiyelerle doludur, ancak otoriter örnekler konusunda kısa bilgi. Bu sorunun amacı, kesin Etki Alanına Dayalı Tasarım modellerinin kesin kurallarını ve somut örneklerini bulmaktır. (İdeal olarak C # 'da.)

Gerçek dünyadaki bir örnek için, DDD'nin bu uygulaması yanlış görünmektedir:

Aşağıdaki WorkItem etki alanı modelleri, Entity Framework tarafından kod ilk veritabanı için kullanılan özellik çantalarından başka bir şey değildir. Fowler başına, bu bir anemik .

WorkItemService katmanı, görünüşte Etki Alanı Hizmetlerinin ortak bir yanılgısıdır; WorkItem için tüm davranış / iş mantığını içerir. Yemelyanov ve diğerleri, usule göredir . (sf. 6)

Peki, aşağıdaki yanlışsa, nasıl düzeltebilirim?
Davranış, yani AddStatusUpdate veya Checkout , WorkItem sınıfına girmeli mi?
WorkItem modelinin hangi bağımlılıkları olmalı?

görüntü tanımını buraya girin

public class WorkItemService : IWorkItemService {
    private IUnitOfWorkFactory _unitOfWorkFactory;

    //using Unity for dependency injection
    public WorkItemService(IUnitOfWorkFactory unitOfWorkFactory) {
        _unitOfWorkFactory = unitOfWorkFactory;
    }

    public void AddStatusUpdate(int workItemId, int statusId) {

        using (var unitOfWork = _unitOfWorkFactory.GetUnitOfWork<IWorkItemUnitOfWork>()) {
            var workItemRepo = unitOfWork.WorkItemRepository;
            var workItemStatusRepo = unitOfWork.WorkItemStatusRepository;

            var workItem = workItemRepo.Read(wi => wi.Id == workItemId).FirstOrDefault();
            if (workItem == null)
                throw new ArgumentException(string.Format(@"The provided WorkItem Id '{0}' is not recognized", workItemId), "workItemId");

            var status = workItemStatusRepo.Read(s => s.Id == statusId).FirstOrDefault();
            if (status == null)
                throw new ArgumentException(string.Format(@"The provided Status Id '{0}' is not recognized", statusId), "statusId");

            workItem.StatusHistory.Add(status);

            workItemRepo.Update(workItem);
            unitOfWork.Save();
        }
    }
}

(Bu örnek daha okunaklı olacak şekilde basitleştirildi. Kod kesinlikle hala karışık, çünkü bu karışık bir girişim, ancak etki alanı davranışı şuydu: yeni durumu arşiv geçmişine ekleyerek durumu güncelle. Sonuçta diğer cevaplara katılıyorum. sadece CRUD tarafından ele alınabilirdi.)

Güncelleme

@AlexeyZimarev en iyi cevabı verdi, C # 'daki konuyla ilgili olarak Jimmy Bogard'ın mükemmel bir videosu, ancak bağlantının ötesinde yeterli bilgi vermediği için görünüşe göre aşağıdaki bir yoruma taşındı. Aşağıdaki cevabımdaki videoyu özetleyen notlarım hakkında kaba bir taslak var. Lütfen cevabınızı düzeltmelerle ilgili yorum yapmaktan çekinmeyin. Video bir saat sürüyor ama izlemeye değer.

Güncelleme - 2 Yıl Sonra

Bence DDD'nin yeni ortaya çıkan olgunluğunun bir işareti, 2 yıl çalıştıktan sonra bile, hala bunu yapmanın "doğru yolunu" bildiğime söz veremem. Eşsiz dil, toplu kökler ve davranış odaklı tasarıma yaklaşımı DDD'nin sektöre değerli katkılarıdır. Kalıcılık cehaleti ve olayların kaynaklanması karışıklık yaratıyor ve bunun gibi bir felsefenin onu daha geniş bir kabullenmeden uzak tuttuğunu düşünüyorum. Fakat bu kodu tekrar tekrar yapmak zorunda olsaydım, öğrendiklerimle, bunun gibi bir şey olacağını düşünüyorum:

görüntü tanımını buraya girin

Geçerli bir etki alanı modeli için en iyi uygulama kodunu sağlayan bu (çok etkin) yayına yanıtları hala memnuniyetle karşılıyorum.


6
Onlara söylediğinde tüm felsefi teoriler yere düşer "I don't want to duplicate all my entities into DTOs simply because I don't need it and it violates DRY, and I also don't want my client application to take a dependency on EntityFramework.dll". Varlık Çerçevesi jargonundaki "Varlıklar", "Etki Alanı Modeli"
ndeki

Etki alanımın DTO'ya kopyalanması, eğer Automapper gibi otomatik bir araç kullanılarak yapılması gerekiyorsa, tamam. Bunun günün sonunda nasıl görünmesi gerektiğinden emin değilim.
RJB

16
Ben "Kötü Alan Modelleri hazırlama" Jimmy Bogard en NDC 2012 oturumu izlemenizi öneriyoruz Vimeo . Zengin alanın ne olması gerektiğini ve varlıklarınızdaki davranışlarda bulunarak onları gerçek hayatta nasıl uygulayacağını açıklar. Örnekler çok pratik ve hepsi C #.
Alexey Zimarev

Teşekkürler, videonun ortasındayım ve bu şimdiye kadar mükemmel. Eğer bu yanlışsa, orada bir yerde "doğru" bir cevap olması gerektiğini biliyordum ....
RJB

2
Ben de Java'ya sevgi istiyorum: /
uylmz

Yanıtlar:


59

En yararlı cevap Alexey Zimarev tarafından verildi ve bir moderatör ilk sorumun yorumuna taşımadan önce en az 7 oy aldı.

Cevabı:

Jimmy Bogard'ın NDC 2012 oturumunu "Cimting Wicked Domain Models" oturumunu Vimeo'da izlemenizi tavsiye ederim. Zengin alanın ne olması gerektiğini ve varlıklarınızdaki davranışlarda bulunarak onları gerçek hayatta nasıl uygulayacağını açıklar. Örnekler çok pratik ve hepsi C #.

http://vimeo.com/43598193

Hem ekibimin yararına olan videoyu özetlemek hem de bu yazıya biraz daha kısa bir ayrıntı vermek için bazı notlar aldım. (Video bir saat sürüyor, ancak zamanınız varsa gerçekten her dakikaya değer. Jimmy Bogard, açıklaması için çok fazla kredi hakediyor.)

  • "Çoğu uygulama için ... başladığımızda karmaşık olacaklarını bilmiyoruz. Sadece bu şekilde olurlar."
    • Kod ve gereksinimler eklendikçe karmaşıklık doğal olarak büyür. CRUD gibi uygulamalar çok basit bir şekilde başlayabilir, ancak davranış / kurallar eklenebilir.
    • “İşin güzel yanı, karmaşık bir şekilde başlamak zorunda değiliz. Anemik alan modeliyle başlayabiliriz, bu sadece özellik çantaları ve yalnızca standart yeniden düzenleme teknikleriyle gerçek bir alan modeline doğru ilerleyebiliriz.”
  • Etki alanı modelleri = iş nesneleri. Etki alanı davranışı = iş kuralları.
  • Davranış, genellikle bir uygulamada gizlenir - PageLoad, Button1_Click veya sık sık 'FooManager' veya 'FooService' gibi yardımcı sınıflarda olabilir.
  • Etki alanı nesnelerinden ayrı olan iş kuralları, bu kuralları “hatırlamamızı gerektirir”.
    • Yukarıdaki kişisel örneğimde, bir iş kuralı WorkItem.StatusHistory.Add () şeklindedir. Sadece durumu değiştirmiyoruz, denetim için arşivliyoruz.
  • Etki alanı davranışları "bir uygulamadaki hataları, bir sürü test yazmaktan çok daha kolay bir şekilde ortadan kaldırır." Testler, bu testleri yazmanızı bilmenizi gerektirir. Etki alanı davranışları size test etmek için doğru yollar sunar .
  • Etki alanı hizmetleri, "farklı etki alanı modeli varlıkları arasındaki etkinlikleri koordine etmek için yardımcı sınıflardır".
    • Etki alanı hizmetleri! = Etki alanı davranışı. Varlıklar davranış gösterir, etki alanı hizmetleri sadece varlıklar arasında aracıdır.
  • Etki alanı nesneleri, ihtiyaç duydukları altyapıya sahip olmamalıdır (örn. IOfferCalculatorService). Altyapı hizmeti, onu kullanan etki alanı modeline iletilmelidir.
  • Etki alanı modelleri size neler yapabileceklerini söylemeyi teklif etmeli ve yalnızca bu şeyleri yapabilmeli.
  • Etki alanı modellerinin özellikleri, özel ayarlayıcılarla korunmalıdır, böylece yalnızca model kendi davranışlarını kullanarak kendi özelliklerini belirleyebilir . Aksi takdirde "karışık" olur.
  • Sadece bir ORM için özellik çantaları olan anemik alan modeli nesneleri, yalnızca "ince bir kaplama - veritabanı üzerinde güçlü bir şekilde yazılmış bir sürümdür".
    • "Ancak bir veritabanına bir nesneyi yerleştirmek kolaydır, elimizde bu var."
    • 'En dayanıklı nesne modelleri tam da bu. Anemik bir etki alanı modelini, aslında davranışları olmayan bir uygulamaya göre farklılaştıran şey, bir nesnenin iş kurallarına sahip olması, ancak bu kuralların etki alanı modelinde bulunmamasıdır. '
  • “Birçok uygulama için, herhangi bir gerçek ticari uygulama mantığı katmanı oluşturmaya gerek yoktur, bu sadece veri tabanıyla konuşabilen bir şey ve orada bulunan verileri temsil etmenin kolay bir yolu.”
    • Başka bir deyişle, yaptığınız tek şey özel iş nesnesi veya davranış kuralları olmayan CRUD ise, DDD'ye ihtiyacınız yoktur.

Lütfen dahil edilmesi gerektiğini düşündüğünüz diğer hususlarla veya bu notlardan herhangi birinin işaretin dışında olduğunu düşünüyorsanız çekinmeyin. Mümkün olduğu kadar doğrudan veya paragraftan alıntı yapmaya çalıştım.


Özellikle yeniden düzenlemenin bir araçta nasıl çalıştığını görmek için harika bir video. Çoğu, etki alanı nesnelerinin uygun şekilde kapsüllenmesiyle ilgilidir (tutarlı olduklarından emin olmak için). Teklifler, üyeler, vb. İle ilgili iş kurallarını söyleyen iyi bir iş çıkarır. Değişmez kelimesini birkaç kez söyler (bu, sözleşmeye dayalı alan modellemesidir). .Net kodunun resmi bir işletme kuralı ne daha iyi iletişim kurmasını isterdim, çünkü bu değişiklikler ve onları korumanız gerekir.
Fuhrmanator

6

Sorunuz yanıtlanamıyor, çünkü örneğiniz yanlış. Spesifik olarak, çünkü hiçbir davranış yoktur. En azından etki alanınızla ilgili değil. AddStatusUpdateYöntemin örneği bir etki alanı mantığı değil, bu etki alanını kullanan bir mantıktır. Bu tür bir mantık, dış istekleri yerine getiren bir tür hizmetin içinde olmak anlamlıdır.

Örneğin, belirli bir iş öğesinin yalnızca belirli durumlara sahip olması veya yalnızca N durumlarına sahip olması gerekliliği varsa, bu o zaman etki alanı mantığıdır ve ya bir yöntem olarak ya WorkItemda StatusHistorybir yöntemin parçası olmalıdır .

Kargaşanızın nedeni, ihtiyaç duymayan koda bir kılavuz uygulamaya çalıştığınızdır. Etki alanı modelleri, yalnızca çok sayıda karmaşık etki alanı mantığınız varsa geçerlidir. Örneğin. varlıklar üzerinde çalışan ve gereksinimlerden kaynaklanan mantık. Kod, varlıkları dış verilerden değiştirmekle ilgiliyse, bu muhtemelen bir etki alanı mantığı değildir. Ancak, ifhangi veri ve varlıklarla çalıştığınıza bağlı olarak çok fazla anınız olduğunda, o zaman bu etki alanı mantığıdır.

Gerçek etki alanı modellemenin sorunlarından biri, karmaşık gereksinimleri yönetmekle ilgili olmasıdır. Bu nedenle gerçek gücü ve faydaları basit kod üzerinde gösterilemez. Yararlarını gerçekten görmek için etraflarında tonlarca gereksinimi olan düzinelerce işletmeye ihtiyacınız var. Yine, örneğiniz etki alanı modeli için gerçekten parlamayacak kadar basittir.

Son olarak, bahsedeceğim bazı OT olayları, gerçek OOP tasarımına sahip gerçek bir etki alanı modelinin, Entity Framework'ü kullanarak devam etmesinin gerçekten zor olacağı yönünde. ORM'ler gerçek OOP yapısını ilişkisel olanlarla eşleştirmekle tasarlanırken, hala birçok sorun vardır ve ilişkisel model çoğu zaman OOP modeline sızacaktır. EF'den çok daha güçlü olduğunu düşündüğüm nBibernate ile bile, bu bir problem olabilir.


Güzel nokta. AddStatusUpdate yöntemi daha sonra, Data'ya ya da Infrastrcture'daki başka bir projeye ait olurdu? Teorik olarak WorkItem'e ait olabilecek herhangi bir davranışa örnek nedir? Herhangi bir psuedo kodu veya mock-up çok takdir edilecektir. Benim örneğim aslında daha okunaklı olması için basitleştirildi. Başka varlıklar var ve örneğin AddStatusUpdate'in bazı ekstra davranışları var - aslında bir durum kategorisi adı alıyor ve bu kategori yoksa, kategori oluşturulur.
RJB

@RJB Dediğim gibi, AddStatusUpdate etki alanını kullanan koddur. Yani etki alanı sınıflarını kullanan bir çeşit web servis veya uygulama. Ve dediğim gibi, herhangi bir mockup veya sahte kod bekleyemezsiniz, çünkü OOP alan modelinin gerçek avantajını göstermek için yeterince büyük bir karmaşıklık projesi yapmanız gerekecektir.
Öforik

5

WorkItem ile ilişkili iş mantığınızı bir "şişman hizmete" dahil etmenin varsayımına göre, benim iddia edebileceğim içsel bir kalıp değildir.

Anemik etki alanı modeli hakkındaki düşünceleriniz ne olursa olsun, bir İş Hattı .NET uygulamasına özgü standart desenler ve uygulamalar, çeşitli bileşenlerden oluşan işlem katmanlı bir yaklaşımı teşvik eder. İş mantığının etki alanı modelinden özellikle ortak bir etki alanı modelinin diğer .NET bileşenleri arasında ve aynı zamanda farklı teknoloji yığınlarındaki bileşenler veya fiziksel katmanlar arasında iletişimini kolaylaştırmak için ayrılmasını teşvik ederler.

Buna bir örnek, basit veri türlerini içeren bir DLL dosyasına sahip olan bir Silverlight istemci uygulamasıyla iletişim kuran .NET tabanlı bir SOAP web hizmeti olabilir. Bu etki alanı varlığı projesi, bu DLL'ye sahip olan ilgilenen Silverlight bileşenlerinin yalnızca hizmet için mevcut bileşenlere bağlı olabilecek nesne davranışlarına maruz kalmayacağı bir .NET derlemesi veya Silverlight derlemesi içine yerleştirilebilir.

Bu tartışma konusundaki tutumunuza bakılmaksızın, bu, Microsoft tarafından ortaya konan benimsenen ve kabul edilen modeldir ve benim görüşüme göre yanlış bir yaklaşım değil, daha sonra kendi davranışını tanımlayan bir nesne modeli de mutlaka bir anti-model değildir. Bu tasarımla ilerlerseniz, etki alanı modelinizi görmesi gereken diğer bileşenlerle bütünleşmeniz gerekiyorsa, karşılaşabileceğiniz bazı sınırlamaları ve acı noktalarını anlamak ve anlamak en iyisidir. Bu özel durumda, belki de bir Tercümanın nesne yönelimli stil etki alanı modelinizi belirli davranış yöntemlerini göstermeyen basit veri nesnelerine dönüştürmesini isteyebilirsiniz.


1
1) İş mantığını etki alanı modelinden nasıl ayırabilirsiniz? Bu iş mantığının yaşadığı alan; Bu etki alanındaki varlıklar, bu iş mantığı ile ilişkili davranışları yürütüyor. Gerçek dünyanın hiçbir hizmeti yoktur, etki alanı uzmanlarının başında da yokturlar. 2) Sizinle bütünleşmek isteyen herhangi bir bileşenin kendi etki alanı modelini oluşturması gerekir, çünkü ihtiyaçları farklı olacaktır ve etki alanı modeliniz üzerinde farklı bir görüşü olacaktır . Etrafında paylaşılabilen bir etki alanı modeli oluşturabilmeniz uzun zamandır süren bir güvendir.
Stefan Billiet

1
@StefanBilliet Bunlar, evrensel bir etki alanı modelinin yanlışlığıyla ilgili iyi noktalardır, ancak daha önce yaptığım gibi daha basit bileşenlerde ve bileşen etkileşiminde mümkündür. Benim görüşüme göre, etki alanı modelleri arasındaki çeviri mantığı birçok sıkıcı ve kazancı kod için yapabilir ve güvenli bir şekilde önlenebilirse, o zaman bu iyi bir tasarım seçimi olabilir.
maple_shaft

1
Dürüst olmak gerekirse, bence tek iyi tasarım seçimi bir işletme uzmanının sebep olabileceği bir model. Bir işletmenin etki alanındaki belirli sorunları çözmek için kullanması için bir etki alanı modeli oluşturuyorsunuz. Davranışı etki alanı varlıklarından hizmetlere bölmek, katılan herkes için bunu zorlaştırır, çünkü bir etki alanı uzmanının mevcut konuşmaya neredeyse hiç benzemeyen hizmet koduna söylediklerini sürekli olarak eşlemeniz gerekir. Tecrübelerime göre, kazan ile yazmaktan çok daha fazla zaman kaybediyorsun. Bu, kazan dairesi kodunun etrafında bir yol olmadığını söylemez.
Stefan Billiet

@StefanBilliet Mükemmel bir dünyada, bir iş uzmanının geliştiricilere oturup zaman ayıracağı yere katılıyorum. Yazılım endüstrisinin gerçekliği, iş uzmanının bu seviyede yer almak için hiçbir zamana veya ilgisine sahip olmaması veya daha da kötüsü, ancak geliştiricilerin sadece belirsiz rehberlikle bulması beklenir.
maple_shaft

Doğru, ama bu gerçeği kabul etmek için bir sebep değil. Böyle bir arayışa devam etmek, geliştiricilerin zamanını (ve muhtemelen itibarını) ve müşterinin parasını harcamaktır. Açıkladığım süreç, zaman içinde inşa edilmesi gereken bir ilişki; çok çaba gerektirir, ancak çok daha iyi sonuçlar verir. "Ubiquitous Language" in genellikle DDD'nin en önemli yönü olarak kabul edilmesinin bir nedeni var.
Stefan Billiet

5

Bu sorunun oldukça eski olduğunu biliyorum, bu yüzden bu cevap gelecek için. Teoriye dayanmak yerine somut bir örnekle cevap vermek istiyorum.

"İş öğesinin durumunun değiştirilmesi" WorkItemsınıfında şöyle olmalıdır:

public SomeStatusUpdateType Status { get; private set; }

public void ChangeStatus(SomeStatusUpdateType status)
{
    // Maybe we designed this badly at first ;-)
    Status = status;       
}

Şimdi WorkItemsınıfınız kendini yasal bir durumda tutmaktan sorumlu. Ancak uygulama oldukça zayıf. Ürün sahibi, için yapılan tüm durum güncellemelerinin geçmişini istiyor WorkItem.

Bunu şöyle bir şeye değiştiriyoruz:

private ICollection<SomeStatusUpdateType> StatusUpdates { get; private set; }
public SomeStatusUpdateType Status => StatusUpdates.OrderByDescending(s => s.CreatedOn).FirstOrDefault();

public void ChangeStatus(SomeStatusUpdateType status)
{
    // Better...
    StatusUpdates.Add(status);       
}

Uygulama büyük ölçüde değişti, ancak ChangeStatusyöntemin arayanı , temel uygulama detaylarından habersiz ve kendisini değiştirmek için hiçbir neden yok.

Bu, IMHO adlı zengin bir etki alanı modeli varlığının bir örneğidir.

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.