Howa以及在哪里扣留两个ValueObject之间的关系逻辑

时间:2018-01-08 15:32:46

标签: php domain-driven-design

我有一个名为Event的实体使用两个ValueObjects EventTypeEventCategory。每个允许的类别都有一个允许的EventType子集。

WeatherCategory =>     RainEvent
                       SunEvent
CompetitionCategory => FootbalMatch
                       TennishMatch
AnyCategory     =>     RainEvent
                       SunEvent
                       FootbalMatch
                       TennishMatch

当然我想以某种方式描述这种关系。起初我使用外部Validator检查实体的两个ValueObject之间关系的有效性。现在我需要根据特定类别获取事件集。

基本上,我所拥有的是一个存储库合约,它为我提取事件,按类别过滤。不幸的是我的基础设施没有类别信息,所以我必须根据EventType列表映射查询。

我想知道 - 根据ddd - 我是否应该将两个ValueObject之间的映射信息添加到其中一个中,如果我最好将它添加到我的Entity中,只留下两个valueobjects没有任何关系信息或将它放入其他课程(不知道这个)。

1 个答案:

答案 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接口并仅使用具体类,如果其他实现不适用(即,它始终是硬编码的关联),事情可以简化。