Как частично обновить агрегат DDD образом - PullRequest
0 голосов
/ 28 апреля 2020

Я занимаюсь разработкой приложения с C#. NET способом DDD. Я также проверил eshopcontainers , но он не объясняет, что я хотел бы знать, поэтому позвольте мне задать вопрос здесь. У меня вопросы

  • Можно ли частично обновить агрегат root непосредственно из пакетной операции?

Подробно
Я разрабатываю приложение 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)
    {
        ...
    }
}

В этой модели я хотел бы запускать пакет, который периодически проверяет NextTriggerDate в AutoRemindRules, и если NextTriggerDate старше, чем сейчас , партия отправляет письма пользователям, которые не проголосовали за RSVP. В конце концов, пакет обновляет NextTriggerDate, вызывая SetNextTriggerDate. Если я напишу код, подобный следующему, на уровне Usecase (Application) в пакете, я пойму, чего хочу. Однако я не думаю, что эти коды следуют правилу DDD, поскольку оно частично обновляет совокупность root. Можно ли написать такой код на уровне приложения или, если нет, кто-нибудь может сказать мне лучший способ кодирования?

//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 в совокупности root, и пакет использует этот метод. Однако меня беспокоит производительность и нагрузка на БД. в этом случае пакет должен прочитать кучу ненужных записей RSVP в дополнение к AutoRemindRule записям. Мне интересно, есть ли другой подход, который заключается в меньшей нагрузке на БД при сохранении метода DDD.

1 Ответ

1 голос
/ 28 апреля 2020

Можно ли частично обновить агрегат root непосредственно из пакетной операции?

Краткий ответ: Нет.

Точка агрегатов инкапсулирует данные и разоблачение бизнес-операций, так что проверки и инварианты проверяются и применяются. Если вы позволите внешним процессам изменять данные агрегатов, то это полностью отрицает их назначение.

Потенциальные решения:

  1. Не используйте агрегаты. Если у вас есть только таблица с датами и некоторыми данными, а также пакетное задание, но нет или почти нет бизнес-логики c для применения, просто сделайте это: таблица и пакетное задание. На первый взгляд кажется, что единственное, что вам нужно обеспечить, это то, что NextTriggerDate когда-нибудь появится в будущем, и вы можете кодировать это в пакетное задание. Но я могу представить, что у вас может быть больше правил, например, не запускать более X раз, не запускать ранее, чем за 1 день, и т. Д. c. Или даже пусть агрегат определит следующую дату запуска, основываясь на некотором внутреннем логе c (первый запуск через 1 день, второй через 2 дня и т. Д. c). Агрегаты очень удобны для этих вещей, так как каждый агрегат будет хранить состояние, необходимое для расчета следующего изменения состояния.

  2. Оцените, действительно ли является проблемой загрузка полной совокупности RSVP или нет. Загрузка некоторых дополнительных столбцов не должна сильно ухудшать производительность для большинства приложений и должна быть необходимой ценой для того, чтобы получить совокупную инкапсуляцию его бизнес-логики c на тот случай, если вам нужно будет сделать то, что я упомянул по предыдущему пункту. Если вместо этого вы создаете объект с огромным масштабом и малой бизнес-логикой c, подумайте о другом подходе.

  3. Если проблема заключается в том, что загрузка полного агрегата подразумевает загрузку большой коллекции, такой как TimeSlots, которая не требуется для этой бизнес-операции, вы можете обойти ее, указав c метод в вашем хранилище для загрузки агрегата без загрузки этой коллекции. Вы должны кодировать ваши совокупные root операции, которые требуют, чтобы эта коллекция проверяла, была ли загружена коллекция, и в противном случае не работала, чтобы избежать ошибок.

...