我有一个名为Event
的实体使用两个ValueObjects EventType
和EventCategory
。每个允许的类别都有一个允许的EventType
子集。
即
WeatherCategory => RainEvent
SunEvent
CompetitionCategory => FootbalMatch
TennishMatch
AnyCategory => RainEvent
SunEvent
FootbalMatch
TennishMatch
当然我想以某种方式描述这种关系。起初我使用外部Validator
检查实体的两个ValueObject之间关系的有效性。现在我需要根据特定类别获取事件集。
基本上,我所拥有的是一个存储库合约,它为我提取事件,按类别过滤。不幸的是我的基础设施没有类别信息,所以我必须根据EventType
列表映射查询。
我想知道 - 根据ddd - 我是否应该将两个ValueObject之间的映射信息添加到其中一个中,如果我最好将它添加到我的Entity中,只留下两个valueobjects没有任何关系信息或将它放入其他课程(不知道这个)。
答案 0 :(得分:1)
这取决于您的特定域名,但最可能的是此不变量不是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
}
}
关于值对象至少有两种设计:使用两个单独的值对象和一个值对象。
我想为类型和类别设置两个值对象:
在Application层中,在调用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);
}
}
如果将事件类型和类别实现为单个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;
}
}
然后通过注入Factory
域服务,可以在EventTypeValidator
中提取此验证,如下所示:
//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);
}
}
然后,Application服务可能如下所示:
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
接口并仅使用具体类,如果其他实现不适用(即,它始终是硬编码的关联),事情可以简化。