DDD creando un aggregato in risposta a un evento su un altro

3

Sto osservando uno scenario per la creazione di un'istanza aggregata da un trigger e un aggregato diverso.

Ho incorporato qualche logica nel mio DDD e Event Sourcing con Onion architecture scenario di apprendimento intorno alla registrazione dell'utente e alla creazione di un account utente.

Attualmente ho una semplice istanza di classe di registrazione che ha un numero di attributi intorno a Indirizzo e nome e-mail. Questo al momento ha un singolo metodo chiamato Activate.

Quando è attiva la registrazione , deve essere creato un account .

Internamente, sto registrando eventi sulle varie azioni.

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

Sebbene quanto sopra sia memorizzato in una tabella SQL, non realizza completamente l'Event Sourcing - poiché avrà sempre un solo record che verrà aggiornato solo se l'utente resetta il codice di attivazione.

Quindi, quando viene attivata la registrazione, ho bisogno di creare una nuova istanza di account. Attualmente vedo Registrazione e Account come parte dei clienti bounded context . Ritengo che la registrazione e gli account siano due percentuali separate domains /% co_de all'interno del contesto: potrei sbagliarmi! Avevo anche la sensazione che l'account potesse essere un sub-domains , sebbene leggessi post sul blog di Udi Dahans quando non usare CQRS Mi sento un po 'deluso da quello che stavo progettando.

Ora ci sono diversi modi per implementarlo. Il mio primo pensiero è stato semplicemente gestirlo in un Event sourced aggregate . In genere metterei questa logica qui mentre coordinano le cose. Ciò richiederebbe il Application Service e il INewRegistrationRepository iniettati - per copiare le informazioni dall'una all'altra.

Abbiamo utilizzato molti servizi applicativi al lavoro e sono diventato impantanato nel numero che abbiamo a causa del modo in cui la nostra logica di business è (gestori statici, helper, infrastruttura mescolata con un modello di dominio anemico.

Ma stavo leggendo un po 'su Process Managers e Sagas, I che mi ha un po' confuso e ha fermato i miei pensieri per quel giorno.

Sono tutto per mantenere le cose semplici, e questa è una parte semplice di un'implementazione di dominio che sto guardando. Mi sento un po 'sopraffatto con i servizi applicativi: il gestore dei domini di dominio o Saga si adatta meglio a questo scenario.

In che modo i professionisti di DDD guardano qui all'implementazione di uno scenario del genere?

    
posta Andez 03.02.2018 - 19:47
fonte

2 risposte

2

In realtà, l'evento RegistrationActiv è in realtà lo stesso con AccountCreated. Consiglierei di giustificare se è necessario il modello di registrazione. Il criterio è se fa parte della lingua ubiquitaria del tuo dominio.

Per quanto riguarda il tuo primo tentativo,

My first thought was to simply handle this in an Application Service. Typically I would put that logic here as they coordinate things. This would require the INewRegistrationRepository and IAccountRepository injected - to copy the information from one to the other.

Non vorrei incoraggiare questo approccio. In questo modo, i servizi dell'applicazione sono molto facili da far saltare in aria. Di solito, dovremmo sempre cercare di mantenere il servizio applicativo il più semplice possibile.

Inoltre, stai citando

"I feel Registration and Customers are two separate domains/sub-domains within the context"

, questa è un'altra prova che non dovremmo metterli insieme. Suggerirei di utilizzare Eventual Consistenza per affrontare tale scenario. Tecnicamente, le persone usano solitamente un servizio di dominio per pubblicare un evento (RegistrationActived nel tuo caso) da un dominio, quindi iscriversi in un altro dominio (Account o dominio del cliente nel tuo caso).

    
risposta data 04.02.2018 - 04:16
fonte
1

Potresti usare un servizio di registrazione del servizio di dominio che per attivare il comando di registrazione fa 2 cose:

 1. RegistrationAggregate.activeRegistration
 2. AccountAggregateService.createAccount

Ma in caso di crash del server, ecc., potrebbe accadere che la registrazione fosse attivata ma l'account non è stato creato. La coerenza non sarà garantita

Questo comportamento non è auspicabile nel tuo caso d'uso. Vorresti creare un account per ogni attivazione della registrazione.

Quindi dovresti scrivere questa logica di coordinamento usando jobs / Process Manager .

Una soluzione di esempio che utilizzi i process manager e la consistenza finale sarebbe la seguente:

RegistrationAggregate publishes RegistrationActivated event.

Un Process Manager CreateAccountProcessManager nel dominio Account ascolta l'evento e invia un comando a AccountAggregateService per creareAccount. Qui AccountAggregateService è un livello di servizio di dominio sottile che interagisce con AccountAggregate.

CreateAccountProcessManager{

Handle(RegistrationActivatedEvent message){

AccountAggregateService.createAccount(AccountInfo accountInfo);

}

}

Poiché il tuo evento RegistrationActivated verrà mantenuto e verrà contrassegnato come consumato solo per la creazione di un account, la coerenza sarà sempre garantita.

Un altro approccio all'implementazione del gestore dei processi senza coerenza finale sarebbe simile a questo:

Viene creato un gestore processi sul comando activate registration che esegue quanto segue:

1. RegistrationAggregateService.activeRegistration
2. AccountAggregateService.createAccount

Questo approccio è chiamato pure orchestration.

Puoi scegliere qualsiasi implementazione che si adatti al tuo caso d'uso e invariant di business il meglio. Ogni approccio include pro e contro. Questo è un bell'articolo sugli approcci e i loro pro e cons.

    
risposta data 09.02.2018 - 08:30
fonte

Leggi altre domande sui tag