CQRS中的事件版本控制

时间:2013-05-13 11:27:36

标签: events domain-driven-design versioning cqrs

我们处于开发周期(asp.net mvc applciation)的某个阶段,我们需要对现有的命令和事件进行更改(比如添加/删除一些属性等)。

我一直在努力寻找一种在系统中引入命令/事件版本控制的方法。我已经在google / stackoverflow等上阅读了很多帖子,但我还是要看一个实现它的代码示例。在版本控制时是否应该遵循推荐的模式。如果是,任何示例/片段?

编辑:这是我用这个

走了多远
  1. 我已经向后版本化了我的事件,这样最新的事件将始终被称为相同,而过时的事件将添加一个后缀,如'_V1','_ V2'等。
  2. 所以,如果我有一个活动

    public class OrderSubmittedEvent : IDomainEvent
    {
        public int OrderId { get; private set; }
    
        public OrderSubmittedEvent(int orderId)
        {
            OrderId = orderId;
        }
    }
    

    如果我必须添加一些属性,我将上面的事件重命名为

    public class OrderSubmittedEvent_V1 : IDomainEvent
    {
        public int OrderId { get; private set; }
    
        public OrderSubmittedEvent_V1(int orderId)
        {
            OrderId = orderId;
        }
    }
    

    并引入另一个与我的原始事件同名的事件,但添加了属性,如此

    public class OrderSubmittedEvent : IDomainEvent
    {
        public int OrderId { get; private set; }
    
        public OrderSubmittedEvent(int version = 1, int orderId = 0, string customerName =  
                                   "Joe blogs", string address = "Earth")
        {
            OrderId = orderId;
            CustomerName = customerName;
            Address = address;
            CurrentVersion = version;
        }
    
        public static int LatestVersion
        {
            get { return 2; }
        }
    
        public int CurrentVersion { get; set; }
    
        public string CustomerName { get; set; }
        public string Address { get; set; }
    }
    

    我仍然需要继续更改发布此事件的代码,以包含新属性的值。

    1. 当我从事件存储中获取所有事件(例如,用于重放)时,任何给定的时间点,在反序列化后(在这种情况下为OrderSubmittedEvent),它们将始终具有相同的类型,其中新属性不属于旧的属性填充了默认值的事件。
    2. 在重播我的活动时,我让我的活动通过IEventUpgrader 首先验证事件是否是可用的最新版本。因为类型将始终是事件类型,所以此检查基于属性“LatestVersion”和“CurrentVersion”

      每个人都会想到这种方法?

      下一个待办事项

      1. 如果事件是旧版本,则发布'UpdateMYEVENT'事件
      2. 谢谢

5 个答案:

答案 0 :(得分:7)

通常您只需要对事件进行版本控制,您可以忽略这些命令,因为您不会将它们存储在事件存储中。

实现版本控制的方法很少..我的方法非常简单:

[Obsolete]
public class CompanyCreated
{
    public Guid Id { get; set; }
    public string Name { get; set; }
}

public class CompanyCreated_V2
{
    public Guid Id { get; set; }
    public string CompanyName { get; set; }
    public string TaxNumber { get; set; }
}

当您从事件存储中读取事件时,您需要处理事件从旧事件到新事件的转换。

另外,你需要知道你永远不会删除任何旧的事件类,因此我将它们装饰为Obsolete,让其他开发人员知道不要使用该事件。

答案 1 :(得分:4)

如果您只是添加&删除属性,可能不需要版本事件;只需忽略已删除的序列化属性,并为添加的属性使用合理的默认值。

答案 2 :(得分:1)

不可否认,我没有机会尝试以下方法,但我希望从第一天开始对版本进行烘焙:

由于完整的类型名称是相关的,我会选择命名空间。

namespace Primary.Messages.V1
{
    public class CompanyCreated
    {
        public Guid Id { get; set; }
        public string Name { get; set; }
    }
}

namespace Primary.Messages.V2
{
    public class CompanyCreated
    {
        public Guid Id { get; set; }
        public string Name { get; set; }
        public string TaxNumber { get; set; }
    }
}

这些可以在不同的集合中,你可以将旧的标记标记为过时的(如Sarmaad所建议的那样)。可能旧版本不一定是过时的。

有什么想法吗?

答案 3 :(得分:1)

对于混合事件和命令,我会保持谨慎。他们有不同的目的并解决不同的问题。

要更好地理解我的意思,请这样思考 命令更像是RESTful API,客户端与服务器之间的通信。 事件搜索更多是一种存储数据的方式。

两者都需要版本控制,以通过不变性提供向后兼容性,但这又是出于不同的原因。因此,实现和异常是不同的。

我绝对会推荐一本书getScaledMinimumScalingSpan(),以便为事件源系统的版本控制提供更多信息。

有关命令的更多信息,请查看Event Versioning by Greg Young,尤其是CQRS series

答案 4 :(得分:0)

我完全没有理由,同时考虑为什么需要事件版本控制的问题,更具体地说是在答案中建议的方式?

我只能想到两个用例

1-目前正在使用的事件类已弃用,不再需要。 然后可以随时在git中跟踪该类。那么为什么要通过保留死类来打扰和复杂活动代码?

2-业务需求已更改,现在您需要保留基本事件,但您还需要另一个具有某些参数差异的类似事件。 这可以通过多种方式解决,例如装饰器模式可以在很大程度上帮助处理这种变化 或者,新事件可能代表一个独特的域概念,而不是试图强迫概念进入现有模型,最好在语义上更加命名并以这种方式使用它。