如何以DDD的方式部分更新聚合体?

问题描述 投票:0回答:1

我正在用C# .NET以DDD的方式开发一个应用程序。我也检查了 商店容器但它没有解释我想知道的东西,所以让我在这里给一个问题。我的问题是

  • 可以直接通过批处理操作部分更新根集合体吗?

详细介绍 我正在开发一个RSVP应用。现在我想做的是给还没有投票的人发送提醒邮件。我的域模型和基础设施层是这样的。

//Domain Model
class RSVP //RootAggreagate
{
    public long Id {get; private set;}
    public List<TimeSlot> TimeSlots {get; private set;}  

    public AutoRemindRule AutoRemindRule {get; private set;}
}

class AutoRemindRule 
{
    public long Id {get; private set;}
    public int IntervalHour { get; private set; }
    public DateTimeOffset NextTriggerDate { get; private set; }
    public DateTimeOffset RemindBeginDate { get; private set; }

    //Foreign Key for Plan
    public long RSVPId


    void SetNextTriggerDate() 
    {
        //Compute NewNextTriggerDate based on IntervalHour and RemindBeginDate field.
        NextTriggerDate = NewNextTriggerDate;
    }
}

//Infrastructure Layer (EF Core)
public class MyDbContext : DbContext
{
    //Plan Aggregate
    public DbSet<RSVP> RSVPs { get; set; }
    public DbSet<TimeSlot> TimeSlots { get; set; }
    public DbSet<AutoRemindRule> AutoRemindRules { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        ...
    }
}

在这个模型中,我想运行一个批处理程序来检查 NextTriggerDateAutoRemindRules 定期,如果 NextTriggerDate 是比现在更老的,批处理会给没有投RSVP的用户发送邮件。最终,批处理会更新 NextTriggerDate 呼叫 SetNextTriggerDate. 如果我在Usecase(Application)层的批处理中写下如下的代码,就可以实现我想要的东西,但我认为这些代码并不遵循DDD规则,因为聚合根的更新是部分的。但是,我认为这些代码不符合DDD规则,因为它部分更新了聚合根。在应用层写这样的代码可以吗,如果不可以,谁能告诉我一个更好的编码方式?

//Usecase(Application) layer in a batch
using (var context = new MyDbContext) 
{
    var rules = context.AutoRemindRules.Where(i => i.NextTiggerdate < DateTimeOffset.Now);
    foreach (var rule in rule)
    {
        SendRemindMail();
        rule.SetNextTriggerDate();

        context.SaveChanges();
    }
}

更新 另一种方法是创建一个方法来更新 AutoRemindRule 在聚合根目录下,批处理利用该方法。但是,我担心的是性能和对DB的负载。AutoRemindRule 记录。我想知道是否有另一种方法可以在保持DDD方式的同时减少DB的负载。

c# .net domain-driven-design ef-core-2.0
1个回答
1
投票

可以直接从批处理操作中部分更新根集合吗?

简单的回答是。不可以

聚合体的意义在于封装数据和暴露业务操作,以便检查和执行验证和不变性。如果让外部进程修改聚合体的数据,那么就完全违背了聚合体的目的。

潜在的解决方案。

  1. 不要使用聚合体 如果你有的只是一个带有日期和一些数据的表和一个批处理作业,但没有或几乎没有业务逻辑可以执行,那么就只做:一个表和一个批处理作业。乍一看,似乎你唯一需要强制执行的是NextTriggerDate是未来的某个时间,你可以将其编码到批处理作业中。但我可以想象,你可以有更多的规则,比如不要触发超过X次,不要早于1天触发等等。甚至让聚合体根据一些内部逻辑来决定下一个触发日期(1天后第一次触发,2天后第二次触发等等)。聚合体对于这些事情是非常方便的,因为每个聚合体都会存储他们计算下一个状态变化所需要的状态。

  2. 评估一下加载完整的RSVP聚合是否真的有问题。对于大多数应用来说,加载一些额外的列应该不会对性能造成很大的影响,而且为了获得聚合体封装其业务逻辑的好处,在你需要做像我在上一点提到的那些事情时,这是一个必要的代价。如果相反,你正在构建一个规模巨大而业务逻辑很少的功能,那么就可以考虑采用不同的方法。

  3. 如果问题是加载完整的聚合意味着加载一个像TimeSlots这样的大集合,而这个集合恰好不是该业务操作所需要的,你可以在你的仓库中提供一个特定的方法来绕过它,在不加载该集合的情况下加载聚合。你应该将你的聚合根操作编码为需要该集合的操作,以验证该集合是否被加载,否则就会失败,以避免bug。

© www.soinside.com 2019 - 2024. All rights reserved.