
最近,在Zinc Prod播客中,我和我的朋友们讨论了CQRS / ES模式及其在Elixir中实现的一些功能。 因为 我在工作中使用Laravel,如果不深入Internet并没有找到如何在此框架的生态系统中使用这种方法,这是一个罪过。
我邀请大家参加会议,我试图尽可能抽象地描述这个话题。
一些定义
CQRS (命令查询职责隔离)-将读写操作分配到单独的实体中。 例如,我们写入主数据库,从副本数据库读取。 CQRS。 事实和误解 -帮助您全面了解Zen CQRS。
ES (事件源)-存储一个实体或一组实体的所有状态更改。
CQRS / ES是一种体系结构方法,其中我们将实体状态更改的所有事件保存在事件表中,并向其中添加聚合和投影仪。
聚合 -在内存中存储制定业务逻辑决策(加快编写),制定决策(业务逻辑)和发布事件所需的属性。
投影仪 -侦听事件并将其写入单独的表或数据库(以加快读取速度)。

在战斗中
Laravel事件投影仪 -Laravel的CQRS / ES库
Larabank是采用CQRS / ES方法的存储库。 我们将其试用。
库配置将告诉您在哪里看,并告诉它是什么。 我们看一下event-projector.php文件。 描述工作的必要条件:
projectors
-套准投影仪;reactors
-套准反应堆。 Reactor-该库中的事件处理增加了副作用,例如,在该存储库中,如果您尝试超过提款限额的三倍,则会编写MoreMoneyNeeded事件,并向用户发送有关其财务困难的消息;replay_chunk_size
重播块的大小。 ES的功能之一是能够从事件中还原历史记录。 Laravel事件投影仪使用此设置为此类操作期间的内存泄漏做好了准备。
注意迁移。 除了标准的Laravel表,我们还有
stored_events
主ES表,其中包含几列用于元事件数据的非结构化数据,我们将事件类型存储在一行中。 重要列aggregate_uuid
存储aggregate_uuid
的uuid以接收与其相关的所有事件;accounts
-用户帐户的投影仪表,对于快速返回余额状态下的当前数据是必需的;transaction_counts
投影仪的用户交易次数表,对于快速返回已完成的交易次数是必需的。
现在,我建议您提出创建新帐户的请求。
开立账户
标准resource
路由描述AccountsController 。 我们对store
方法感兴趣
public function store(Request $request) { $newUuid = Str::uuid();
AccountAggregateRoot继承库AggregateRoot 。 让我们看一下控制器调用的方法。
persist
方法在事件投影器 stored_event_model
配置中指定的模型storeMany
以storeMany
调用storeMany
方法storeMany
在本例中为StoredEvent
public static function storeMany(array $events, string $uuid = null): void { collect($events) ->map(function (ShouldBeStored $domainEvent) use ($uuid) { $storedEvent = static::createForEvent($domainEvent, $uuid); return [$domainEvent, $storedEvent]; }) ->eachSpread(function (ShouldBeStored $event, StoredEvent $storedEvent) {
* 排队投影仪
投影仪AccountProjector和TransactionCountProjector实现了Projector
因此它们将与记录同步地响应事件。
好的,已经创建了一个帐户。 我建议考虑客户如何阅读它。
发票显示
如果帐户的投影仪实现了QueuedProjector界面,则用户将不会看到任何内容,除非依次处理该事件。
最后,我们将研究如何从帐户中补充和提取资金。
充值和提款
再次,查看AccountsController :
考虑AccountAggregateRoot
补充帐户时:
public function addMoney(int $amount) { $this->recordThat(new MoneyAdded($amount)); return $this; }
* AggregateRoot
提款时:
public function subtractMoney(int $amount) { if (!$this->hasSufficientFundsToSubtractAmount($amount)) {

结论
我试图描述在Laravel上的CQRS / ES中“入职”的过程尽可能不含水。 这个概念非常有趣,但并非没有功能。 实施之前,请记住以下几点:
- 最终的一致性;
- 最好在单独的域中使用DDD;您不应该完全按照这种模式构建大型系统;
- 对事件表的架构进行更改可能会非常痛苦;
- 负责任地,选择事件的粒度是值得的,事件越具体,表中的事件就越多,与它们一起工作将需要更多的资源。
我将很高兴注意到错误。