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

时间:2014-05-30 15:40:44

标签: php hibernate doctrine domain-driven-design

这里的问题是关于如何正确处理DDD DE,假设我们有这个非常简单的例子(我知道对于简单的项目,不需要DDD,但这只是一个例子)。我们有User(Aggregate root)和UserProfile(Value Object),所以表是:

用户

- 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方法的问题:

  • 我们应该在save中致电$user->changeCountry()吗?此类行为方法(changePasswordchangeCountry)在更改后是否会将对象保留在存储中?
  • 如果应该将它包装在交易中?我想是的,因为我们在这里有DomainEvent。
  • 如果没有DomainEvent,我们还应该将对象保存到存储中吗?在这种情况下,此方法(changeCountrymoveToCountry)用于表达行为但是应该启动事务吗?这个是否有任何推荐?
  • 或许我们只应该使用UserProfileChanged $oldInfo之类的参数来发起一个域名事件$newInfo,但对于我而言,这个事件缺少域名。

重点是使事情正确,但没有不必要的持久性调用。我知道我不应该考虑域层的持久性,但是获取20 sql更新而不是1并不是一个好的解决方案。

1 个答案:

答案 0 :(得分:0)

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

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

我知道这只是一个例子,但请确保捕获用户的意图。这种更新方法看起来像是在构建一个CRUD应用程序,但是在事实发生后正试图对意图进行逆向工程 - 这在你重构等时可能有意义。