Event Sourcing in .NET Core – part 5: offline consumers
Event Sourcing in .NET Core – part 5: offline consumers

Event Sourcing in .NET Core – part 5: offline consumers

2020, Jun 05    

Ok so apparently last time I lied. Or at least I lied to me: it wasn’t the last episode of the Event Sourcing series 🙂 This time we’re going to see how we can implement offline consumers for our integration events.

My SuperSafeBank repo was still calling me and I knew there were other things to add. It was like an itch, constantly reminding me to add more and more code, features on top of features.

So what is an offline consumer? Well, it’s quite simple: it is something that doesn’t have any visible access points. No API. No UI.

A naked implementation will just expose the necessary hooks for the Ops team to monitor its health, and that’s it.

And why in the world we would want something like that?

Well, it’s simple. Background operations. Everything that can be processed “offline” and doesn’t require human intervention. Long-running tasks for example. Stuff like:

  • transcoding media files
  • processing large batches of rows on a DB
  • sending newsletters

Usually, an offline consumer is implemented as a Console application, hosting implementations of our friend BackgroundService.

It’s not very different from the usual WebHost we use in ASP.NET. Something like this:

class Program
{
    static void Main(string[] args)
    {
        CreateHostBuilder(args).Build().Run();
    }

    private static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureAppConfiguration((ctx, builder) =>
                {
                    builder.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
                })
                .ConfigureServices((hostContext, services) =>
                {
                   // register your services here
                });
}

The difference here is that we’re calling .ConfigureServices() instead of .ConfigureWebHostDefaults() as we would normally do to create an HTTP Server.

Now, what bank would be SuperSafeBank if it was not sending notifications to its customers? A very lame one, I’ll tell you that.

I’ve added a new service to our ecosystem, in this case I decided to use the naming convention “worker” to state its offline consumer nature.

Its sole purpose is to listen to specific events and shoot a notification when necessary. For example when an account is created or there’s a withdrawal or a deposit operation. The kind of things that everybody would keep under their radar when it comes to money.

The bulk of this new system is the AccountEventsWorker class, which will start consuming event and react to them:

private async Task OnEventReceived(object s, IDomainEvent<Guid> @event)
{
    var notification = @event switch
    {
                AccountCreated newAccount => await _notificationsFactory.CreateNewAccountNotificationAsync(newAccount.OwnerId, newAccount.AggregateId),
                Deposit deposit => await _notificationsFactory.CreateDepositNotificationAsync(deposit.OwnerId, deposit.Amount),
                Withdrawal withdrawal => await _notificationsFactory.CreateWithdrawalNotificationAsync(withdrawal.OwnerId, withdrawal.Amount),
                _ => null
    };

    if (null != notification)
        await _notificationsService.DispatchAsync(notification);
}

I will still keep adding more features to the repository so stay tuned and feel free to contribute!

Did you like this post? Then