如何在TimeSeries中使用DDD聚合?

时间:2019-02-19 15:55:10

标签: c# domain-driven-design ef-core-2.0 ef-core-2.1 ef-core-2.2

我正在尝试实现一种面向DDD的解决方案来管理时间序列数据。以下代码示例和模式可在eShopOnWeb中找到。本质上,存在三个实体。 SiteSignalSampleSite可以具有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);
    }

以新的模式发展可能只是最初的不确定性,但这在某种程度上让人感到不对。

我使用两个聚合是否正确?

1 个答案:

答案 0 :(得分:0)

  

更困难的问题是如何加载示例实体

我发现我需要小心地区分两种不同的信息。我的模型是权威的信息,以及参考数据。

您可能想查看Data on the Outside vs Data on the Inside

现实世界中来自传感器的信号不属于我们的模型。我们只是在 here 中存储它的副本,因为与尝试全部存储在那里相比,这样做更具成本效益。因此,当手头的任务是参考数据捕获时,我们不需要“聚合”。

这就是说,我们之所以要捕获数据是因为我们想对它做某事-因此,我们可能拥有一个域模型,该模型将捕获的数据的分区聚集在一起以执行有趣的计算。但是,根据我的经验,这是并发行为;汇总数据的过程不应阻止我们收集更多数据。

相反,通常看起来是来自外部世界的数据流,并且内部流程进行簿记,并使用更新的参考来跟踪到达的信号历史中的位置。

>