ASP.NET Design – Abstracting Email Service

asp.netdesignseparation-of-concerns

Got a fairly large web application in asp.net using C#. Also have a mobile setup using a common api that we wrote to basically share data across both of our apps using web methods and common classes.

Email was initially thrown together in maybe two days using a simple email class and some basic properties.

For instance, once a record is created we send an email. Imagine a crm type system such that when an account or a customer is created an email is sent. The thing I dislike the most is we're calling it right from within for instance the presentation layer:

MyAPI.SaveCompany(company object);
Email.SendMail(...);

For the mobile application we've got a web service to for instance save a company just like above. Then just as shown above after saving a company we do the same thing Email.SendMail right within the web method.

Its time to fix this as to me it just feels wrong. What do you guys recommend I do?

  • Should this be a standalone service that sends emails based on events in a database table that keeps track of all these events,

  • Or should I just create an email class like I already have and just call it somewhere else?

I thought about just calling it right when I call SaveCompany (inside this method) and then I wouldn't have to worry about my web UI or my mobile since its inside SaveCompany it would call it automatically. But then this feels wrong as it ties up my manager class (my API) to some physical email implementation.

Can someone please give me some hints or how they accomplish this possibly a look at their design or thought process.

As mentioned this is ASP.NET Web Forms, C#, a lot of jQuery, and SQL Server 2008.

Best Answer

Basically, you have two issues here:

  • E-mails are dispatched from presentation layer,
  • The API is tied up to physical e-mail implementation.

The first issue is solved by the first step of moving the code where it belongs: in business layer. You shouldn't save companies from presentation layer, and you shouldn't send e-mails from there: move all this code where it belongs to, and focus presentation layer on presentation, i.e. generation of HTML code or JSON or XML from a model.

Business layer:

public class Example
{
    public void ProcessSamplePage(...)
    {
        CustomerManagement.Create(...);
        SmtpService.NotifyCustomerRegistered(...);
        var model = ...
        Presentation.GenerateHtml(model);
    }
}

Moving from ASP.NET to ASP.NET MVC may help avoiding such errors in future.

The next step is Dependency Injection, which also solves the second problem. Instead of using a specific class provided by .NET Framework to send e-mails directly from your business layer, you can create an interface which will have a specific method for dispatching messages, and then using whatever implementation you want. Dispatching message can mean sending an actual e-mail through .NET Framework, sending an e-mail through a third-party library, sending an e-mail by manually connecting to SMTP server, storing a text file in a directory where the SMTP service will pick it or... storing the message in the database.

Business layer:

public class Example
{
    public Example(IMessagesDispatcher dispatcher) { ... }

    public void ProcessSamplePage(...)
    {
        CustomerManagement.Create(...);
        this.dispatcher.NotifyCustomerRegistered(...);
        var model = ...
        Presentation.GenerateHtml(model);
    }
}

Now that you can store messages in the database instead of just sending them through e-mails, you can imagine more complex scenarios, such as a Windows Service which checks regularly the database and sends pending e-mails. This also allows to control the load of SMTP server: if you have to send hundreds of e-mails per second between 2 PM and 3 PM, but only a few dozen e-mails per minute at night, you can reschedule low-priority e-mails to be actually dispatched at night.

Related Topic