DDD создает агрегат в ответ на событие на другом

Я рассматриваю сценарий создания совокупного экземпляра из триггера и другого агрегата.

Я включил некоторую логику в свой DDD и Event Sourcing с кодом Onion architecture вокруг регистрации пользователя и создания учетной записи пользователя.

В настоящее время у меня есть простой экземпляр класса регистрации, который имеет несколько атрибутов вокруг адреса и имени электронной почты. В настоящее время у него есть один метод с именем «Активировать».

Когда активирована Регистрация , необходимо создать Аккаунт .

Внутри я записываю события на различные действия.

public partial class NewRegistration : EventSourcedAggregate
{
    internal NewRegistration(
        string organisationName, 
        AccountContact contact)
    {
        OrganisationName = organisationName ?? throw new ArgumentNullException(nameof(organisationName));
        Contact = contact ?? throw new ArgumentNullException(nameof(contact));
        RequestedDate = SystemClock.Current.GetCurrentUtcDateTime();

        Apply(new RegistrationCreated(
            OrganisationName, 
            Contact.EmailAddress.ToString(), 
            Contact.Name.GivenName,
            Contact.Name.FamilyName,
            RequestedDate));
    }

     NewRegistration(Guid id, int version)
        : base(id, version)
    {

    }

    public string OrganisationName { get; private set; }

    public AccountContact Contact { get; private set; }

    public string ActivationCode { get; private set; }

    public DateTime RequestedDate { get; private set; }

    public DateTime? ActivatedDate { get; private set; }

    public void ChangeActivationCode(string activationCode)
    {
        if (ActivatedDate.HasValue) throw new InvalidOperationException("The registration has already been activated");

        if (activationCode != ActivationCode)
        {
            Apply(new ActivationCodeChanged(activationCode));
        }
    }

    public void Activate()
    {
        Apply(new RegistrationActivatedEvent(SystemClock.Current.GetCurrentUtcDateTime()));
    }

    protected sealed override void Apply(DomainEvent changes)
    {
        When((dynamic)changes);
    }

    private void When(ActivationCodeChanged activationCodeChanged)
    {
        ActivationCode = activationCodeChanged.ActivationCode;
    }

    private void When(RegistrationActivatedEvent registrationActivatedEvent)
    {
        ActivatedDate = registrationActivatedEvent.ActivationDate;
    }
}

Несмотря на то, что вышеуказанное хранится в таблице SQL, оно не полностью реализует Event Sourcing - поскольку в нем будет только одна запись, которая будет обновляться только в том случае, если пользователь сбросит код активации.

Итак, когда регистрация активирована, мне нужно создать новый экземпляр учетной записи. В настоящее время я вижу учетную запись и учетную запись как часть клиентов bounded context. Я чувствую, что регистрация и учетные записи являются двумя отдельными domains /sub-domains в контексте - возможно, я ошибаюсь! Я также чувствовал, что учетная запись может быть Event sourced aggregate, хотя чтение Сообщение в блоге Udi Dahans о том, когда не использовать CQRS Я немного разочарован тем, что планировал.

Теперь есть несколько способов реализовать это. Моя первая мысль заключалась в том, чтобы просто обработать это в Application Service. Обычно я бы поставил эту логику здесь, поскольку они координируют вещи. Для этого потребуется ввести INewRegistrationRepository и IAccountRepository - копировать информацию от одного к другому.

Мы используем множество сервисов приложений на работе, и я увяз в количестве, которое у нас есть из-за нашей бизнес-логики (статические менеджеры, помощники, инфраструктура, смешанная с моделью анемической области.

Но, немного прочитав «Менеджеры процессов» и «Саги», я несколько смутил меня и остановил свои мысли на этот день.

Я все для упрощения вещей, и это простая часть реализации домена, на которую я смотрю. Я немного одолел Application Services - это диспетчер процессов или Saga лучше подходит для этого сценария.

Как практикующие DDD здесь видят реализацию такого сценария?

3 голоса | спросил Andez 3 FebruaryEurope/MoscowbSat, 03 Feb 2018 21:47:12 +0300000000pmSat, 03 Feb 2018 21:47:12 +030018 2018, 21:47:12

2 ответа


2

Фактически, ваше событие RegistrationActived на самом деле совпадает с AccountCreated. Я бы рекомендовал обосновать, нужна ли вам модель регистрации. Критерием является то, является ли это частью вездесущего языка вашего домена.

Что касается вашей первой попытки,

  

Моя первая мысль заключалась в том, чтобы просто обрабатывать это в службе приложений. Обычно я бы поставил эту логику здесь, поскольку они координируют вещи. Для этого потребуется ввести INewRegistrationRepository и IAccountRepository - скопировать информацию из одной в другую.

Я бы не стал поощрять этот подход. Таким образом, ваши приложения очень легко взорваться. Обычно мы всегда стараемся максимально упростить обслуживание приложений.

Кроме того, вы упоминаете

  

«Я чувствую, что регистрация и клиенты - два отдельных   домены /поддомены в контексте "

, это еще одно доказательство того, что мы не должны собирать их вместе. Я бы предложил использовать Eventual Consistency для решения такого сценария. Технически люди обычно используют службу домена для публикации события (RegistrationActived в вашем случае) из одного домена, а затем подписываются в другом домене (учетная запись или домен клиента в вашем случае).

ответил ivenxu 4 FebruaryEurope/MoscowbSun, 04 Feb 2018 06:16:45 +0300000000amSun, 04 Feb 2018 06:16:45 +030018 2018, 06:16:45
1

Вы можете использовать службу регистрации доменных служб, которая при активации команды регистрации выполняет 2 действия:

 1. RegistrationAggregate.activeRegistration
 2. AccountAggregateService.createAccount

Но в случае сбоя сервера и т. д. может произойти, что регистрация была активирована, но учетная запись не была создана. Консистенция не будет обеспечена

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

Следовательно, вам нужно будет написать эту координационную логику, используя jobs /Process Managers .

Примерное решение с использованием диспетчеров процессов и возможной согласованности будет выглядеть следующим образом:

RegistrationAggregate publishes RegistrationActivated event.

Менеджер процессов CreateAccountProcessManager в домене учетных записей прослушивает событие и выдает команду AccountAggregateService для createAccount. Здесь AccountAggregateService - это уровень обслуживания тонких доменов, который взаимодействует с AccountAggregate.

CreateAccountProcessManager{

Handle(RegistrationActivatedEvent message){

AccountAggregateService.createAccount(AccountInfo accountInfo);

}

}

Поскольку ваше событие RegistrationActivated будет сохранено и будет помечено как потребленное только при успешном создании учетной записи, всегда будет обеспечена согласованность.

Другой подход к реализации диспетчера процессов без возможной последовательности будет следующим:

Создается диспетчер процессов при активации команды регистрации, которая выполняет следующие действия:

1. RegistrationAggregateService.activeRegistration
2. AccountAggregateService.createAccount

Этот подход называется чистой оркестровкой.

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

ответил Anisha Agrawal 9 FebruaryEurope/MoscowbFri, 09 Feb 2018 10:30:44 +0300000000amFri, 09 Feb 2018 10:30:44 +030018 2018, 10:30:44

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132