Uygulama veya etki alanı hizmetindeki DDD depoları


29

Bugünlerde DDD çalışıyorum ve DDD ile depoların nasıl yönetileceği ile ilgili bazı sorularım var.

Aslında iki olasılıkla karşılaştım:

İlk

Okuduğum hizmetleri yönetmenin ilk yolu, bir uygulama hizmetine bir havuz ve bir etki alanı modeli enjekte etmektir.

Bu şekilde, uygulama hizmeti yöntemlerinden birinde, bir etki alanı hizmeti yöntemini (iş kurallarını kontrol ederek) çağırırız ve koşul iyiyse, depoya varlık veritabanından devam ettirmek / almak için özel bir yöntem çağrılır.

Bunu yapmanın basit bir yolu olabilir:

class ApplicationService{

  constructor(domainService, repository){
    this.domainService = domainService
    this.repository = repository
  }

  postAction(data){
    if(this.domainService.validateRules(data)){
      this.repository.persist(new Entity(data.name, data.surname))
    }
    // ...
  }

}

İkinci

İkinci olasılık, depoyu domainService içindeki yerine enjekte etmek ve depoyu yalnızca domain hizmeti aracılığıyla kullanmaktır:

class ApplicationService{

  constructor(domainService){
    this.domainService = domainService
  }

  postAction(data){
    if(this.domainService.persist(data)){
      console.log('all is good')
    }
    // ...
  }

}

class DomainService{

  constructor(repository){
    this.repository = repository
  }

  persist(data){
    if(this.validateRules(data)){
      this.repository.save(new Entity(data.name))
    }
  }

  validateRules(data){
    // returns a rule matching
  }

}

Bundan sonra, hangisinin en iyisi olduğunu (hangisi en iyisi varsa) veya bunların ikisinde de ne ifade ettiklerini ayırt edemiyorum.

Bana birinin diğerinden daha iyi olabileceği bir örnek verebilir misiniz ve neden?



"bir uygulama hizmetinde bir depo ve etki alanı modeli enjekte etmek için." Bir yere "etki alanı modeli" enjekte etmekle neyi kastediyorsunuz? DDD alan modeli açısından AFAICT, alandan bütün kavramlar kümesi ve aralarındaki uygulama ile ilgili etkileşimler anlamına gelir. Bu soyut bir şey, hafıza içi bir nesne değil. Enjekte edemezsin.
Alexey

Yanıtlar:


31

Kısa cevap - bir uygulama hizmetinden veya bir etki alanı hizmetinden depoları kullanabilirsiniz - ancak neden ve nasıl yaptığınızı düşünmeniz önemlidir.

Etki Alanı Hizmetinin Amacı

Etki Alanı Hizmetleri, etki alanı kavramlarını / mantığını - etki alanı hizmeti yöntemini de içine almalıdır:

domainService.persist(data)

her yerde persistbulunan dilin bir parçası olmadığı ve sebat etme işlemi etki alanı iş mantığının bir parçası olmadığı için bir etki alanı hizmetine ait değildir.

Genel olarak, etki alanı hizmetleri, birden fazla toplamla koordinasyon veya çalışma gerektiren iş kurallarınız / mantığınız olduğunda kullanışlıdır. Mantık yalnızca bir toplamı içeriyorsa, toplamın varlıkları üzerinde bir yöntemde olması gerekir.

Uygulama Hizmetlerinde Depolar

Bu nedenle, örneğin, örneğinizde, ilk seçeneğinizi tercih ediyorum - ancak etki alanı hizmetiniz api'den gelen ham verileri kabul ettiğinden bile iyileştirme için yer var - etki alanı hizmeti neden yapısını bilmeli data? Ek olarak, veriler yalnızca tek bir toplamla ilişkili görünmektedir, bu nedenle bunun için bir etki alanı hizmeti kullanmanın sınırlı bir değeri vardır - genellikle doğrulamayı varlık yapıcısının içine koyarım. Örneğin

postAction(data){

  Entity entity = new Entity(data.name, data.surname);

  this.repository.persist(entity);

  // ...
}

geçersizse ve bir istisna atsın. Uygulama çerçevenize bağlı olarak, istisnayı yakalamak ve onu api türü için uygun yanıtla eşleştirmek için tutarlı bir mekanizmaya sahip olmak basit olabilir - örneğin bir REST api için 400 durum kodu döndürün.

Etki Alanı Hizmetlerinde Depolar

Yukarıdakilere bakılmaksızın, bazen bir etki alanı hizmetinde bir havuz enjekte etmek ve kullanmak yararlı olabilir, ancak yalnızca depolarınız yalnızca toplu kökleri kabul edip geri gönderecek şekilde uygulandıysa ve ayrıca birden fazla toplama içeren mantığı çıkardığınız zaman kullanışlıdır. Örneğin

postAction(data){

  this.domainService.doSomeBusinessProcess(data.name, data.surname, data.otherAggregateId);

  // ...
}

Etki alanı hizmetinin uygulanması şöyle gözükecektir:

doSomeBusinessProcess(name, surname, otherAggregateId) {

  OtherEntity otherEntity = this.otherEntityRepository.get(otherAggregateId);

  Entity entity = this.entityFactory.create(name, surname);

  int calculationResult = this.someCalculationMethod(entity, otherEntity);

  entity.applyCalculationResultWithBusinessMeaningfulName(calculationResult);

  this.entityRepository.add(entity);

}

Sonuç

Burada anahtar alanı hizmeti olduğunu kapsüller yerde dilin parçası olan bir süreç. Rolünü yerine getirmek için depoları kullanması gerekir - ve bunu yapmak için kesinlikle iyi.

Ancak bir depoyu bir yöntemle sarılmış bir etki alanı hizmeti eklemek, çok persistaz değer katar.

Bu temelde, eğer uygulama servisiniz yalnızca tek bir toplamla çalışmayı gerektiren bir kullanım durumu ifade ediyorsa, depoyu doğrudan uygulama servisinden kullanmakta sorun yoktur.


Pekala, eğer iş kurallarım varsa (Şartname Örüntüsü kurallarını kabul ediyorsam), eğer sadece bir varlık ile ilgiliyse, o varlığın onaylanmasından başka ne yapmalıyım? Kullanıcı varlığının içindeki iyi bir kullanıcı posta biçimini kontrol etmek gibi iş kurallarına uymak garip görünüyor. Öyle değil mi? Küresel yanıtla ilgili olarak, teşekkür ederim. “Uygulanacak varsayılan kural” olmadığı anlaşıldı ve gerçekten de kullanım alanlarımıza bağlı. Ben de bu işin tüm ayırt etmek yapacak işlerimiz var
mfrachet

2
Açıklığa kavuşturmak için, işletmeye ait kurallar yalnızca o işletmenin sorumluluğunda olan kurallardır. Kabul ediyorum, iyi bir kullanıcı e-posta biçimini kontrol etmek, Kullanıcıya ait olduğunu hissetmiyor. Şahsen, böyle bir doğrulama kuralını bir e-posta adresini temsil eden bir Değer Nesnesine koymak isterim. Kullanıcının EmailAddress türünde bir özelliği olur ve EmailAddress yapıcısı bir dizeyi kabul eder ve dizenin istenen biçimle eşleşmemesi durumunda bir istisna atar. Ardından, bir e-posta adresi kaydetmesi gereken diğer varlıklarda EmailAddress ValueObject'i yeniden kullanabilirsiniz.
Chris Simon,

Tamam, şimdi Neden Değer Nesnesini kullandığımı anladım. Ancak, değer nesnesinin, biçimi yöneten iş kuralı olan bir özelliği gösterdiği anlamına gelir?
mfrachet

1
Değer Nesneleri değişmez olmalıdır. Genel olarak bu, yapıcıyı ilklendirdiğiniz ve doğruladığınız anlamına gelir ve herhangi bir özellik için public get / private set düzenini kullanın. Ama eşitlik, ToString süreci vs. örneğin tanımlamak için dil yapıları kullanabilir kacper.gunia.me/ddd-building-blocks-in-php-value-object veya github.com/spring-projects/spring-gemfire-examples/ blob / master /…
Chris Simon

Teşekkürler @ ChrisSimon, nihayet ve sadece teori değil, kod içeren gerçek bir yaşam DDD durumuna cevap. 5 gün boyunca SO'yu ve web'i bir toplamın yaratılması ve kaydedilmesi için işlevsel bir örnek olarak geçirdim ve bu, bulduğum en açık açıklama.
e_i_pi 25:17

2

Kabul edilen cevaplarla ilgili bir sorun var:

Etki alanı modelinin depoya bağımlı olmasına izin verilmez ve etki alanı hizmeti etki alanı modelinin bir parçasıdır -> etki alanı hizmeti depoya bağlı olmamalıdır.

Bunun yerine yapmanız gereken, zaten uygulama hizmetinde olan iş mantığı yürütmesi için gereken tüm varlıkları bir araya getirmek ve ardından modelinize örnek nesneler sağlamaktır.

Örneğinize göre şöyle görünebilir:

class ApplicationService{

  constructor(domainService, repository){
    this.domainService = domainService
    this.repositoryA = repositoryA
    this.repositoryB = repositoryB
    this.repositoryC = repositoryC
  }

  // any parsing and/or pre-business validation already happened in controller or whoever is a caller
  executeUserStory(data){
    const entityA = this.repositoryA.get(data.criterionForEntityA)
    const entityB = this.repositoryB.get(data.criterionForEntityB)

    if(this.domainService.validateSomeBusinessRules(entityA, entityB)){
      this.repositoryC.persist(new EntityC(entityA.name, entityB.surname))
    }
    // ...
  }
}

Bu nedenle, kural: Domain modeli dış katmanlara bağlı değildir.

Alan hizmetine vs Başvuru itibaren bu makalede :

  • Etki alanı hizmetleri, uygulama hizmetleri olarak bir API sağlama amaçlı bir cephe olduğu için çok ayrıntılıdır.

  • Etki alanı hizmetleri, bir varlık veya değer nesnesine doğal olarak yerleştirilemeyen etki alanı mantığı içerirken, uygulama hizmetleri etki alanı mantığının yürütülmesini düzenler ve hiçbir etki alanı mantığı uygulamazlar.

  • Etki alanı hizmeti yöntemleri, işlenenler ve dönüş değerleri gibi başka etki alanı öğelerine sahipken, uygulama hizmetleri kimlik değerleri ve ilkel veri yapıları gibi önemsiz işlenenler üzerinde çalışır.

  • Uygulama hizmetleri, etki alanı mantığını yürütmek için gereken altyapı hizmetlerine bağımlılıkları beyan eder.


1

Hizmetleriniz ve nesneleriniz tutarlı bir sorumluluk kümesini kapsamadıkça, kalıplarınızın hiçbiri de iyidir.

Öncelikle, etki alanı nesnenizin ne olduğunu söyleyin ve etki alanı dilinde neler yapabileceğini konuşun. Eğer geçerli ya da geçersiz olabilirse, neden bunu etki alanı nesnesinin bir özelliği olarak göremiyorsunuz?

Örneğin, nesnelerin geçerliliği sadece başka bir nesne anlamında anlamlıysa, o zaman belki bir dizi hizmete dahil edilebilecek bir “etki alanı nesneleri için doğrulama kuralı X” sorumluluğu vardır.

Bir nesneyi onaylamak, iş kurallarına uymayı gerektiriyor mu? Muhtemelen değil. 'Nesneleri saklama' sorumluluğu normal olarak ayrı bir depo nesnesine gider.

Şimdi, bir dizi sorumluluğu kapsayan, bir nesne yaratan, doğrulayan ve geçerliyse saklamak istediğiniz bir işleminiz var.

Bu işlem etki alanı nesnesine özgü mü? Öyleyse onu nesnenin bir parçası yap.ExamQuestion.Answer(string answer)

Alan adınızın başka bir bölümüne uyuyor mu? oraya koyBasket.Purchase(Order order)

ADM REST hizmetlerini tercih eder misiniz? Tamam ozaman.

Controller.Post(json) 
{ 
    parse(json); 
    verify(parsedStruct); 
    save(parsedStruct); 
    return 400;
}
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.