Consistenza transazionale con chiamate API esterne

2

Ho un'applicazione DDD che utilizza due API esterne: Google Calendar API e Google Gmail. In uno dei servizi dell'applicazione voglio creare un evento del calendario (utilizzando l'API di Calendar) e quindi inviare un'e-mail di conferma con l'API di Google Gmail:

        public ValidationResult CreateEventAndSendEmail(string email, EventViewModel EventViewModel)
        {
            // SEVERAL VALIDATIONS

            // Call to Google Calendar API
            validationResult = _googleApiProxy.CreateCalendarEvent(...);

            // Confirmation email
            if (validationResult.Succeeded)
            {
                var email = new Email
                {
                    ...
                };

               var emailSent = _emailApiProxy.SendEmail(email);
               if (!emailSent.Succeeded)
               {
                   // Call to Gmail API
                   validationResult.Errors.Add(string.Format("Error sending email to {0}", user.Email));

                   // What if the email wasn't delivered?
               }
            }

            return validationResult;
        }

Ma come devo gestire il caso quando la chiamata all'API di Calendar ha funzionato, ma l'API di Gmail non è riuscita? Qual è il modo migliore per rendere transazionale questo intero metodo quando ci sono due chiamate a API esterne (e diverse)? Un'alternativa sarebbe un'altra chiamata all'API di Calendar che cancella l'evento ma, sarebbe questo il giusto approccio?

    
posta Rafael Companhoni 26.02.2016 - 23:27
fonte

2 risposte

3

Riepilogo di alto livello: hai una macchina statale che si nasconde qui; renderlo esplicito. Articola ciascuno degli stati intermedi e terminali e definisci i bordi tra loro e assicurati che siano allineati con i tuoi requisiti aziendali. Quando raggiungi uno stato terminale, segnala il risultato.

Le non interessa molto - stai chiamando in servizi su cui non hai alcun controllo; non fanno parte del tuo contesto limitato. Quindi non c'è niente da fare per te ...

... a meno che il tuo modello non tenga traccia di questa canzone e balla. In tal caso, il tuo computer di stato inizia ad evolversi in un gestore di processi, in cui lo stato persistente di ogni istanza di process manager ti dice quanto tempo hai ottenuto nel tuo protocollo evento / email.

Dalla mia comprensione del DDD, la solita risposta sarebbe che tu usi la "transazione" per aggiornare il modello di dominio, quindi fai in modo che le tue API esterne chiamino in modo asincrono. In altre parole, le transazioni tracciano le modifiche della macchina a stati e le risposte restituite dalle chiamate asincrone sono i trigger che fanno avanzare la macchina a stati.

Vedi Messaggistica affidabile senza transazioni distribuite

Scritto a mano lunga, l'intero protocollo potrebbe essere simile a:

  • Transazione n. 1, creare l'istanza del gestore processi nel suo stato iniziale e pianificare una chiamata asincrona all'api del calendario.
  • Esegui la chiamata asincrona all'ap del calendario e pubblica il risultato in modo asincrono nel gestore processi
  • Transazione n. 2, aggiorna il gestore processi con il risultato della chiamata all'api del calendario, continua l'aggiornamento e pianifica una chiamata asincrona all'email api
  • Esegui la chiamata asincrona all'API email e pubblica il risultato in modo asincrono nel gestore processi
  • Transazione n. 3, aggiorna il gestore processi con il risultato della chiamata all'api del calendario, ecc.

Fatto in questo modo, puoi sempre capire in che stato ti trovi e riprogrammare i comandi inviati che non sono ancora stati riconosciuti; ciò significa che è possibile ripristinare il processo allo stato corretto dopo un riavvio reidratandolo dal livello di persistenza, riprogrammando i comandi secondo necessità.

La soluzione è molto più ordinata se i comandi che stai eseguendo sono - o possono essere fatti sembrare - idempotenti.

Si noti che questo sarà più adatto se il client non richiede una risposta sincrona all'intero protocollo (ad esempio, in un contesto REST, probabilmente si riporterà al client che il processo è stato 201 creato, e dare identificare una risorsa che può essere utilizzata per il polling per gli aggiornamenti).

    
risposta data 27.02.2016 - 06:35
fonte
0

Qui vedo pochissimo DDD. Hai un metodo che prende alcuni DTO e fa due chiamate API esterne.

Possiamo parlare di DDD se effettivamente hai qualcosa (aggregato) nel tuo modello che è stato creato per questo evento. Quindi, è possibile che alcune politiche reagiscano agli eventi di dominio pubblicati da tale aggregazione e quindi chiamate API asincrone. Potresti avere un gestore di processi come indicato nella risposta accettata, ma eviterei di usare transazioni distribuite.

Analizza, perché dovresti aspettarti che l'API di Google fallisca? Hai intenzione di evitare qualcosa che probabilmente non accadrà mai? Le transazioni hanno come obiettivo il fallimento, quindi puoi facilmente eseguire il rollback, ma in questo caso specifico avrai il 99,99999% di successo.

Invece, pianificherei per il successo di entrambe le chiamate e creerò un meccanismo leggero per affrontare gli errori che improbabilmente accadranno. Se si utilizzano messaggi durevoli con una politica di retry appropriata, si ripristinerà automaticamente quasi tutti i possibili errori di rete. Nel caso in cui si verifichi ancora un errore, è sempre possibile inserire un messaggio non funzionante nella coda degli errori e fare in modo che l'infrastruttura di registrazione e monitoraggio reagisca (in pratica, inviare una e-mail).

    
risposta data 02.03.2016 - 16:38
fonte

Leggi altre domande sui tag