我正在尝试实现一种面向DDD的解决方案来管理时间序列数据。以下代码示例和模式可在eShopOnWeb中找到。本质上,存在三个实体。 Site
,Signal
和Sample
。 Site
可以具有Signals
的集合,而Signal
可以具有样本的集合。
public class Site: BaseEntity, IAggregateRoot
{
// Collection loaded by EFCore through Repository
private List<Signal> signals = new List<Signal>();
// Public read only access
public IEnumerable<Signal> Signals => this.signals.AsReadOnly();
}
public class Signal: BaseEntity, IAggregateRoot
{
// Signal has to belong to Site
public int SiteId { get; private set; }
// Typical EF Nav property removed
// Signal should have no access to it's 'parent' properties
// public Site Site { get; set;}
private List<Sample> samples = new List<Sample>();
public IEnumerable<Sample> Samples => this.samples.AsReadOnly();
}
public class Sample : BaseEntity
{
public int SignalId { get; private set; }
public DateTime TimeStamp { get; set; }
public double? Value { get; set; }
}
作为第一步,在没有可用的Evans或Vernon书籍的情况下(他们都在帖子中)苦苦挣扎,我决定选择两个AggregateRoot,其中Site
更为突出。实际上,应该通过Signal
访问Site
聚合。
我发现的主要问题是将Samples
的子集加载到Signal
。
根据eShopOnWeb示例中使用的Specification
模式,我可以很容易地使用Site
聚合并通过调用{加载它的Signals
聚合集合SiteRepository
层中的{1}}:
Infrastructure
如果我在一个 public sealed class SiteFilterSpecification : BaseSpecification<Site>
{
public SiteFilterSpecification(int id)
: base(s => s.Id == id)
{
this.AddInclude(s => s.Signals);
}
}
班级中,向我提供了一个网站和一个时间段,该时间段将用来计算某些内容,通常涉及多个Service
,那么规范模式将建议:
Signals
我在这里发现的问题是,在规范中无法过滤 public double GetComplexProcess(Site site, DateTime start, DateTime end)
{
var specification = new SiteSignalsWithSamplesSpec(site.Id, start, end);
var signals = this.SignalRepository.List(specification);
// signals should be loaded with the appropriate samples...
}
随附的Samples
Signal
您可以使用这种方法并加载所有 public sealed class SiteSignalsWithSamplesSpecification : BaseSpecification<Signal>
{
public SiteSignalsWithSamplesSpecification(int siteId, DateTime from, DateTime end)
: base(s => s.SiteId == siteId)
{
// This throws exception at runtime
this.AddInclude(s => s.Samples.Where(sa => sa.TimeStamp >= from && sa.TimeStamp <= end));
}
}
,但是在处理时间序列数据时,这可能意味着成千上万个实体,而我们真正需要的是重点选择它们。
我目前正在做什么;并不是特别“干净”的是实现Generic Repository类的版本,专门用于在Samples
实体上部分加载Sample
数据。
Signal
public interface ISignalRepository : IAsyncRepository<Signal>
{
Task<IEnumerable<Signal>> GetBySiteIdWithSamplesAsync(int siteId, DateTime from, DateTime to);
}
以新的模式发展可能只是最初的不确定性,但这在某种程度上让人感到不对。
我使用两个聚合是否正确?
答案 0 :(得分:0)
更困难的问题是如何加载示例实体
我发现我需要小心地区分两种不同的信息。我的模型是权威的信息,以及参考数据。
您可能想查看Data on the Outside vs Data on the Inside。
现实世界中来自传感器的信号不属于我们的模型。我们只是在 here 中存储它的副本,因为与尝试全部存储在那里相比,这样做更具成本效益。因此,当手头的任务是参考数据捕获时,我们不需要“聚合”。
这就是说,我们之所以要捕获数据是因为我们想对它做某事-因此,我们可能拥有一个域模型,该模型将捕获的数据的分区聚集在一起以执行有趣的计算。但是,根据我的经验,这是并发行为;汇总数据的过程不应阻止我们收集更多数据。
相反,通常看起来是来自外部世界的数据流,并且内部流程进行簿记,并使用更新的参考来跟踪到达的信号历史中的位置。
>