Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/php/232.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Php 如何在事件存储中序列化/反序列化事件,在事件存储中存储聚合根是否很糟糕?_Php_Domain Driven Design_Cqrs_Event Sourcing - Fatal编程技术网

Php 如何在事件存储中序列化/反序列化事件,在事件存储中存储聚合根是否很糟糕?

Php 如何在事件存储中序列化/反序列化事件,在事件存储中存储聚合根是否很糟糕?,php,domain-driven-design,cqrs,event-sourcing,Php,Domain Driven Design,Cqrs,Event Sourcing,我正在尝试用PHP制作CQRS事件源应用程序。我想知道,将聚合根(AbstractItem,在下面的示例中)放入在db中序列化的事件中可以吗?(我想没有,但有什么替代方案?)例如,我有一个带有此句柄方法的AddItemToCartCommand命令处理程序: public function handle(Command $command) { Assertion::isInstanceOf($command, AddItemToCartCommand::class

我正在尝试用PHP制作CQRS事件源应用程序。我想知道,将聚合根(
AbstractItem
,在下面的示例中)放入在db中序列化的事件中可以吗?(我想没有,但有什么替代方案?)例如,我有一个带有此句柄方法的
AddItemToCartCommand
命令处理程序:

    public function handle(Command $command)
    {
        Assertion::isInstanceOf($command, AddItemToCartCommand::class);

        $cart = $this->loadCart($command->getCartId());
        $item = $this->loadItem($command->getItemId());

        $cart->addItem($item);

        $this->eventRepository->save($cart);
    }
Cart
AbstractItem
是聚合根。 My
Cart
AR的实现方式如下:

class Cart extends AggregateRoot
{

    /** @var UuidInterface */
    private $customerId;

    /** @var AbstractItem[] */
    private $items;

    public function __construct(UuidInterface $cartId, UuidInterface $customerId)
    {
        $this->apply(new EmptyCartCreated($cartId, $customerId));
    }

    public function addItem(AbstractCartItem $item)
    {
        $this->apply(new ItemToCartAdded($this->getId(), $item));
    }

    protected function applyEmptyCartCreated(EmptyCartCreated $event)
    {
        $this->setId($event->getCartId());
        $this->customerId = $event->getCustomerId();
    }

    protected function applyItemToCartAdded(ItemToCartAdded $event)
    {
        $item                                 = $event->getItem();
        $this->items[(string) $item->getId()] = $item;
    }
}
现在,问题在于具有以下结构的
ItemToCartAdded
事件:

class ItemToCartAdded extends AbstractDomainEvent
{

    /** @var UuidInterface */
    protected $cartId;

    /** @var AbstractItem */
    protected $item;

    public function __construct(UuidInterface $cartId, AbstractItem $item)
    {
        $this->cartId = $cartId;
        $this->item   = $item;
    }

    public function getCartId(): UuidInterface
    {
        return $this->cartId;
    }

    public function getItem(): AbstractItem
    {
        return $this->item;
    }
}
也许我更应该有一些DTO对象来保存
AbstractItem
数据,而不是
AbstractItem
AR本身在
ItemToCartAdded
事件中。然后,我将在
applyItemToCartAdded
方法中根据这些DTO数据实例化新的
AbstractItem
对象

但由于item是抽象类,我不知道需要实例化什么实现。当然,我可以在那个DTO中有类名,所以我知道,然后以某种方式破解它。但这似乎有点过分,因为我在
AbstractItem
上有私有构造函数,并在特定实现中使用工厂方法

另一方面,序列化整个聚合根会导致序列化出现问题:当我序列化AR时,我需要在某个点进行反序列化,但我该如何做呢?我知道有反射,但它是丑陋的黑客绕过我的AR验证反序列化,因此我可以结束某种程度上无效的AR可能陷入噩梦。还是不

我应该如何序列化和反序列化我的事件来很好地解决这个问题?也许我可以在
ItemToCartAdded
事件中只添加这两个聚合的ID,但这样我就无法将该事件应用到购物车AR,从而使其具有
AbstractItem
s列表(最终保护我的一些不变量)。我最终只会得到购物车AR中的项目id列表,因为我当然无法访问AR中的存储库

我的问题在哪里,或者我做错了什么


顺便说一句,我使用这个库的分支:

简单答案-您不会将聚合放入事件中

事件表示状态的更改。与其存储聚合的当前状态,不如捕获更改的内容。这是你的活动。在您的情况下,添加项目的id将是所需的最低信息。顺便说一句,为了方便起见,我喜欢在活动中添加更多信息

为什么在事件中存储AR不好


事件是不可变的。AR根据定义是可变的。AR也被封装,这使得序列化充其量也很尴尬。

答案很简单-您不会将聚合放入事件中

事件表示状态的更改。与其存储聚合的当前状态,不如捕获更改的内容。这是你的活动。在您的情况下,添加项目的id将是所需的最低信息。顺便说一句,为了方便起见,我喜欢在活动中添加更多信息

为什么在事件中存储AR不好

事件是不可变的。AR根据定义是可变的。AR也被封装,这使得串行化充其量也很尴尬

我正在尝试用PHP制作CQRS事件源应用程序。我想知道,将聚合根(以下示例中的AbstractItem)放入在db中序列化的事件中可以吗?(我想没有,但还有什么选择?)

不,你不能。我同意@Codescribler。 我认为你的设计有问题

而Cart和AbstractItem是聚合根

为什么要将
AbstractItem
设置为
Aggregate root
AbstractItem
保护什么不变量?它处理什么
命令
,产生什么
事件?这是您的主要设计问题。你应该根据你的
通用语言
来命名它,比如
产品
,或者如果你需要一个更抽象的名字,那么
CartItem

Product
是聚合根,但位于不同的有界上下文中,如
Inventory
。如果不需要保护任何不变量,它也可以是CRUD实体。在该BC中,它处理从您的商店中创建、修改和删除产品的操作

您现在处于
排序有界上下文中
,应该查看此BC中的域规则。那么,你的规则是什么?您是否允许将
库存
BC中的价格修改传播到
购物车
中的项目?我不这么认为。您的客户会非常生气,至少在没有任何通知的情况下不会,因为他们将商品添加到购物车中,价格发生了变化。因此,让我们假设,一旦产品被添加到购物车中,价格就会冻结。因此,它将是不变的

现在谈谈
购物车
。您需要关于添加到
购物车中的
产品
的哪些信息来保护您的不变量?什么是
Cart
不变量?嗯,你可以有一个最大的
订单
价值,比如10000美元。因此,您需要识别
产品
产品价格
。 因此,
CartItem
是一个不可变的
Value对象
,包含
ProductId
ProductPrice
。 因此,您的代码应该如下所示:

namespace Domain\Ordering;

class Cart
{
    /** @var CartItem[] */
    private $items = [];

    const MAXIMUM_CART_VALUE = 10000;

    public function handleAddItemToCart(AddItemToCart $command)
    {
        if ($this->getTotalCartValue() + $command->getItem()->getTotalItemPrice() > self::MAXIMUM_CART_VALUE) {
            throw new \Exception(sprintf("A cart value cannot be more than %d USD", self::MAXIMUM_CART_VALUE));
        }

        yield new AnItemWasAddedToCart($command->getCartId(), $command->getItem());
    }

    public function applyAnItemWasAddedToCart(AnItemWasAddedToCart $event)
    {
        $this->items[] = $event->getItem();
    }

    private function getTotalCartValue()
    {
        return array_reduce($this->items, function (float $acc, CartItem $item) {
            return $acc + $item->getTotalItemPrice();
        }, 0.0);
    }
}

class AddItemToCart implements Command
{

    /**
     * @var CartId
     */
    private $cartId;
    /**
     * @var CartItem
     */
    private $item;

    public function __construct(
        CartId $cartId,
        CartItem $item
    )
    {
        $this->cartId = $cartId;
        $this->item = $item;
    }

    public function getCartId(): CartId
    {
        return $this->cartId;
    }

    public function getItem(): CartItem
    {
        return $this->item;
    }
}

class AnItemWasAddedToCart implements Event
{
    /**
     * @var CartId
     */
    private $cartId;
    /**
     * @var CartItem
     */
    private $item;

    public function __construct(
        CartId $cartId,
        CartItem $item
    )
    {
        $this->cartId = $cartId;
        $this->item = $item;
    }

    public function getCartId(): CartId
    {
        return $this->cartId;
    }

    public function getItem(): CartItem
    {
        return $this->item;
    }
}

class CartId
{
    //...
}

class ProductId
{
    //...
}

class CartItem
{
    /**
     * @var ProductId
     */
    private $productId;
    /**
     * @var float
     */
    private $pricePerUnit;
    /**
     * @var int
     */
    private $quantity;

    public function __construct(
        ProductId $productId,
        float $pricePerUnit,
        int $quantity
    )
    {
        $this->productId = $productId;
        $this->pricePerUnit = $pricePerUnit;
        $this->quantity = $quantity;
    }

    public function getProductId(): ProductId
    {
        return $this->productId;
    }

    public function getTotalItemPrice()
    {
        return $this->pricePerUnit * $this->quantity;
    }
}
我正在尝试用PHP制作CQRS事件源应用程序。我想知道,将聚合根(以下示例中的AbstractItem)放入在db中序列化的事件中可以吗?(我想没有,但还有什么选择?)

不,你不能。我同意@Codescribler。 我认为你的设计有问题

而Cart和AbstractItem是聚合根

为什么要将
AbstractItem
设置为
Aggregate root
AbstractItem
保护什么不变量?什么