CQRS 命令和事件作为通用 类?
CQRS Commands and Events as generic classes?
在我看到的大多数示例中,命令和事件都表示为 classes。这意味着您必须编写名称为 属性 的 CorrectNameCommand class 和名称为 属性 的 NameCorrectedEvent class。鉴于在大多数情况下命令和事件都被序列化和反序列化并发送给其他方(编译时类型安全),这种显式 classes 相对于更通用的 class 的优势是什么?
示例:
一个命令class,带有一个名称(表示命令的类型)、应该处理该命令的 ag 的键和一个对象数组或 name/value 对用于任何其他参数.
一个事件class本质上是一样的(也许我们可以把共享的部分放在一个CommandEventBaseclass)。
命令(和事件)处理程序现在必须检查命令(事件)的名称而不是其 class 类型,并且必须依赖列表中参数的正确性(如反序列化器必须依赖于序列化格式是正确的)。
这是个好方法吗?如果是,为什么示例和教程中没有使用它?如果不是,问题是什么?
重复
当命令和事件被序列化时,编译时安全性会丢失,这是一个公平的观点,但在静态类型语言中,我仍然更喜欢强类型的命令和事件类型。
这样做的原因是它为您提供了一段负责解释消息元素的代码库。序列化往往是相当(类型)安全的;反序列化是你可能会遇到问题的地方。
不过,我更愿意在一个地方处理任何此类问题,而不是分散在整个代码库中。
这与事件特别相关,因为您可能有多个事件处理程序处理同一类型的事件。如果您将事件视为弱类型字典,则需要在每个事件处理程序中重复 Tolerant Reader 的实现。
另一方面,如果您将事件和命令视为强类型,您的反序列化器可以是您必须维护的单一容忍 Reader。
类型
综上所述,我能理解为什么您使用 C# 或 Java 等语言时,发现为每条消息定义不可变 DTO 的开销似乎很大:
public sealed class CorrectNameCommand
{
private readonly string userId;
private readonly string newName;
public CorrectNameCommand(string userId, string newName)
{
this.userId = userId;
this.newName = newName;
}
public string UserId
{
get { return this.userId; }
}
public string NewName
{
get { return this.newName; }
}
public override bool Equals(object obj)
{
var other = obj as UserName;
if (other == null)
return base.Equals(obj);
return object.Equals(this.userId, other.userId)
&& object.Equals(this.newName, other.newName);
}
public override int GetHashCode()
{
return this.userId.GetHashCode() ^ this.newName.GetHashCode();
}
}
确实,看起来工作量很大。
这就是我最近更喜欢使用其他语言来实现 CQRS 的原因。在 .NET 上,F# 是一个完美的选择,因为 以上所有代码都归结为一行代码:
type CorrectNameCommand = { UserId : string; NewName : string }
这就是我要做的,而不是四处传递弱类型词典。上次我听到 Greg Young 谈论 CQRS(NDC Oslo 2015)时,他似乎对 F# 也有 'converted'。
在我看到的大多数示例中,命令和事件都表示为 classes。这意味着您必须编写名称为 属性 的 CorrectNameCommand class 和名称为 属性 的 NameCorrectedEvent class。鉴于在大多数情况下命令和事件都被序列化和反序列化并发送给其他方(编译时类型安全),这种显式 classes 相对于更通用的 class 的优势是什么?
示例:
一个命令class,带有一个名称(表示命令的类型)、应该处理该命令的 ag 的键和一个对象数组或 name/value 对用于任何其他参数.
一个事件class本质上是一样的(也许我们可以把共享的部分放在一个CommandEventBaseclass)。
命令(和事件)处理程序现在必须检查命令(事件)的名称而不是其 class 类型,并且必须依赖列表中参数的正确性(如反序列化器必须依赖于序列化格式是正确的)。
这是个好方法吗?如果是,为什么示例和教程中没有使用它?如果不是,问题是什么?
重复
当命令和事件被序列化时,编译时安全性会丢失,这是一个公平的观点,但在静态类型语言中,我仍然更喜欢强类型的命令和事件类型。
这样做的原因是它为您提供了一段负责解释消息元素的代码库。序列化往往是相当(类型)安全的;反序列化是你可能会遇到问题的地方。
不过,我更愿意在一个地方处理任何此类问题,而不是分散在整个代码库中。
这与事件特别相关,因为您可能有多个事件处理程序处理同一类型的事件。如果您将事件视为弱类型字典,则需要在每个事件处理程序中重复 Tolerant Reader 的实现。
另一方面,如果您将事件和命令视为强类型,您的反序列化器可以是您必须维护的单一容忍 Reader。
类型
综上所述,我能理解为什么您使用 C# 或 Java 等语言时,发现为每条消息定义不可变 DTO 的开销似乎很大:
public sealed class CorrectNameCommand
{
private readonly string userId;
private readonly string newName;
public CorrectNameCommand(string userId, string newName)
{
this.userId = userId;
this.newName = newName;
}
public string UserId
{
get { return this.userId; }
}
public string NewName
{
get { return this.newName; }
}
public override bool Equals(object obj)
{
var other = obj as UserName;
if (other == null)
return base.Equals(obj);
return object.Equals(this.userId, other.userId)
&& object.Equals(this.newName, other.newName);
}
public override int GetHashCode()
{
return this.userId.GetHashCode() ^ this.newName.GetHashCode();
}
}
确实,看起来工作量很大。
这就是我最近更喜欢使用其他语言来实现 CQRS 的原因。在 .NET 上,F# 是一个完美的选择,因为 以上所有代码都归结为一行代码:
type CorrectNameCommand = { UserId : string; NewName : string }
这就是我要做的,而不是四处传递弱类型词典。上次我听到 Greg Young 谈论 CQRS(NDC Oslo 2015)时,他似乎对 F# 也有 'converted'。