在我看到的大多数示例中,命令和事件都表示为类。这意味着您必须使用name属性编写CorrectNameCommand类,并使用name属性编写NameCorrectedEvent类。鉴于在大多数情况下命令和事件都被序列化和反序列化并发送给其他方(编译时类型安全),这个显式类相对于更通用的类有什么优势?
示例:
带有Name(表示命令类型)的Command类,应该处理命令的ag的键以及任何其他参数的对象或名称/值对的数组。
Event类基本相同(也许我们可以将共享部分放在CommandEventBase类中)。
命令(和事件)处理程序现在要检查命令(事件)的名称而不是其类类型,并且必须依赖于列表中参数的正确性(就像反序列化器必须依赖于序列化的那样)格式是正确的。)
这是一个好方法吗?如果是,为什么不在样本和教程中使用它?如果没有,有什么问题?
答案 0 :(得分:6)
<强>复制强>
值得注意的是,当序列化命令和事件时,编译时安全性会丢失,但在静态类型语言中,我仍然更喜欢强类型的命令和事件类型。
这样做的原因是它为您提供了一个代码库,负责解释消息元素。序列化往往是(类型)安全的;反序列化是您可能遇到问题的地方。
尽管如此,我更愿意在一个地方处理任何此类问题,而不是在整个代码库中展开。
这与事件特别相关,因为您可能有多个事件处理程序处理相同类型的事件。如果您将事件视为弱类型字典,则您需要在每个事件处理程序中复制 Tolerant 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奥斯陆2015),他似乎已经转变了##到F#也是如此。