我无法理解如何在 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);
}
最佳答案
对于您的任何一个示例,事件溯源都不是一个选项。
随着新事件的出现,旧事件变得无关紧要。这些不一定需要合在一起;对于整个读数历史,没有要保护的不变量。
可以在 saga 中管理一系列事件,只保留有限数量的知识,并级联成结果事件。
关于c# - CQRS 写入端 DDD 中的时间序列/时间数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29146167/