4

I have an DDD application that makes use of two external APIs: Google Calendar API and Google Gmail. In one of the application services I want to create a calendar event (using the Calendar API) and then send a confirmation email with Google Gmail API:

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

But how should I handle the case when the Calendar API call worked but the Gmail API failed? What is the best way to make this whole method transactional when there are two calls to external (and different) APIs? One alternative would be another call to the Calendar API cancelling the event but, would be this the right approach?

2
  • This is a good question but DDD is a red herring. The problem of making a transactional operation out of several calls to inherently non-transactional operations is one I bet most of us have struggled with in our professional lives :) But it has little to do with DDD.
    – Andres F.
    Commented Mar 1, 2016 at 16:19
  • Yes, I've realized that later. I've picked the DDD tag because CreateEventAndSendEmail is an application layer service (which manages 'transictionality') and it implements one use case. Commented Mar 1, 2016 at 17:24

2 Answers 2

5

High level summary: you've got a state machine hiding here; make it explicit. Articulate each of the intermediate and terminal states, and define the edges between them, and make sure that those align with your business requirements. When you reach a terminal state, report the result.

doesn't care very much -- you're calling into services that you don't have any control over; they aren't part of your bounded context. So there isn't anything for you to do...

... unless your model is supposed to be keeping track of this song and dance. In that case, your state machine begins to evolve into a process manager, where the persisted state of each process manager instance tells you how far along you've gotten in your event/email protocol.

From my understanding of DDD, the usual answer would be that you use "the" transaction to update the domain model, then make your external api calls asynchronously. In other words, the transactions track the changes of the state machine, and the responses returned by the asynchronous calls are the triggers that advance the state machine.

See Reliable Messaging without Distributed Transactions

Written out long hand, your entire protocol might look like:

  • Transaction #1, create the instance of the process manager in its initial state, and schedule an asynchronous call to the calendar api.
  • Run the asynchronous call to the calendar api, and asynchronously publish the result to the process manager
  • Transaction #2, update the process manager with the result of the call to the calendar api, persist the update, and schedule an asynchronous call to the email api
  • Run the asynchronous call to the email api, and asynchronously publish the result to the process manager
  • Transaction #3, update the process manager with the result of the call to the calendar api, etc.

Done this way, you can always figure out what state you are in, and re-schedule any dispatched commands that have not yet been acknowledged; this means that you can recover the process to the correct state after a restart by rehydrating it from the persistence layer, rescheduling commands as necessary.

The solution is a lot tidier if the commands you are running are - or can be made to seem - idempotent.

Note that this will be a better fit if the client doesn't require a synchronous response to the whole protocol (for instance, in a REST context, you would probably report back to the client that the process had been 201 Created, and give identify a resource that can be used to poll for updates).

0
0

I see very little DDD here. You have a method that takes some DTO and makes two external API calls.

We can talk about DDD if you actually have something (aggregate) in your model that is created for this event thing. Then, you might have some policies reacting to the domain events that is being published by that aggregate and so asynchronous API calls. You might have a process manager as pointed in the accepted answer, but I would avoid using distributed transactions.

Analyse, why would you expect Google API to fail? Do you plan to avoid something that probably never happens? Transactions are targeting for failure so you can easily roll back, but in this particular case you will have 99,99999% success.

Instead, I would plan for success of both calls and create some lightweight mechanism to tackle errors that will unlikely happen. If you use durable messaging with proper retry policy, you will silently recover almost all of possible network failures. In case you will still get a failure, you can always put failing message to the error queue and have your logging and monitoring infrastructure react on it (basically, send you an email).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.