验证联系人在 Axon 中具有唯一的电子邮件
Validating a Contact has a Unique Email in Axon
我很想知道在使用 Axon 框架验证电子邮件字段对于联系人聚合的一组电子邮件是唯一的时,最佳实践方法是什么。
示例设置
ContactCreateCommand {
identifier = '123'
name = 'ABC'
email = 'info@abc.com'
}
ContactAggregate {
ContactAggregate(ContactCreateCommand cmd) {
//1. cannot validate email
AggregateLifecycle.apply(
new ContactCreatedEvent(//fields ... );
);
}
}
根据我对这可能如何实施的理解,我已经确定了多种可能的处理方法,但也许还有更多。
1.在 Aggregate
中什么都不做
这种方法强制要求(命令的)调用者在发送命令之前通过电子邮件查询联系人,允许几毫秒,最终一致性允许重复。
缺点:
- 命令的任何“调用者”都需要执行此验证检查,因为无法使用 Axon 查询处理程序在聚合内部执行此检查。
- 可能会发生重复,因此基于这些事件的所有预测都需要以某种方式处理这种重复
2。在单独的持久层中验证
这种方法引入了一个新的持久层,可以验证聚合内部的唯一性。
在 ContactAggregate 命令处理程序中 ContactCreateCommand 然后我们可以针对这个持久层发出查询(例如 table 在带有唯一索引的 postgres 中),我们可以根据包含所有集合的数据库验证电子邮件
缺点:
- 引入外部持久层(微服务外部)以保证联系人之间的唯一性
- 应在持久层中考虑扩展,使用高度扩展的聚合来解决这个问题可能会成为瓶颈
3。使用 Saga 和单例聚合
此方法通过引入最多只能有 1 个实例的聚合(例如,目标标识符始终相同)来增强以前的设置。这样我们创建了一个 'Singleton Aggregate' 只负责封装所有联系人电子邮件地址的集合。
ContactEmailValidateCommand {
identifier = 'SINGLETON_ID_1'
email='info@abc.com'
customerIdentifier = '123'
}
UniqueContactEmailAggregate {
@AggregateIdentifier
private String identifier;
Set<String> email = new HashSet<>();
on(ContactEmailValidateCommand cmd) {
if (email.contains(cmd.email) == false) {
AggregateLifecycle.apply(
new ContactEmailInvalidatedEvent(//fields ... );
} else {
AggregateLifecycle.apply(
new ContactEmailValidatedEvent(//fields ... );
);
}
}
}
完成此检查后,我们可以对 ContactEmailInvalidatedEvent 或 ContactEmailValidatedEvent 重新采取适当的行动,这可能会导致之后的联系无效。
这种方法的好处是它保持聚合本地的持久性,这可以提供更好的缩放(随着添加更多节点,存在更多具有本地管理集的聚合)。
缺点
- 相当多的样板来替换“创建唯一索引”
- 这种方法允许 'invalid' 联系人永远污染事件存储
- 'Singleton Aggregate' 很复杂,以确保它是真实的(也许有更简单或更好的方法)
- CreateContactCommand 的 'invoker' 必须检查 Saga 的结果
其他人如何解决这个问题?我觉得选项 2 可能是最简单的方法,但是还有其他选项吗?
您本质上要寻找的是基于集的验证(我认为 here 博客很好地解释了这个概念,以及如何在 Axon 中处理它)。简而言之,验证某个字段包含(或不包含)在一组数据中。在执行 CQRS 时,这会成为一个有点有趣的概念来推理,有几种解决方案(正如您已经描绘的那样)。
我认为最好的解决方案总结在您的第二个选项下,即为电子邮件地址使用专用的持久层。您只需创建一个仅包含电子邮件地址的非常简洁的模型,您将在发布 ContactCreateCommand
之前对其进行验证。请注意,此持久层属于命令模型,因为它用于执行业务验证。因此,您将介绍一个示例,其中您的命令模型中不仅有聚合,还有视图。正如您正确指出的那样,这个视图当然需要针对它的用例进行优化。也许引入一个在应用程序启动时创建的缓存不会太糟糕。
为确保此电子邮件地址视图尽可能最新,最明智的做法是确保它在与 ContactCreatedEvent
(我假设包含新电子邮件地址)时相同的事务中更新出版。您可以通过为您的“电子邮件地址视图”设置一个专用的事件处理组件来做到这一点,该组件通过 SubscribingEventProcessor
(SEP)进行更新。这将起作用,因为 SEP 由发布事件(您的聚合)的同一线程调用。
在发送命令之前查询此模型时,您有多种选择。例如,您可以使用仅对 ContactCreateCommand
做出反应的 MessageDispatchInterceptor
。或者,您引入一个 Handler Enhancer 专门用于反应 ContactCreateCommand
以执行此验证。或者,您引入另一个命令,例如 RequestContactCreationCommand
,它针对常规组件。该组件将处理命令、验证模型,如果获得批准,则发送 ContactCreateCommand
.
这是我对这种情况的两分钱,希望这对@vcetinick 有所帮助!
我很想知道在使用 Axon 框架验证电子邮件字段对于联系人聚合的一组电子邮件是唯一的时,最佳实践方法是什么。
示例设置
ContactCreateCommand {
identifier = '123'
name = 'ABC'
email = 'info@abc.com'
}
ContactAggregate {
ContactAggregate(ContactCreateCommand cmd) {
//1. cannot validate email
AggregateLifecycle.apply(
new ContactCreatedEvent(//fields ... );
);
}
}
根据我对这可能如何实施的理解,我已经确定了多种可能的处理方法,但也许还有更多。
1.在 Aggregate
中什么都不做这种方法强制要求(命令的)调用者在发送命令之前通过电子邮件查询联系人,允许几毫秒,最终一致性允许重复。
缺点:
- 命令的任何“调用者”都需要执行此验证检查,因为无法使用 Axon 查询处理程序在聚合内部执行此检查。
- 可能会发生重复,因此基于这些事件的所有预测都需要以某种方式处理这种重复
2。在单独的持久层中验证
这种方法引入了一个新的持久层,可以验证聚合内部的唯一性。
在 ContactAggregate 命令处理程序中 ContactCreateCommand 然后我们可以针对这个持久层发出查询(例如 table 在带有唯一索引的 postgres 中),我们可以根据包含所有集合的数据库验证电子邮件
缺点:
- 引入外部持久层(微服务外部)以保证联系人之间的唯一性
- 应在持久层中考虑扩展,使用高度扩展的聚合来解决这个问题可能会成为瓶颈
3。使用 Saga 和单例聚合
此方法通过引入最多只能有 1 个实例的聚合(例如,目标标识符始终相同)来增强以前的设置。这样我们创建了一个 'Singleton Aggregate' 只负责封装所有联系人电子邮件地址的集合。
ContactEmailValidateCommand {
identifier = 'SINGLETON_ID_1'
email='info@abc.com'
customerIdentifier = '123'
}
UniqueContactEmailAggregate {
@AggregateIdentifier
private String identifier;
Set<String> email = new HashSet<>();
on(ContactEmailValidateCommand cmd) {
if (email.contains(cmd.email) == false) {
AggregateLifecycle.apply(
new ContactEmailInvalidatedEvent(//fields ... );
} else {
AggregateLifecycle.apply(
new ContactEmailValidatedEvent(//fields ... );
);
}
}
}
完成此检查后,我们可以对 ContactEmailInvalidatedEvent 或 ContactEmailValidatedEvent 重新采取适当的行动,这可能会导致之后的联系无效。
这种方法的好处是它保持聚合本地的持久性,这可以提供更好的缩放(随着添加更多节点,存在更多具有本地管理集的聚合)。
缺点
- 相当多的样板来替换“创建唯一索引”
- 这种方法允许 'invalid' 联系人永远污染事件存储
- 'Singleton Aggregate' 很复杂,以确保它是真实的(也许有更简单或更好的方法)
- CreateContactCommand 的 'invoker' 必须检查 Saga 的结果
其他人如何解决这个问题?我觉得选项 2 可能是最简单的方法,但是还有其他选项吗?
您本质上要寻找的是基于集的验证(我认为 here 博客很好地解释了这个概念,以及如何在 Axon 中处理它)。简而言之,验证某个字段包含(或不包含)在一组数据中。在执行 CQRS 时,这会成为一个有点有趣的概念来推理,有几种解决方案(正如您已经描绘的那样)。
我认为最好的解决方案总结在您的第二个选项下,即为电子邮件地址使用专用的持久层。您只需创建一个仅包含电子邮件地址的非常简洁的模型,您将在发布 ContactCreateCommand
之前对其进行验证。请注意,此持久层属于命令模型,因为它用于执行业务验证。因此,您将介绍一个示例,其中您的命令模型中不仅有聚合,还有视图。正如您正确指出的那样,这个视图当然需要针对它的用例进行优化。也许引入一个在应用程序启动时创建的缓存不会太糟糕。
为确保此电子邮件地址视图尽可能最新,最明智的做法是确保它在与 ContactCreatedEvent
(我假设包含新电子邮件地址)时相同的事务中更新出版。您可以通过为您的“电子邮件地址视图”设置一个专用的事件处理组件来做到这一点,该组件通过 SubscribingEventProcessor
(SEP)进行更新。这将起作用,因为 SEP 由发布事件(您的聚合)的同一线程调用。
在发送命令之前查询此模型时,您有多种选择。例如,您可以使用仅对 ContactCreateCommand
做出反应的 MessageDispatchInterceptor
。或者,您引入一个 Handler Enhancer 专门用于反应 ContactCreateCommand
以执行此验证。或者,您引入另一个命令,例如 RequestContactCreationCommand
,它针对常规组件。该组件将处理命令、验证模型,如果获得批准,则发送 ContactCreateCommand
.
这是我对这种情况的两分钱,希望这对@vcetinick 有所帮助!