When you build a distributed system it is difficult to design good transaction guarantee mechanism. If your communication is asynchronous, you communicate with remote services using some messaging system. I guess you already know that network is not reliable, so your message can be lost. To guarantee message delivery you have to resend it when confirmation is missing. There are solutions to guarantee idempotence for the service receiving data. What I want to share is my idea how to make sending e-mail service as close as possible to the idempotence solution.
The problem I am trying to solve is how to avoid multiple send of the same e-mail to a user. You cannot build distributed transaction at SMTP layer level, especially if you are communicating with external SMTP server. An exception in your application can appear after sending an e-mail to SMTP server, but before you store that fact in your database. It means you can lose information about successful communication with your SMTP server.
There is a fallback mechanism in SMTP – server sends e-mail to the sender when message could not be delivered successfully. Unfortunately there is no message when message was successfully sent. The idea I have is simple – use BCC (Blind carbon copy). When you are sending e-mail just add BCC in e-mail assigned to your dedicated e-mail address (service account). It means that you should receive an e-mail copy of successfully sent message. When your application crashes just after sending e-mail to SMTP server, you will not lose information that e-mail was sent successfully.
NOTE! There is a lot of other issues to be solved anyway, but the mechanism described here would increase the confidence that an e-mail was sent.