Ensuring Data Quality with Command Handler Validators

In the realm of software development, ensuring the quality of the data you're handling is paramount. With the rise of Microservices, CQRS (Command Query Responsibility Segregation) and Mediator patterns, developers now, more than ever, need a reliable mechanism to validate data before processing it. Enter Command Handler Validators - a mechanism to validate command objects before they're handled. In this blog post, we'll discuss how to implement a Command Handler Validator using MediatR, FluentValidation, and ASP.NET Core. A Practical Example Consider the following code for creating a new customer: public class CreateCustomerCommand : IRequest { public string FirstName { get; set; } public string LastName { get; set; } public string Email { get; set; } } The CreateCustomerCommand command is then handled by CreateCustomerCommandHandler: public class CreateCustomerCommandHandler : IRequestHandler { public async Task Handle(CreateCustomerCo...

Enhancing Functionality with the Decorator Pattern in .NET

Introduction

The Decorator Pattern is a structural design pattern that allows you to dynamically add new functionality to an object without changing its existing behaviour. This pattern is highly effective in situations where you need to extend the functionality of an object without making changes to its code or using inheritance. In this blog post, we will discuss the decorator pattern in .NET using a real-world example.

Consider a scenario where you have a list of players and you need to fetch the list from a service. The service might take a considerable amount of time to return the list of players. You may want to add caching or logging functionality to the service, but you don't want to modify the existing code. This is where the decorator pattern comes into play.


Example

Define the Interface

First, we define the interface IPlayersService with a single method, GetPlayersList():

 public interface IPlayersService
 {
   IEnumerable GetPlayersList();
 } 


Implement the Interface

Next, we create a concrete implementation of the IPlayersService interface, called PlayersService, which fetches the list of players and simulates a delay using Thread.Sleep(4000):

public class PlayersService : IPlayersService
{
    public IEnumerable GetPlayersList()
    {
        Thread.Sleep(4000);
        return new List()
        {
            new Player(){ Id=1,  Name="Syed Haider"},
            new Player(){ Id=2,  Name="Ali Haider"},
            new Player(){ Id=3,  Name="Muhammad Haider"},
            new Player(){ Id=4,  Name="Ali Mustafa"},
            new Player(){ Id=5,  Name="Musa Kazim"}
        };
    }
}

Create the Decorators

We will now create two decorators that will add caching and logging functionality to our PlayersService. These decorators will implement the IPlayersService interface and have a reference to the original service.

a. Caching Decorator


The PlayersServiceCachingDecorator adds caching functionality to the GetPlayersList() method. It uses the IMemoryCache to store the list of players and retrieves it when needed.

public class PlayersServiceCachingDecorator : IPlayersService
{
    private readonly IPlayersService _playersService;
    private readonly IMemoryCache _memoryCache;

    private const string GetPlayersListCacheKey = "players.list";

    public PlayersServiceCachingDecorator(IPlayersService playersService, IMemoryCache memoryCache)
    {
        _playersService = playersService;
        _memoryCache = memoryCache;
    }
    
    public IEnumerable GetPlayersList()
    {
        IEnumerable? players = null;

        if (_memoryCache.TryGetValue(GetPlayersListCacheKey, out players)) return players;
        players = _playersService.GetPlayersList();
        var cacheEntryOptions = new MemoryCacheEntryOptions().SetSlidingExpiration(TimeSpan.FromMinutes(1));

        _memoryCache.Set(GetPlayersListCacheKey, players, cacheEntryOptions);

        return players;
    }
}

b. Logging Decorator

The PlayersServiceLoggingDecorator adds logging functionality to the GetPlayersList() method. It logs the start and end time of the method execution and also logs each player's details.

public class PlayersServiceLoggingDecorator : IPlayersService
{
    private readonly IPlayersService _playersService;
    private readonly ILogger _logger;

    public PlayersServiceLoggingDecorator
    (
        IPlayersService playersService,
        ILogger logger
    )
    {
        _playersService = playersService
	_logger = logger;
    }

    public IEnumerable GetPlayersList()
    {
       _logger.LogInformation("Starting to fetch data..");
       var stopWatch = Stopwatch.StartNew();

       var players = _playersService.GetPlayersList();

       foreach (var player in players)
       {
          _logger.LogInformation("Player: " + player.Id + ", Name: " + player.Name);
       }

       stopWatch.Stop();
       var elapsedTime = stopWatch.ElapsedMilliseconds;

       _logger.LogInformation($"Finished fetching data in {elapsedTime} milliseconds");

       return players;
    }
}

Configure the Decorators

Finally, we need to configure our decorators in the dependency injection container. Here, we are using the Decorate extension method to register our decorators based on the configuration.

 builder.Services.AddMemoryCache();
 builder.Services.AddScoped();
 
 builder.Services.Decorate<IPlayersService, PlayersServiceCachingDecorator>();
 builder.Services.Decorate<IPlayersService, PlayersServiceLoggingDecorator>();
 


Dynamic Configuration of Decorators


One of the main advantages of using the decorator pattern is the ability to enable or disable the added functionality dynamically. In our example, we can enable or disable caching using a value in the app configuration.

To achieve this, we have stored the EnableCaching setting in the app configuration file (e.g., appsettings.json). This allows us to easily control whether or not to use caching without altering the code.

 {
    "EnableCaching": "true",
    "EnableLogging": "true"
 }

In the dependency injection configuration, we check the value of the EnableCaching setting using Convert.ToBoolean(builder.Configuration["EnableCaching"]). If it's set to true, the caching decorator will be registered; otherwise, the caching functionality will be disabled.

 if (Convert.ToBoolean(builder.Configuration["EnableCaching"]))
 {
     builder.Services.Decorate<IPlayersService, PlayersServiceCachingDecorator>();
 }

 if (Convert.ToBoolean(builder.Configuration["EnableLogging"]))
 {
     builder.Services.Decorate<IPlayersService, PlayersServiceLoggingDecorator>();
 }

The same approach can be applied to the logging decorator as well.

By using this dynamic configuration, we can quickly enable or disable caching and logging functionality for our PlayersService without changing the service implementation or recompiling the application. This provides greater flexibility and control over the application's behaviour, making it easier to adapt to different environments or requirements.

Introducing Scrutor

In our example, we used the Decorate extension method to register decorators in the dependency injection container. This method is not part of the default .NET Core dependency injection framework. Instead, we used a popular third-party library called Scrutor, which extends the built-in .NET Core dependency injection container with assembly scanning and decoration functionality.

Scrutor is an open-source library designed specifically for .NET Core applications. It simplifies the process of working with decorators and provides a more fluent API for registering services and their decorators.

To use Scrutor in your project, you'll need to install the NuGet package:

Install-Package Scrutor 

Once you have installed the Scrutor library, you can use its extension methods like Decorate to configure your decorators in the dependency injection container:

 builder.Services.Decorate();

The Decorate method wraps the original service registration with the provided decorator type, ensuring that the decorators are resolved and injected correctly by the dependency injection container.

By using the Scrutor library, you can simplify your decorator configuration and take advantage of its additional features, such as assembly scanning, to further enhance your .NET Core applications.

Conclusion

The Decorator Pattern provides a flexible way to add new functionality to objects without modifying their existing behaviour. In this example, we've demonstrated how to implement caching and logging decorators for a .NET service. By using the decorator pattern, you can keep your code clean, maintainable, and extensible. The dynamic configuration and the use of the Scrutor library enhance the flexibility and usability of the decorator pattern in .NET Core applications.

Comments

  1. Source Code can found here: https://github.com/SyedShahbaz/DecoratorPattern

    ReplyDelete

Post a Comment

Popular posts from this blog

Ensuring Data Quality with Command Handler Validators

Supercharge Your Unit Testing with MOQ, Dependency Injection, and Fluent Assertions