Configuring the background service

Now that we have defined the event bus abstraction, we can proceed by creating a new BackgroundService type that will use the IMediator interface to dispatch the sold-out messages. This book uses RabbitMQ because it is open source and easy to configure but keep in mind that there are tons of products and technologies related to this topic. Before continuing with the implementation of the background service, it is necessary to add some packages to the Cart.Infrastructure project:

dotnet add package RabbitMQ.Client

Furthermore, we can proceed by creating a new class that extends the BackgroundService type:

using System;
using System.Threading;
using System.Threading.Tasks;
using Cart.Domain.Events;
using Cart.Infrastructure.Configurations;
using MediatR;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;

namespace Cart.Infrastructure.BackgroundServices
{
public class ItemSoldOutBackgroundService : BackgroundService
{
private readonly IMediator _mediator;
private readonly ILogger<ItemSoldOutBackgroundService> _logger;
private readonly EventBusSettings _options;
private readonly IModel _channel;

public ItemSoldOutBackgroundService( IMediator mediator,
EventBusSettings options, ConnectionFactory factory,
ILogger<ItemSoldOutBackgroundService> logger)
{
_options = options;
_logger = logger;
_mediator = mediator;

try
{
var connection = factory.CreateConnection();
_channel = connection.CreateModel();
}
catch (Exception e)
{
_logger.LogWarning("Unable to initialize the event bus:
{message}", e.Message);
}
}

...
}
}

The preceding snippet defines the ItemSoldOutBackgroundService type. The class extends the BackgroundService base class exposed by the Microsoft.Extensions.Hosting namespace. The constructor injects the IMediator interface to dispatch the collected events to the ItemSoldOutEventHandler type. Furthermore, it also defines the attribute of the IModel type that will be populated by the ConnectionFactory type injected in the constructor. The _channel attribute will be used by the ExecuteAsync method provided by the BackgroundService class to dispatch the events. Let's proceed by overriding the ExecuteAsync method:

using MediatR;
using Microsoft.Extensions.Hosting;
using Newtonsoft.Json;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;

namespace Cart.Infrastructure.BackgroundServices
{
public class ItemSoldOutBackgroundService : BackgroundService
{
...

protected override Task ExecuteAsync(CancellationToken
stoppingToken)
{
stoppingToken.ThrowIfCancellationRequested();

var consumer = new EventingBasicConsumer(_channel);

consumer.Received += async (ch, ea) =>
{
var content = System.Text.Encoding.UTF8.
GetString(ea.Body);
var @event = JsonConvert.DeserializeObject
<ItemSoldOutEvent>(content);

await _mediator.Send(@event, stoppingToken);
_channel.BasicAck(ea.DeliveryTag, false);
};

try
{
consumer.Model.QueueDeclare(_settings.EventQueue, true,
false);

_channel.BasicConsume(_options.EventQueue, false,
consumer);
}
catch (Exception e)
{
_logger.LogWarning("Unable to consume the event bus:
{message}"
, e.Message);
}

return Task.CompletedTask;
}
}
}

The preceding snippet uses the _channel attribute to initialize a new EventingBasicConsumer instance. For each received message, it deserializes the Body attribute into an ItemSoldOutEvent type and it sends the event to the IMediator instance using the Send method. Finally, it activates the consuming process by using the EventQueue name provided by the EventBusSettings type. Also, in this case, the consumption process is wrapped using a try-catch to isolate the process in the event of failure.

Before we can use RabbitMQ, it is necessary to configure the client so that we connect to the right event bus instance. Let's start by creating a new extension method in the Cart.Infrastructure project, which can be found under the Extensions folder:

using Cart.Infrastructure.Configurations;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using RabbitMQ.Client;

namespace Cart.Infrastructure.Extensions
{
public static class EventsExtensions
{
public static IServiceCollection AddEventBus(this
IServiceCollection services, IConfiguration
configuration)
{
var config = new EventBusSettings();
configuration.Bind("EventBus", config);
services.AddSingleton(config);

ConnectionFactory factory = new ConnectionFactory
{
HostName = config.HostName,
UserName = config.User,
Password = config.Password
};

services.AddSingleton(factory);

return services;
}
}
}

The previous definition implements an extension method bound with the IServiceCollection interface, which is provided by the dependency injection system of ASP.NET Core. It is used in the Startup class to connect to RabbitMQ. The AddEventBus method initializes the ConnectionFactory class by passing the parameter of RabbitMQ. 

Finally, we can proceed by activating the background service by adding the following extension methods to the ConfigureServices method of the Startup class:

using System;
...

namespace Cart.API
{
public class Startup
{

public void ConfigureServices(IServiceCollection services)
{
services
...
.AddEventBus(Configuration)
.AddHostedService<ItemSoldOutBackgroundService>();

}

...

}
}

The AddEventBus method adds all of the dependencies needed by the consumption process of the sold-out events to the dependency injection. Also, the AddHostedService method registers ItemSoldOutBackgroundService as the IHostedService type. Finally, to use the event bus, we can proceed by defining the connection information in the appsettings.json file of the Cart.API project, as follows:

{
...
"EventBus": {
"HostName": "catalog_esb",
"User": "guest",
"Password": "guest",
"EventQueue": "ItemSoldOut"
}
}

The connection parameters point to the catalog_esb instance defined in the docker-compose.yml file defined in the catalog service. Furthermore, the ItemSoldOutBackgroundService class will process the message in the ItemSoldOut queue and trigger the removal of the items.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
13.58.121.214