Php 如何在事件存储中序列化/反序列化事件,在事件存储中存储聚合根是否很糟糕?
我正在尝试用PHP制作CQRS事件源应用程序。我想知道,将聚合根(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
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
是聚合根。
MyCart
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
保护什么不变量?什么