DDD(领域驱动设计)事件和持久性


DDD (Domain Driven Design) events and persistance

这里的问题是关于如何正确处理DDD DE,让我们说我们有这个非常简单的例子(我知道对于简单的项目DDD是不需要的,但这只是一个例子)。我们有User(聚合根)和UserProfile(值对象),所以表是:

用户

- id
- email
- password

user_profile

- country_id
- first_name
- second_name
正如我们所知,我们的代码应该表达行为,而不应该以数据为中心,因此,例如在我们的六边形一侧(UI浏览器),我们有这个应用程序服务来处理这种情况:
//UserService application service
public static function update($formDTO)
$user->changeCountry($form->country);
$user->changePassword($form->password);
$user->attributes = $form->userData();
$user->save(); // here we use AR not DDD ORM like; you can see this as entityManager->flush(); if you like Hibernate or Doctrine.

方法changeCountry看起来像:

 public function changeCountry($country)
 {
     if ($this->country->id != $country->id) {
          $oldCountry = $this->country;
          $this->moveToCountry($country);
          ...->eventsManager->raise(new UserMovedToCountryEvent(
              [
                  'user' => $this,
                  'oldCountry' => $oldCountry,
                  'newCountry' => $newCountry,
              ],
          ))
     }
 }

关于changePasswordchangeCountry方法的问题:

  • 应该在$user->changeCountry()中调用save吗?这样的行为方法(changePasswordchangeCountry)是否应该在更改对象后将其持久化到存储中?
  • 如果应该,那么我们是否应该将其封装在transaction中?我想是的,因为我们这里有DomainEvent。
  • 如果没有DomainEvent,我们还应该将对象持久化到存储中吗?在这种情况下,这个方法(changeCountrymoveToCountry)用于表达行为,但它应该启动事务吗?这款有什么建议吗?
  • 或者也许我们应该只提出一个域事件UserProfileChanged与参数像$oldInfo $newInfo,但对我来说,这一个缺乏域。

关键是使事情正确,但不需要大量的持久性调用。我知道我不应该考虑域层上的持久性,但获得20 sql更新而不是1不是一个好的解决方案。

域对象不应该与持久性有关。Repositories负责聚合持久性。您可以从存储库中获取聚合,调用聚合上的方法,并在应用程序层中再次持久化它。这将导致两次数据库调用;一个SELECT和一个UPDATE -在一个事务中卷起。

var user = repository.GetById(userId);
user.MoveToCountry(country);
repository.Update(user);

我知道这只是一个例子,但要确保你抓住了用户的意图。这个更新方法看起来像是在构建一个CRUD应用程序,但实际上是在试图对意图进行逆向工程——当您重构等时,这可能是有意义的。