【问题标题】:How to use DDD aggregates with TimeSeries?如何将 DDD 聚合与 TimeSeries 一起使用?
【发布时间】:2019-02-19 15:55:10
【问题描述】:

我正在尝试实施一个更面向 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 书籍的情况下苦苦挣扎(它们在帖子中),我决定有两个 AggregateRoots,Site 比较突出。那是一个Signal 聚合应该真正通过Site 访问。

我发现的主要问题是将Samples 的子集加载到Signal

根据eShopOnWeb 示例中使用的Specification 模式,我可以相当轻松地使用Site 聚合并通过调用Infrastructure 层中的SiteRepository 来加载它的Signals 聚合集合:

    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));
        }
    }

您可以使用这种方法并加载所有 Samples,但在处理时间序列数据时,这可能意味着数十万个实体,而我们真正需要的是集中选择它们。

我目前在做什么;而这感觉并不特别“干净”,就是实现一个 Generic Repository 类的版本,专门用于在Signal 实体上部分加载Sample 数据。

    public interface ISignalRepository : IAsyncRepository<Signal>
    {
        Task<IEnumerable<Signal>> GetBySiteIdWithSamplesAsync(int siteId, DateTime from, DateTime to);
    }
    public class SignalRepository : EfRepository<Signal>, ISignalRepository
    {
        public SignalRepository(ForecastingContext dbContext) : base(dbContext)
        {
        }

        public async Task<IEnumerable<Signal>> GetBySiteIdWithSamplesAsync(int siteId, DateTime from, DateTime to)
        {
            var signals = await this.dbContext.Signals.Where(s => s.SiteId == siteId).ToListAsync();

            foreach (var signal in signals)
            {
                this.dbContext.Entry(signal)
                    .Collection(s => s.Samples)
                    .Query()
                    .Where(s => s.TimeStamp >= from && s.TimeStamp <= to)
                    .Load();
            }

            return signals;
        }
    }

这可能只是以新模式开发带来的最初不确定性,但这不知怎么感觉不对。

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

【问题讨论】:

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


    【解决方案1】:

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

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

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

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

    也就是说,我们正在捕获数据,因为我们想用它做某事,当然——所以我们可能有一个域模型,它将我们捕获的数据的分区聚合在一起以执行有趣的计算.但是——在我的经验中——是一种并发行为;汇总数据的过程不应阻止我们收集更多数据。

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

    【讨论】:

    • 那需要一杯正经的咖啡;明天我会读一遍,看看我们要去哪里。
    • 好论文;有趣的是,这些概念多年来是如何演变的,以及微服务和领域事件是如何在其上构建的。所以……评论。是的,我同意,这就是我们正在做的事情。许多外部服务向这个应用程序提供数据(拉而不是推,但这是后来的重构)管理它的代码在 SampleRepository 级别运行;这是可以接受的,因为它的 EFCore 并执行批量操作以提高性能。不过,我希望 ApplicationServices 工作更多 DDD,这意味着 Site.Signal.Samples 语义。我已经更新了我的问题:-)
    猜你喜欢
    • 2020-05-14
    • 1970-01-01
    • 1970-01-01
    • 2019-12-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多