10

How to easily extend your app using MediatR notifications

“You don’t want domain knowledge leaking into your controllers”.

Probably the biggest question you face as a developer every single day is “where to put your code“? Should you use repositories or query classes? Should you have one class or two to represent your domain object? Should you write a new class or extend an existing one?

In short what constitutes a single responsibility and how separate should your concerns actually be?

MediatR Notifications can help with some of these thorny issues.

An example

Imagine you’ve created your shiny new web app and put in place a nice and simple contact us form.

Abiding by the KISS principle you’ve added some basic functionality so that when your customers complete this form it sends an email with their details.

        public IHttpActionResult Post([FromBody]ContactUsForm contactUs)
        {
            var mailMessage = new MailMessage(contactUs.EmailAddress, "you@yoursite.com"); 
            mailMessage.Subject = "Contact from web site";
            mailMessage.Body = contactUs.Message;            

            var smtp = new SmtpClient();
            smtp.Send(mailMessage);

            return Ok();
        }

Granted, this is a simplistic implementation for demo purposes. In reality you’d be thinking about exception handling etc.

Now your boss comes along and asks for the ability to report on how often people are submitting their details.

OK, so that’s fine. Once you’ve sorted out your database schema etc. you add some code to also save this request to the database.

        public IHttpActionResult Post([FromBody]ContactUsForm contactUs)
        {
            var mailMessage = new MailMessage(contactUs.EmailAddress, "you@yoursite.com"); 
            mailMessage.Subject = "Contact from web site";
            mailMessage.Body = contactUs.Message;            

            var smtp = new SmtpClient();
            smtp.Send(mailMessage);

            var userMessage = new UserMessage {
                Received = System.DateTime.Now,
                UserEmailAddress = contactUs.EmailAddress,
                UserFullName = contactUs.FullName
            };
            db.UserMessages.Add(userMessage);
            db.SaveChanges();

            return Ok();
        }

At this point your Single Responsibility Principle alarm bells are going off but you decide to leave it alone for now.

This boss of yours is never happy so they come back a few days later and ask if you can also save the customer’s details to your CRM system.

Back to the controller we go.

public class ContactController : ApiController
{
using BlogSite.Models;
using BlogSite.Models.Crm;
using System.Net.Mail;
using System.Web.Http;

namespace BlogSite.Controllers.Api
{
    public class ContactController : ApiController
    {
        private ApplicationDbContext db = new ApplicationDbContext();        

        public IHttpActionResult Post([FromBody]ContactUsForm contactUs)
        {
            var mailMessage = new MailMessage(contactUs.EmailAddress, "you@yoursite.com");
            mailMessage.Subject = "Contact from web site";
            mailMessage.Body = contactUs.Message;

            var smtp = new SmtpClient();
            smtp.Send(mailMessage);

            var userMessage = new UserMessage
            {
                Received = System.DateTime.Now,
                UserEmailAddress = contactUs.EmailAddress,
                UserFullName = contactUs.FullName
            };
            db.UserMessages.Add(userMessage);
            db.SaveChanges();

            var crm = new CrmInterface();
            crm.Connect();
            crm.SaveContact(new CrmContact { EmailAddress = contactUs.EmailAddress, FullName = contactUs.FullName });

            return Ok();
        }
    }
}
}

(Again, a somewhat simplified example).

Taking stock

And so it goes on. Every new requirement results in a change to the existing controller.

This is potentially risky. Any kind of change to existing code raises the possibility of breaking existing functionality.

Furthermore this code executes synchronously. If your CRM system or mail server is slow at handling requests your users are going to be waiting around for a while.

Separating Concerns

By now it’s very clear that this controller action is doing a tad too much. One approach would be to pull each distinct responsibility into it’s own class.

using BlogSite.Services.Crm;
using BlogSite.Services.Data;
using BlogSite.Services.Email;
using System.Web.Http;

namespace BlogSite.Controllers.Api
{
    public class ContactController : ApiController
    {
        private IEmailService _emailService;
        private IUserMessageService _userMessageService;
        private ICrm _crm;

        public ContactController(IEmailService emailService, IUserMessageService userMessageService, ICrm crm)
        {
            _emailService = emailService;
            _userMessageService = userMessageService;
            _crm = crm;
        }

        public IHttpActionResult Post([FromBody]ContactUsForm contactUs)
        {
            _emailService.Send(contactUs.EmailAddress, "you@yoursite.net", "Contact from web site", contactUs.Message);
            _userMessageService.RecordUserMessageReceived(contactUs.EmailAddress, contactUs.FullName);
            _crm.SaveContact(new CrmContact { EmailAddress = contactUs.EmailAddress, FullName = contactUs.FullName });
            return Ok();
        }
    }
}
What's in a name?
At this point you’ve had to think what to call these separate classes.

Classes with names like “service” and “manager” tend to get bigger and bigger over time and rarely stick to one responsibility.

If you try using specific verbs instead you’re inevitably left wondering what to call the method. For example, SendEmail.Send(); feels a bit clunky.

It does have the benefit of encouraging single responsibility though: SendEmail.SaveToDatabase(); clearly suggests a new class is trying to break out.

This is better, concerns have been separated and the controller action is looking a lot leaner.

However you’re still running synchronously (you could amend the services to run asynchronously), your controller is directly coupled to the services (look at the usings for evidence of that one) and you’ll still need to come back and modify this controller if your demanding boss swings back around with another feature request.

Switching to MediatR Notifications

So how can MediatR help?

If you’re new to MediatR check out this post on simplifying your controllers with the command pattern and MediatR for a quick recap.

One of the things MediatR gives you is the option to raise one notification in your code then have multiple actions performed off the back of it.

Instead of putting all your business logic in the controller action (or indeed delegating to multiple services) you can publish a notification.

Notifications can be published synchronously or asynchronously.

    public class ContactController : ApiController
    {
        private IMediator _mediator;

        public ContactController(IMediator mediator)
        {
            _mediator = mediator;
        }

        public async Task<IHttpActionResult> Post([FromBody]ContactUsForm contactUs)
        {
            var messageReceivedFromUserNotification = new MessageReceivedFromUserNotification {
                EmailAddress = contactUs.EmailAddress,
                FullName = contactUs.FullName,
                Message = contactUs.Message,
                SubmittedAt = DateTime.Now
            };
            await _mediator.PublishAsync(messageReceivedFromUserNotification);

            return Ok();
        }
    }

From the controller’s perspective that’s it, job done, it can put it’s feet up and never be bothered again.

The notification is effectively a DTO representing the details of the notification.

    public class MessageReceivedFromUserNotification : IAsyncNotification
    {
        public DateTime SubmittedAt { get; set; }
        public string FullName { get; set; }
        public string EmailAddress { get; set; }
        public string Message { get; set; }
    }

Next up you’ll need a handler for each distinct action you want to take when this notification is raised.

    public class SaveUserMessage : IAsyncNotificationHandler<MessageReceivedFromUserNotification>
    {
        private ApplicationDbContext db = new ApplicationDbContext();

        public async Task Handle(MessageReceivedFromUserNotification notification)
        {
            var userMessage = new UserMessage
            {
                Received = notification.SubmittedAt,
                UserEmailAddress = notification.EmailAddress,
                UserFullName = notification.FullName
            };
            db.UserMessages.Add(userMessage);
            await db.SaveChangesAsync();
        }
    }

Adding additional functionality becomes an exercise in adding new handlers.

    public class NotifySalesUserMessageReceived : IAsyncNotificationHandler<MessageReceivedFromUserNotification>
    {
        public async Task Handle(MessageReceivedFromUserNotification notification)
        {
            var mailMessage = new MailMessage(notification.EmailAddress, "you@yoursite.com");
            mailMessage.Subject = "Contact from web site";
            mailMessage.Body = notification.Message;

            var smtp = new SmtpClient();
            await smtp.SendMailAsync(mailMessage);
        }
    }

Surviving flaky external services

Finally, if you’re communicating with external systems (database, CRM, SMTP servers) and really need resilience even if those services go down, you might consider going even further than MediatR and use a service bus.

For example, you could implement queues using Azure Service Bus, RabbitMQ etc.

This takes the decoupling one step further and ensures your “notifications” can be persisted and retried in the event of any external system “flakiness”.

The good thing is, if you use MediatR in the first place, switching to a messaging/queuing approach is a simple exercise.

  • Pingback: Simplify your controllers with the Command Pattern and MediatR - JonHilton.Net()

  • Pingback: Notes from the ASP.NET Community Standup – September 6, 2016 | .NET Web Development and Tools Blog()

  • Pingback: Simplify your controllers with the Command Pattern and MediatR - jonhilton.net()

  • Pingback: Szumma #056 – 2016 36. hét | d/fuel()

  • Have not wrapped my head around the async / Task stuff in asp.net MVC, so this might be a little off topic.

    I’m assuming creating the handler’s async makes them pretty much fire and forget? So the only advantage in the scenario above is that your controller action will return Ok(); sooner than if you used non async handlers?

    • Hi Disman.

      Honestly, I’m not expert at Async myself and haven’t used it extensively but I think it comes down to a couple of things.

      One is resource management on the server side.

      The server has a limit to the number of requests it can handle concurrently which is largely driven by the number of threads available in a thread pool (and this is bound to the CPU/memory/IO of the server.

      When you await a task the execution state is parked so that it may proceed at a later time (when the work is finished) thereby not blocking the thread and significantly increasing the number of requests that can be handled concurrently. This means you can handle many more requests with the same hardware (than if you do everything synchronously).

      Now people may shoot holes in this explanation, please go ahead and put me straight!

      Secondly, as you say it will result in your controller action returning sooner. How important this is to you is probably dependant on what’s happening behind the action. If you had to keep everyone waiting whilst you sent an email or accessed a slow database (or third party cloud service) then they would probably feel like your application was unresponsive or “laggy”. By returning immediately they would get a much better sense that your app is fast and works “smoothly”.

      Finally, if you have something you want to run asynchronously but still need to give feedback to the user when certain key events occur, you can employ something like SignalR to “push” responses up to the client. This can be a really useful pattern because it saves your web application from having to poll (for example to see the status of something in the system) and can instead have messages pushed to it when key state changes occur. You could easily have one of your MediatR notifications make a call via SignalR to the browser meaning even long-running operations could trigger an update in the UI when they complete.

      Hope that all makes sense and is helpful 🙂

      • Spetsen

        Your description of the benefit of async (“execution state is parked so that it may proceed at a later time (when the work is finished)”) is fairly correct, assuming my understanding of async is correct. Async is complex, so you never know…

        The assumption made by you and Disman that the controller action returns sooner is not correct though. When you “await” the async call you tell the compiler/runtime/… to not run the next line of code until the task has completed. As the implementation of PublishAsync returns a task that finishes when all handlers have finished (https://github.com/jbogard/MediatR/blob/master/src/MediatR/Mediator.cs#L80) you will not return Ok() until all handlers have done their thing.

        Actually, your controller action will probably even return a bit later as there is some overhead with async and await. This should not be noticeable though and the benefit of being able to handle more requests without slowing down often outweighs that small drawback.

        If you really want to do fire-and-forget you could potentially skip awaiting the task but I seem to remember reading some blog post about this being super unsafe due to a combination of how async works and how ASP.NET works. I have no idea why though. Apparently using something like Hangfire (http://hangfire.io/) is the best way to do fire-and-forget in ASP.NET.

  • Dotnet Shadow

    With regards to your example, how do you ensure that save changes actually occurs and that you don’t send out an email if it doesn’t?
    Would you have to publish a new notification within SaveUserMessage?

    controller -> publish(messageReceivedFromUserNotification)
    then within the SaveUserMessage -> publish(saveReceivedFromUserNotification)

    • Thanks for the question.

      I tend to think of it like this. The user doesn’t really care what happens, only that their intent to contact you is logged and will be dealt with (someone will see it and take the necessary action). They definitely don’t want their request to be cancelled or lost due to a technical fault.

      The sales team receiving the notification may or may not care whether it’s been saved somewhere.

      If the database save fails, then the question is simply, what should happen next? You could try again (this would often overcome any transient database issues). You can define retry policies very easily using something like Polly.

      https://github.com/App-vNext/Polly

      You could (as you indicated) raise another message, either an error (it failed) or success (it worked!) notification.

      Another thing to consider is mixing “commands” and “notifications”. For example, If the most important thing was to save the customer as a lead to your database, you might consider publishing a command to do this (“SaveLeadCommand”) and then raise a notification after that completes (to notify sales etc.) Here’s an example of that (command, followed by notification).

      https://github.com/HTBox/allReady/blob/master/AllReadyApp/Web-App/AllReady/Features/Tasks/TaskSignupCommandHandler.cs

      If you want to ensure you can survive your application going down (or new deploys etc) then you could always take the next step and consider a service bus such as RabbitMQ, Azure Service Bus or Amazon SQS. These work in a very similar way but with the added resilience that comes with persistent queues. Generally once the message is on the queue it is guaranteed to be delivered at least once.

      It’s an oldie now but Jimmy Bogard (author of MediatR) covered this himself in a post a few years ago (in his case using NServiceBus).

      https://lostechies.com/jimmybogard/2011/11/22/stop-premature-email-sending-with-nservicebus/

      • Dotnet Shadow

        Thank you so much for your in depth response you’ve given me a few ideas