CQRS中写入侧DDD中的时间序列/时间数据

时间:2015-03-19 13:40:56

标签: c# domain-driven-design time-series cqrs event-sourcing

我无法理解如何支持DDD中的时间序列/时间数据以及如何使用CQRS在写入端处理它。最终,我希望找到一个与事件采购相关的解决方案。

以温度预报为例,温度变化也会影响某个地区/地区的预测能源需求。假设温度预测可以进入未来(基于历史数据),将所有预测加载到Location聚合我认为如果不对加载的数据量进行一些限制,那将是不切实际的。

在考虑事件源时,同步/存储在CQRS写入端使用的此类数据的好/推荐方法是什么?

我的以下任何尝试(选项A或B)是否被认为是合适的DDD / CQRS解决方案?

选项A:

允许独立更新温度并使用流程管理器/ saga订阅事件,然后重新计算需求。这种解决方案有助于保持聚合体尺寸较小,但感觉聚合边界可能是错误的,因为需求取决于温度,现在遍布命令/事件。

// OverrideTemperatureForecastCommandHandler.cs 
public void Handle(OverrideTemperatureForecast cmd)
{
    var from = cmd.TemperatureOverrides.Min(t => t.DateTime);
    var to = cmd.TemperatureOverrides.Max(t => t.DateTime);

    TemperatureForecasts forecasts = temperatureForecastRepository.GetByLocation(cmd.LocationId, from, to);

    forecasts.Override(cmd.TemperatureOverrides);

    temperatureForecastRepository.Save(forecasts);
    // raises 
    // TemperatureForecastsOverridden(locationId, overrides)
}

// TemperatureForecastsOverriddenProcessManager.cs 
public void Handle(TemperatureForecastsOverridden @event)
{
    var from = cmd.TemperatureOverrides.Min(t => t.DateTime);
    var to = cmd.TemperatureOverrides.Max(t => t.DateTime);

    // issue a command to recalculate the energy demand now temperature has changed...
    commandBus.Send(new RecalculateEnergyDemand 
       { 
          LocationId = @event.LocationId,
          From = from,
          To = to
       }));
}

// RecalculateEnergyDemandCommandHandler.cs 
public void Handle(RecalculateEnergyDemand cmd)
{
    EnergyDemand demandForecasts = energyDemandForecastRepository.GetByLocation(cmd.LocationId, cmd.From, cmd.To);

    // have to fetch temperature forecasts again...
    TemperatureForecasts temperatureForecasts = temperatureForecastRepository.GetByLocation(cmd.LocationId, cmd.From, cmd.To);

    demandForecasts.AdjustForTemperature(temperatureForecasts);

    energyDemandForecastRepository.Save(demandForecasts);
    // raises 
    // ForecastDemandChanged(locationId, demandforecasts)
}

选项B:

创建单个聚合'位置'并根据给定的日期范围在内部预加载预测数据。从DDD行为的角度来看,这感觉更干净,但加载一个约束到时间范围的聚合对我来说感觉有点尴尬(或者只是我?)。在不限制预测值的大小的情况下,“位置”可以定位。总量可能会变得很大。

// OverrideTemperatureForecastCommandHandler.cs 
public void Handle(OverrideTemperatureForecast cmd)
{
    var from = cmd.TemperatureOverrides.Min(t => t.DateTime);
    var to = cmd.TemperatureOverrides.Max(t => t.DateTime);

    // use from/to to limit internally the range of temperature and demand forecasts that get loaded in to the aggregate.
    Location location = locationRepository.Get(cmd.LocationId, from, to);

    location.OverrideTemperatureForecasts(cmd.TemperatureOverrides);

    locationRepository.Save(forecasts);
    // raises 
    // TemperatureForecastsOverridden(locationId, overrides)
    // ForecastDemandChanged(locationId, demandforecasts)
}

对于选项A或B,读取端的非规范化器可能类似于:

// TemperatureDenormaliser.cs
public void Handle(TemperatureForecastsOverridden @event)
{
    var from = @event.Overrides.Min(t => t.DateTime);
    var to = @event.Overrides.Max(t => t.DateTime);

    var temperatureDTOs = storage.GetByLocation(@event.LocationId, from, to);

    // TODO ... (Add or update)

    storage.Save(temperatureDTOs);
}


// EnergyDemandDenormalizer.cs
public void Handle(ForecastDemandChanged @event)
{
    var from = @event.Overrides.Min(t => t.DateTime);
    var to = @event.Overrides.Max(t => t.DateTime);

    var demandDTOs = storage.GetByLocation(@event.LocationId, from, to);

    // TODO ... (Add or update)

    storage.Save(demandDTOs);
}

1 个答案:

答案 0 :(得分:1)

您的两个示例都不可以选择事件源。

随着新事件的出现,旧事件变得无关紧要。这些不一定需要汇总在一起。没有任何变量可以保护整个阅读历史。

系列事件可以用传奇来管理,只保留有限的知识,然后级联为结果事件。