DDD'de depolar bir varlık veya etki alanı nesnelerini göstermeli mi?


11

Anladığım kadarıyla, DDD'de, birleştirilmiş kökü olan bir depo deseni kullanmak uygundur. Sorum şu: Verileri bir varlık veya etki alanı nesneleri / DTO olarak döndürmeli miyim?

Belki bazı kod sorumu daha fazla açıklayacaktır:

varlık

public class Customer
{
  public Guid Id { get; set; }
  public string FirstName { get; set; }
  public string LastName { get; set; }
}

Böyle bir şey yapmalı mıyım?

public Customer GetCustomerByName(string name) { /*some code*/ }

Yoksa böyle bir şey mi?

public class CustomerDTO
{
  public Guid Id { get; set; }
  public FullName { get; set; }
}

public CustomerDTO GetCustomerByName(string name) { /*some code*/ }

Ek soru:

  1. Bir depoda IQueryable veya IEnumerable i döndürmeli miyim?
  2. Bir hizmet veya depo, ben böyle bir şey yapmak gerekir .. GetCustomerByLastName, GetCustomerByFirstName, GetCustomerByEmail? ya da sadece benzer bir yöntem GetCustomerBy(Func<string, bool> predicate)mi yapıyorsunuz ?

GetCustomerByName('John Smith')Veritabanınızda yirmi John Smith varsa ne dönecektir? Görünüşe göre hiç kimsenin aynı ada sahip olmadığı varsayılıyor.
bdsl

Yanıtlar:


8

verileri varlık veya etki alanı nesneleri / DTO olarak döndürmeli miyim

Bu tamamen kullanım durumlarınıza bağlıdır. Tam varlık yerine DTO döndürmeyi düşünebilmemin tek nedeni, varlığınızın çok büyük olması ve yalnızca bir alt kümesi üzerinde çalışmanız gerektiğidir.

Bu durumda, belki de etki alanı modelinizi yeniden düşünmeli ve büyük varlığınızı ilgili küçük varlıklara ayırmalısınız.

  1. Bir depoda IQueryable veya IEnumerable i döndürmeli miyim?

İyi bir kural, her zaman mümkün olan en basit (kalıtım hiyerarşisinde en yüksek) türünü döndürmektir. Bu nedenle, IEnumerabledepo tüketicisinin bir ile çalışmasına izin vermek istemiyorsanız geri dönün IQueryable.

Şahsen, bir geri dönmek IQueryablesızdıran bir soyutlama olduğunu düşünüyorum ama tutkuyla olmadığını iddia eden birkaç geliştirici ile tanıştım. Zihnimde tüm sorgulama mantığı Havuz tarafından barındırılmalı ve gizlenmelidir. Arama kodunun sorgularını özelleştirmesine izin verirseniz, deponun amacı nedir?

  1. Bir hizmette veya depoda şöyle bir şey yapmalıyım .. GetCustomerByLastName, GetCustomerByFirstName, GetCustomerByEmail? ya da sadece GetCustomerBy (Func predicate) gibi bir yöntem yapın?

Ben noktada 1'de belirtildiği gibi Aynı nedenle, kesinlikle yok kullanın GetCustomerBy(Func<string, bool> predicate). İlk başta cazip gelebilir, ancak tam da bu yüzden insanlar jenerik depolardan nefret etmeyi öğrendiler. Sızdı.

Gibi şeyler GetByPredicate(Func<T, bool> predicate)sadece somut sınıfların arkasına saklandığında faydalıdır. Yani, sadece somut depolar (örn. ) Tarafından kullanılan RepositoryBase<T>maruz kalan soyut bir temel sınıfınız olsaydı, bu iyi olurdu.protected T GetByPredicate(Func<T, bool> predicate)public class CustomerRepository : RepositoryBase<Customer>


Yani, DTO sınıflarının etki alanı katmanında olması sorun değil mi?
kod balığı

Söylemeye çalıştığım bu değil. Ben bir DDD gurusu değilim, bu yüzden kabul edilebilir olup olmadığını söyleyemem. Meraktan, neden tüm varlığı iade etmiyordun? Çok mu büyük?
MetaFight

Gerçekten değil. Sadece yapılacak doğru ve kabul edilebilir şeyin ne olduğunu bilmek istiyorum. Tam bir varlığı mı yoksa yalnızca alt kümesini mi döndürür? Sanırım cevabınıza dayanıyor.
kod balığı

6

Alanlarını uygulamak için CQRS kullanan büyük bir topluluk var. Benim hissim, deponuzun arayüzü onlar tarafından kullanılan en iyi uygulamalara benziyorsa, çok yoldan sapmayacaksınız.

Gördüklerime dayanarak ...

1) Komut işleyicileri genellikle toplamı bir depo yoluyla yüklemek için havuzu kullanır. Komutlar, toplamanın belirli bir örneğini hedefler; depo kökü kimliğe göre yükler. Görebildiğim gibi, komutların toplu bir koleksiyona karşı çalıştırıldığı bir durum yoktur (bunun yerine, önce toplamaların toplanmasını almak için bir sorgu çalıştırırsınız, ardından koleksiyonu numaralandırır ve her birine bir komut verirsiniz.

Bu nedenle, toplamı değiştireceğiniz bağlamlarda, deponun varlığı (yani toplam kök olarak) döndürmesini beklerim.

2) Sorgu işleyicileri toplamlara hiç dokunmaz; bunun yerine, toplanmaların / toplanmaların durumunu belirli bir zamanda tanımlayan değer nesnelerinin projeksiyonları ile çalışırlar. Öyleyse AggregateDTO yerine ProjectionDTO'yu düşünün ve doğru fikre sahipsiniz.

Toplamaya karşı sorgular çalıştıracağınız, görüntülemeye hazırlayacağınız bağlamlarda, bir varlık yerine döndürülen bir DTO veya bir DTO koleksiyonu görmeyi beklerim.

Tüm getCustomerByPropertyaramalarınız bana sorgular gibi geliyor, bu yüzden ikinci kategoriye girecekler. Büyük olasılıkla koleksiyonu oluşturmak için tek bir giriş noktası kullanmak istiyorum, bu yüzden eğer

getCustomersThatSatisfy(Specification spec)

makul bir seçimdir; sorgu işleyicileri daha sonra verilen parametrelerden uygun belirtimi oluşturacak ve bu belirtimi depoya geçirecektir. Dezavantajı, imzanın havuzun hafızada bir koleksiyon olduğunu düşündürmesidir; depo sadece ilişkisel bir veritabanına karşı bir SQL deyimi çalıştırmanın bir soyutlama ise yüklemin size çok şey aldığı açık değildir.

Yine de yardımcı olabilecek bazı desenler var. Örneğin, belirtimi elle oluşturmak yerine, depoya kısıtlamaların bir açıklamasını iletin ve ne yapılacağına karar vermek için havuzun uygulanmasına izin verin.

Uyarı: java like typing tespit

interface CustomerRepository {
    interface ConstraintBuilder {
        void setLastName();
        void setFirstName();
    }

    interface ConstraintDescriptor {
        void copyTo(ConstraintBuilder builder);
    }

    List<CustomerProjection> getCustomersThatSatisfy(ConstraintDescriptor descriptor);
}

SQLBackedCustomerRepository implements CustomerRepository {
    List<CustomerProjection> getCustomersThatSatisfy(ConstraintDescriptor descriptor) {
        WhereClauseBuilder builder = new WhereClauseBuilder();
        descriptor.copyTo(builder);
        Query q = createQuery(builder.build());
        //...
     }
}

CollectionBackedCustomerRepository implements CustomerRepository {
    List<CustomerProjection> getCustomersThatSatisfy(ConstraintDescriptor descriptor) {
        PredicateBuilder builder = new PredicateBuilder();
        descriptor.copyTo(builder);
        Predicate p = builder.build();
        // ...
}

class MatchLastName implements CustomerRepository.ConstraintDescriptor {
    private final lastName;
    // ...

    void copyTo(CustomerRepository.ConstraintBuilder builder) {
        builder.setLastName(this.lastName);
    }
}

Sonuç olarak: bir agrega sağlama ve bir DTO sağlama arasındaki seçim, tüketicinin onunla ne yapmasını beklediğinize bağlıdır. Benim tahminim, her bağlam için bir arabirimi destekleyen somut bir uygulama olurdu.


Hepsi güzel ve iyi, ama asker CQRS kullanmaktan bahsetmiyor. Yine de haklısın, eğer problemi yaşarsa sorunu olmazdı.
MetaFight

Bunu duyduğumda CQRS hakkında bilgim yok. Anladığım kadarıyla, AggregateDTO veya ProjectionDTO ise ne döneceğini düşünürken ProjectionDTO'yu iade edeceğim. Sonra getCustomersThatSatisfy(Specification spec)sadece arama seçenekleri için gerekli özellikleri listeleyeceğim. Doğru anladım mı?
kod balığı

Belirsiz. AggregateDTO'nun var olması gerektiğine inanmıyorum. Toplamın amacı, tüm değişikliklerin işletme değişmezini tatmin etmesini sağlamaktır. Bu, karşılanması gereken etki alanı kurallarının kapsüllenmesidir - yani, davranıştır. Öte yandan, projeksiyonlar, devletin iş için kabul edilebilir bazı anlık görüntülerinin temsilidir. Belirtimi açıklığa kavuşturmak için düzenlemeye bakın.
VoiceOfUnreason

Bir AggregateDTO'nun olmaması gerektiğine dair VoiceOfUnreason ile hemfikirim - ProjectionDTO'yu yalnızca veri için bir görünüm modeli olarak düşünüyorum. Şeklini arayanın gerektirdiği şekilde uyarlayabilir ve gerektiğinde çeşitli kaynaklardan veri çekebilir. Toplama Kökü, ilgili tüm verilerin tam bir temsili olmalıdır, böylece tüm referans kuralları kaydetmeden önce test edilebilir. Yalnızca DB tablosu değişiklikleri veya değiştirilmiş veri ilişkileri gibi daha zorlu koşullarda şekil değiştirir.
Brad Irby

1

DDD bilgime dayanarak, buna sahip olmalısınız,

public CustomerDTO GetCustomerByName(string name) { /*some code*/ }

Bir depoda IQueryable veya IEnumerable i döndürmeli miyim?

Bu soru için farklı halklardan karma görünümler bulacaksınız. Ancak şahsen, deponuz CustomerService gibi bir hizmet tarafından kullanılacaksa, IQueryable'ı kullanabilir veya IEnumerable ile yapıştırabilirsiniz.

Depolar IQueryable'ı döndürmeli mi?

Bir hizmette veya depoda şöyle bir şey yapmalıyım .. GetCustomerByLastName, GetCustomerByFirstName, GetCustomerByEmail? ya da sadece GetCustomerBy (Func predicate) gibi bir yöntem yapın?

Deponuzda söylediğiniz gibi genel bir işleve sahip olmalısınız, GetCustomer(Func predicate)ancak hizmet katmanınıza üç farklı yöntem ekleyin, bunun nedeni, hizmetinizin farklı istemcilerden çağrılma şansı olması ve farklı DTO'lara ihtiyaç duymalarıdır.

Henüz farkında değilseniz, genel CRUD için Genel Depo Kalıbı'nı da kullanabilirsiniz .

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.