这里的问题是关于如何正确处理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,
],
))
}
}
关于changePassword
和changeCountry
方法的问题:
- 应该在
$user->changeCountry()
中调用save
吗?这样的行为方法(changePassword
和changeCountry
)是否应该在更改对象后将其持久化到存储中? - 如果应该,那么我们是否应该将其封装在transaction中?我想是的,因为我们这里有DomainEvent。 如果没有DomainEvent,我们还应该将对象持久化到存储中吗?在这种情况下,这个方法(
- 或者也许我们应该只提出一个域事件
UserProfileChanged
与参数像$oldInfo
$newInfo
,但对我来说,这一个缺乏域。
changeCountry
或moveToCountry
)用于表达行为,但它应该启动事务吗?这款有什么建议吗?关键是使事情正确,但不需要大量的持久性调用。我知道我不应该考虑域层上的持久性,但获得20
sql更新而不是1
不是一个好的解决方案。
域对象不应该与持久性有关。Repositories
负责聚合持久性。您可以从存储库中获取聚合,调用聚合上的方法,并在应用程序层中再次持久化它。这将导致两次数据库调用;一个SELECT和一个UPDATE -在一个事务中卷起。
var user = repository.GetById(userId);
user.MoveToCountry(country);
repository.Update(user);
我知道这只是一个例子,但要确保你抓住了用户的意图。这个更新方法看起来像是在构建一个CRUD应用程序,但实际上是在试图对意图进行逆向工程——当您重构等时,这可能是有意义的。