Stop premature email sending with NServiceBus


We use NServiceBus quite a lot to manage integration points where the other side isn’t transactional and we need to “shore up the process” of communicating with external services. One integration point we often don’t think about in terms of an integration point is the sending of an email.

Often times, you’ll see some sort of process that needs to notify someone that the job is complete via email. It’ll look something like:

public void SomeServiceCall()
{
    DoSomeWorkHere();
    CreateSomeObjects();

    SaveToADatabase();

    var message = new MailMessage();
    var client = new SmtpClient();
    client.Send(message);
}

This is all fine and dandy, but the problem comes when something like this is executed inside a transaction. We expect that if the SomeServiceCall method is wrapped in a transaction, that the database save should roll back. But what about that email? It often won’t fail until the transaction commits, which is AFTER the email is sent:

image

In the picture above, we can see that the transaction only involves the database, but we can’t un-send our email.

What we need to do here is decouple the actual sending of an email with the message to indicate an email needs to be sent. Right now, we’re using a synchronous, non-transactional mechanism of sending an email. Instead, we just need to wrap that interaction point with an NServiceBus message:

public void SomeServiceCall()
{
    DoSomeWorkHere();
    CreateSomeObjects();

    SaveToADatabase();

    _bus.Send(new SendEmailMessage());
}

We’re using the NServiceBus bus to send a message that we want to send an email. The nice thing about this picture is that the sending of that NServiceBus message now participates in the transaction:

image

In the case of failure, that message never makes it over to my NServiceBus host. If it does succeed:

image

Then my message is delivered to the SMTP NServiceBus host, which will then interact with the SMTP server.

By decoupling the action of sending an email with the intention through an NServiceBus message, I can make sure that the email isn’t sent unless I successfully complete the transaction.

Another nice benefit here is that I was originally synchronously sending an email. With this architecture, if the SMTP server goes down, it doesn’t affect the ability of My Service to stay up. Instead, the durable messaging of MSMQ and NServiceBus reliability mechanisms ensures that I don’t lose the messages of sending those emails.

When to use NHibernate