Agregalar arasındaki referansların validasyonu nasıl tedavi edilir?


11

Birikimler arasında atıfta bulunmakla biraz uğraşıyorum. Topluluğun topluluğa Carbir referansı olduğunu varsayalım Driver. Bu referans sahip olacak şekilde modellenecektir Car.driverId.

Şimdi benim sorunum, bir Caragrega oluşturulmasını doğrulamak için ne kadar ileri gitmem gerektiğidir CarFactory. Ben geçti güveneyim DriverIdbir atıfta mevcut Driver ya da ben kontrol gerektiğini değişmez?

Kontrol etmek için iki olasılık görüyorum:

  • Tam bir sürücü varlığını kabul etmek için otomobil fabrikasının imzasını değiştirebilirim. Fabrika daha sonra o varlıktan kimliği seçecek ve arabayı bununla inşa edecektir. Burada değişmez dolaylı olarak kontrol edilir.
  • Ben bir başvuru olabilir DriverRepositoryiçinde CarFactoryve açıkça diyoruz driverRepository.exists(driverId).

Ama şimdi merak ediyorum, çok fazla değişmez kontrol değil mi? Bu agregaların ayrı sınırlı bağlamda yaşayabileceğini hayal edebiliyordum ve şimdi arabayı BC'yi DriverRepository veya BC sürücüsünün Driver varlığına bağımlılıklar ile kirletecektim.

Ayrıca, alan adı uzmanlarıyla konuşursam, bu tür referansların geçerliliğini asla sorgulamazlardı. Alan modelimi ilgisiz endişelerle kirlettiğimi hissediyorum. Ancak yine, bir noktada kullanıcı girdisi doğrulanmalıdır.

Yanıtlar:


6

Tam bir sürücü varlığını kabul etmek için otomobil fabrikasının imzasını değiştirebilirim. Fabrika daha sonra o varlıktan kimliği seçecek ve arabayı bununla inşa edecektir. Burada değişmez dolaylı olarak kontrol edilir.

Bu yaklaşımı ücretsiz olarak almanız ve her yerde bulunan dil ile iyi bir şekilde hizalanması nedeniyle caziptir. Bir Carbir tarafından tahrik edilmez driverIdfakat tarafından, Driver.

Bu yaklaşım aslında Vaughn Vernon tarafından Kimlik ve Erişim örnek sınırlı bağlamında kullanılır ve burada bir Usertoplamı bir toplama geçirir Group, ancak Groupsadece bir değer türüne dayanır GroupMember. Gördüğünüz gibi bu, kullanıcının etkinliğini kontrol etmesine de izin veriyor (kontrolün eski olabileceğinin farkındayız).

    public void addUser(User aUser) {
        //original code omitted
        this.assertArgumentTrue(aUser.isEnabled(), "User is not enabled.");

        if (this.groupMembers().add(aUser.toGroupMember()) && !this.isInternalGroup()) {
            //original code omitted
        }
    }

Ancak, Driverörneği geçerek kendinizi Driveriçeride yanlışlıkla bir modifikasyona açarsınız Car. Değer referansını geçmek, bir programcının bakış açısından değişikliklerle ilgili mantık yürütmeyi kolaylaştırır, ancak aynı zamanda DDD, her yerde Her Yerli Dil ile ilgilidir, bu yüzden belki de riske değer.

Arayüz Ayırma Prensibi'ni (İSS) uygulamak için gerçekten iyi isimler bulabilirseniz , davranışsal yöntemlere sahip olmayan bir arayüze güvenebilirsiniz. Belki de değişmez bir sürücü referansını temsil eden ve sadece mevcut bir sürücüden (örn DriverDescriptor driver = driver.descriptor().

Bu agregaların ayrı sınırlı bağlamda yaşayabileceğini hayal edebiliyordum ve şimdi arabayı BC'yi DriverRepository veya BC sürücüsünün Driver varlığına bağımlılıklar ile kirletecektim.

Hayır. Bir bağlamdaki kavramların başka bir içeriğe akmayacağından emin olmak için her zaman bir yolsuzlukla mücadele katmanı vardır. Otomobil sürücüsü derneklerine adanmış bir BC'niz varsa aslında çok daha kolaydır, çünkü bu bağlam gibi Carve Driverözellikle mevcut bağlam için mevcut kavramları modelleyebilirsiniz .

Bu nedenle, DriverLookupServiceBC'de araç sürücüsü birliklerini yönetmekle sorumlu bir tanımınız olabilir . Bu hizmet, Sürücü Yönetimi bağlamı tarafından maruz bırakılan ve Driverbüyük olasılıkla bu bağlamda değer nesneleri olacak örnekleri döndüren bir web hizmetini çağırabilir .

Web servislerinin BC'ler arasında mutlaka en iyi entegrasyon yöntemi olmadığını unutmayın. Ayrıca, örneğin UserCreatedSürücü Yönetimi bağlamından bir mesajın sürücünün bir temsilini kendi DB'sinde saklayacak uzak bir bağlamda tüketileceği mesajlara da güvenebilirsiniz . Daha DriverLookupServicesonra bu DB kullanılabilir ve sürücü verileri başka iletilerle (ör. DriverLicenceRevoked) Güncellenir .

Size hangi yaklaşımın alan adınız için daha iyi olduğunu söyleyemem, ancak umarım bu size bir karar vermeniz için yeterli bilgi verecektir.


3

Soruyu sorma şekliniz (ve iki alternatif önerme), tek endişe, driverId'in araç oluşturulduğu sırada hala geçerli olmasıdır.

Bununla birlikte, sürücü silinmeden veya başka bir sürücü verilmeden önce driverId ile ilişkili sürücünün silinmediğinden (ve muhtemelen sürücünün başka bir arabaya atanmadığından da endişelenmelisiniz (bu, etki alanı bir sürücüyü yalnızca bir araba ile ilişkilendirilebilir).

Doğrulama yerine ayırmayı öneririm (varlığın onaylanmasını da içerir). Daha sonra, hala tahsis edilirken silme işlemlerine izin vermeyeceksiniz, böylece inşaat sırasında eski verilerin yarış durumuna ve diğer uzun vadeli sorunlara karşı koruyacaksınız. (Tahsinin, tahsis edilen işaretleri doğruladığını ve işaretlediğini ve atomik olarak çalıştığını unutmayın.)

Btw, @ PriceJones ile hem araba hem de sürücü arasındaki ilişkinin muhtemelen arabadan ya da sürücüden ayrı bir sorumluluk olduğunu kabul ediyorum. Bu tür bir ilişki sadece zaman içinde karmaşıklık içinde büyüyecektir, çünkü kulağa bir programlama problemi (sürücüler, arabalar, zaman dilimleri / pencereler, ikameler, vb.) Gibi gelebilir. kayıtları ve güncel kayıtlar. Bu nedenle, kendi M.Ö. düpedüz değerini hak edebilir.

Tahsis edilen toplam varlıkların BC'si içinde veya ayrı bir BC içinde, örneğin otomobil ve sürücü arasındaki ilişkiyi kurmaktan sorumlu bir tahsis şeması (boole veya referans sayısı gibi) sağlayabilirsiniz. Birincisini yaparsanız, arabaya veya BC sürücüsüne verilen (geçerli) silme işlemlerine izin verebilirsiniz; ikincisini yaparsanız, araba ve sürücü BC'lerinden silinmeleri önlemeniz ve bunun yerine araba ve sürücü ilişkilendirme zamanlayıcısını göndermeniz gerekir.

Ayrıca, BC'ler arasında bazı tahsis sorumluluklarını aşağıdaki gibi bölebilirsiniz. Otomobil ve sürücü BC'nin her biri, bu BC ile tahsis edilen boolean'ı doğrulayan ve ayarlayan bir "tahsis" şeması sağlar; tahsis boole değeri ayarlandığında, BC karşılık gelen varlıkların silinmesini önler. (Ve sistem, araç ve sürücü BC'nin yalnızca araç / sürücü ilişkilendirme planlamasından BC tahsis edilmesine ve yer değiştirmesine izin verecek şekilde ayarlanmıştır.)

Otomobil ve şoför çizelgeleme programı, şimdi ve gelecekteki bazı zaman periyotları / süreleri için otomobille ilişkili sürücülerin bir takvimini tutar ve diğer BC'leri sadece planlanmış bir otomobilin veya sürücünün son kullanımında bildirir.


Daha radikal bir çözüm olarak, otomobil ve sürücü BC'lerini sadece ek tarihsel kayıt fabrikaları olarak değerlendirebilir ve araç / sürücü ilişkilendirme zamanlayıcısına sahiplik bırakabilirsiniz. BC arabası, VIN ile birlikte aracın tüm detaylarıyla birlikte yeni bir araba üretebilir. Otomobilin mülkiyeti otomobil / sürücü birliği zamanlayıcısı tarafından yönetilir. Bir araba / sürücü derneği silinmiş ve arabanın kendisi yok edilmiş olsa bile, arabanın kayıtları tanım gereği hala MÖ araçta mevcuttur ve tarihi verileri aramak için MÖ aracını kullanabiliriz; araba / sürücü dernekleri / sahiplikleri (geçmiş, şimdiki ve potansiyel olarak gelecekteki programlanmış) başka bir MÖ tarafından ele alınmaktadır.


2

Toplama Aracının toplama Sürücüsüne bir referansı olduğunu varsayalım. Bu referans Car.driverId'ye sahip olacak şekilde modellenecektir.

Evet, bu bir toplamı diğerine birleştirmenin doğru yolu.

alan adı uzmanlarıyla görüşürsem, bu tür referansların geçerliliğini asla sorgulamazlardı

Alan adı uzmanlarınıza sormak doğru soru değil. "Sürücü yoksa işletme maliyeti nedir?"

Ben muhtemelen driverId kontrol etmek için DriverRepository kullanmak olmaz. Bunun yerine, bunu yapmak için bir etki alanı hizmeti kullanırdım. Ben niyetini ifade etmek daha iyi bir iş olduğunu düşünüyorum - kapaklar altında, alan adı hizmeti hala kayıt sistemini kontrol eder.

Yani böyle bir şey

class DriverService {
    private final DriverRepository driverRepository;

    boolean doesDriverExist(DriverId driverId) {
        return driverRepository.exists(driverId);
    }
}

Aslında sürücü kimliğiyle ilgili etki alanını birkaç farklı noktada soruyorsunuz

  • Komutu göndermeden önce istemciden
  • Uygulamada, komutu modele geçirmeden önce
  • Etki alanı modeli içinde, komut işleme sırasında

Bu kontrollerin herhangi biri veya tamamı kullanıcı girişindeki hataları azaltabilir. Fakat hepsi eski verilerden çalışıyor; soruyu sorduktan hemen sonra diğer agrega değişebilir. Yani her zaman yanlış negatif / pozitif tehlike vardır.

  • Bir istisna raporunda, komut tamamlandıktan sonra çalıştırın

Burada hala eski verilerle çalışıyorsunuz (raporu çalıştırırken toplamalar komutları çalıştırıyor olabilir, tüm toplamaların en son yazmalarını göremeyebilirsiniz). Ancak, agregalar arasındaki kontroller hiçbir zaman mükemmel olmayacaktır (Car.create (sürücü: 7) Driver.delete (sürücü: 7) ile aynı anda çalışıyor.


1
Driver.deleteolmamalı. Asla agregaların yok edildiği bir alan görmedim. AR'leri etrafta tutarak asla yetim kalmazsın.
plalx

1

Sormak yardımcı olabilir: Arabaların sürücülerle inşa edildiğinden emin misiniz? Gerçek dünyada bir sürücüden oluşan bir araba hiç duymadım. Bu sorunun önemli olmasının nedeni, sizi bağımsız olarak araba ve sürücü oluşturma ve ardından bir araca bir sürücü atayan bazı dış mekanizmalar oluşturma yönünde gösterebilmesidir. Bir araba sürücü referansı olmadan var olabilir ve yine de geçerli bir araba olabilir.

Bir arabanın bağlamınızda kesinlikle bir sürücüsü olması gerekiyorsa, üretici desenini düşünmek isteyebilirsiniz. Bu model, araçların mevcut sürücülerle yapılmasını sağlamaktan sorumlu olacaktır. Fabrikalar bağımsız olarak onaylanmış otomobil ve şoförlere hizmet verecek, ancak inşaatçı, otomobili servis etmeden önce ihtiyaç duyduğu referansa sahip olmasını sağlayacaktır.


Ben de araba / sürücü ilişkisi hakkında düşündüm - ama bir DriverAssignment toplama tanıtmak sadece hangi referans doğrulanması gereken hamle.
VoiceOfUnreason

1

Ama şimdi merak ediyorum, çok fazla değişmez kontrol değil mi?

Sanırım. DB'den belirli bir DriverId getirilirse, boş bir küme döndürülür. Bu nedenle, dönüş sonucunu kontrol etmek, var olup olmadığını sormayı (ve sonra getirmeyi) gereksiz kılar.

Sonra sınıf tasarımı onu da gereksiz kılar

  • "Park edilmiş bir otomobilin sürücüsü olabilir veya olmayabilir"
  • Bir Driver nesnesi DriverIdyapıcıda bir ve gerektiriyorsa .
  • CarSadece ihtiyaçları varsa, DriverIdbir Driver.Idalıcı var. Ayarlayıcı yok.

Havuz iş kurallarının yeri değil

  • A'nın (veya en azından kimliğinin) Carolup olmadığı umurundadır Driver. A Driverumurunda ise bir DriverId. RepositoryVeri bütünlüğü hakkında umurunda ve sürücü daha az arabalar hakkında daha az bakım olabilir.
  • DB veri bütünlüğü kurallarına sahip olacaktır. Boş olmayan anahtarlar, boş olmayan kısıtlamalar, vb. Bu durumda güçlü bir şekilde ilişkili, simbiyotik bir ilişkimiz var, ancak ikisini karıştırmayın.
  • A'nın DriverIdticari alan adı olması uygun sınıflarda ele alınır.

Endişe İhlallerinin Ayrılması

... Repository.DriverIdExists()soruyu sorduğunda olur .

Bir etki alanı nesnesi oluşturun. Değilse, Drivero zaman belki DriverInfo(sadece bir DriverIdve Namediyelim) bir nesne. DriverIdİnşaat üzerine doğrulanır. Var olmalı, doğru tip olmalı ve her neyse. O zaman var olmayan bir sürücü / driverId ile nasıl başa çıkılacağı bir istemci sınıfı tasarım sorunudur.

Belki bir Carsen arayana kadar bir sürücü olmadan iyi Car.Drive(). Bu durumda Carnesne elbette kendi durumunu garanti eder. Olmadan araba kullanamam Driver- iyi, henüz değil.

Bir mülkü sınıfından ayırmak kötüdür

Tabii, Car.DriverIdisterseniz. Ancak şöyle görünmelidir:

public class Car {
    // Non-null driver has a driverId by definition/contract.
    protected DriverInfo myDriver;
    public DriverId {get { return myDriver.Id; }}

    public void Drive() {
       if (myDriver == null ) return errorMessage; // or something
       // ... continue driving
    }
}

Bu değil:

public class Car {
    public int DriverId {get; protected set;}
}

Şimdi Cartüm DriverIdgeçerlilik konularını ele almalı - tek bir sorumluluk ilkesi ihlali; ve gereksiz kod.

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.