这取决于您的特定域,但这个不变量很可能不是 EventAggregate 的责任。
事实上,不变量应该在较低级别强制执行:在 Value 对象级别,由域服务执行;我们称之为EventTypeValidator。
此域服务可以具有以下形式:
interface EventTypeValidator
{
public function isEventTypeAllowedInCategory(string $eventCategory, string $eventType): bool;
}
根据管理此关联的方式,可能会有更多实现。如果关联是硬编码的,那么实现可能是这样的:
//somewhere in the Infrastructure layer
class EventTypeValidatorByMap implements EventTypeValidator
{
public function isEventTypeAllowedInCategory(string $eventCategory, string $eventType): bool
{
return $this->isEventInCategory('AnyCategory', $eventType) || $this->isEventInCategory($eventCategory, $eventType);
}
private function isEventInCategory(string $eventCategory, string $eventType): bool
{
$category = $this->getMap()[$eventCategory];
return (in_array($eventType, $category));
}
private function getMap()
{
return [
'WeatherCategory' => [
'RainEvent',
'SunEvent',
],
'CompetitionCategory' => [
'FootbalMatch',
'TennishMatch',
],
'AnyCategory' => [
'RainEvent',
'SunEvent',
'FootbalMatch',
'TennishMatch',
],
];
}
}
另一方面,如果关联是在数据库中管理的,例如在另一个有界上下文中,域服务可能如下所示:
//somewhere in the Infrastructure layer
class EventTypeValidatorByDatabase implements EventTypeValidator
{
//...
// the database PDO get's injected in the constructor
public function isEventTypeAllowedInCategory(string $eventCategory, string $eventType): bool
{
//create a query that returns true if the $eventType is allowed to be in $eventCategory and false otherwise
}
}
关于值对象至少有两种设计:两个独立的值对象和一个值对象。
我希望类型和类别有两个值对象:
在应用层中,在调用 EventAggregate 之前,会调用 Domain 服务来验证来自 UI 的关联:
class SomeApplicationService
{
/** @var EventTypeValidator */
private $eventTypeValidator;
public function __construct(EventTypeValidator $eventTypeValidator)
{
//the concrete class is resolved by the Dependency injection container
$this->eventTypeValidator = $eventTypeValidator;
}
public function createAnEvent(string $eventId, string $eventCategory, string $eventType)
{
if(!$this->eventTypeValidator->isEventTypeAllowedInCategory($eventCategory, $eventType)){
throw new \Exception(sprintf("Event type %s may not be in the category %s", $eventType, $eventCategory));
}
$event = new Event; //the Aggregate
$event->create($eventId, $eventCategory, $eventType);
$this->repository->persistEvent($event);
}
}
如果将 Event 类型和类别实现为单个 Value 对象,即
class CategorisedEventType
{
/** @var string */
private $eventCategory;
/** @var string */
private $eventType;
public function __construct(string $eventCategory, string $eventType)
{
$this->eventCategory = $eventCategory;
$this->eventType = $eventType;
}
public function getEventCategory(): string
{
return $this->eventCategory;
}
public function getEventType(): string
{
return $this->eventType;
}
}
然后可以通过注入EventTypeValidator 域服务在Factory 中提取此验证,如下所示:
//defined in the Domain layer
class CategorisedEventTypeFactory
{
/** @var EventTypeValidator */
private $eventTypeValidator;
public function __construct(EventTypeValidator $eventTypeValidator)
{
$this->eventTypeValidator = $eventTypeValidator;
}
public function factory(string $eventCategory, string $eventType): CategorisedEventType
{
if(!$this->eventTypeValidator->isEventTypeAllowedInCategory($eventCategory, $eventType))
{
throw new \Exception(sprintf("Event type %s may not be in the category %s", $eventType, $eventCategory));
}
return new CategorisedEventType($eventCategory, $eventType);
}
}
然后,应用程序服务可能如下所示:
class SomeApplicationService
{
/** @var CategorisedEventTypeFactory */
private $eventTypeFactory;
public function __construct(CategorisedEventTypeFactory $eventTypeFactory)
{
$this->eventTypeFactory = $eventTypeFactory;
}
public function createAnEvent(string $eventId, string $eventCategory, string $eventType)
{
$eventTypeAndCategory = $this->eventTypeFactory->factory($eventCategory, $eventType);
$event = new Event; //the Aggregate
$event->create($eventId, $eventTypeAndCategory);
$this->repository->persistEvent($event);
}
}
我更喜欢具有单个 Value 对象和 Factory 的设计,因为它将域逻辑移动到域层。
如果摆脱EventTypeValidator 接口并仅使用具体类,如果其他实现不适用(即它始终是硬编码的关联),则可以简化事情。