Specification Pattern in .NET Core

Implementing Specification Pattern in .NET Core

Introduction

The Specification Pattern is a powerful design pattern that helps improve the flexibility and maintainability of code by encapsulating complex business rules and conditions. In this blog post, we will explore the concept of the Specification Pattern and demonstrate its implementation in .NET Core using practical examples.

What is Specification Pattern?

The Specification Pattern is a behavioural design pattern that allows developers to define business rules as separate entities and combine them to form complex conditions. By doing so, it promotes the separation of concerns and enhances code reusability and readability.

Key Concepts of DDD

The Specification Pattern typically involves the following components:

    • Specification:
      An interface or abstract class representing a business rule or condition.
    • Concrete Specifications:
      Classes that implement the Specification interface and encapsulate specific business rules.
    • Composite Specifications:
      Classes that combine multiple specifications using logical operators (AND, OR, NOT) to form complex conditions.
Implementing the Specification Pattern in .NET Core

Creating the Specification Interface
First, let’s define the Specification interface:

public interface  ISpecification<T>
{
   bool IsSatisfiedBy(T item);
}

Implementing Concrete Specifications

Next, let’s create some concrete specifications that implement the ISpecification interface:

public class PriceSpecification : ISpecification<Product>
{
   private readonly decimal _minPrice;
   private readonly decimal _maxPrice;

   public PriceSpecification(decimal minPrice, decimal maxPrice)
   {
      _minPrice = minPrice;
      _maxPrice = maxPrice;
   }

   public bool IsSatisfiedBy(Product item)
   {
      return item.Price >= _minPrice && item.Price <= _maxPrice;
   }
}

public class CategorySpecification : ISpecification<Product>
{
   private readonly string _category;

   public CategorySpecification(string category)
   {
      _category = category;
   }

   public bool IsSatisfiedBy(Product item)
   {
      return item.Category == _category;
   }
}

Creating Composite Specifications

Now, let’s create some composite specifications to combine multiple specifications:

public class AndSpecification<T> : ISpecification<T>
{
   private readonly ISpecification<T> _specification1;
   private readonly ISpecification<T> _specification2;

   public AndSpecification(ISpecification<T> specification1, ISpecification<T> specification2)
   {
      _specification1 = specification1;
      _specification2 = specification2;
   }

   public bool IsSatisfiedBy(T item)
   {
      return _specification1.IsSatisfiedBy(item) && _specification2.IsSatisfiedBy(item);
   }
}

public class OrSpecification<T> : ISpecification<T>
{
   private readonly ISpecification<T> _specification1;
   private readonly ISpecification<T> _specification2;

   public OrSpecification(ISpecification<T> specification1, ISpecification<T> specification2)
   {
      _specification1 = specification1;
      _specification2 = specification2;
   }

   public bool IsSatisfiedBy(T item)
   {
      return _specification1.IsSatisfiedBy(item) || _specification2.IsSatisfiedBy(item);
   }
}

public class NotSpecification<T> : ISpecification<T>
{
   private readonly ISpecification<T> _specification;

   public NotSpecification(ISpecification<T> specification)
   {
      _specification = specification;
   }

   public bool IsSatisfiedBy(T item)
   {
      return !_specification.IsSatisfiedBy(item);
   }
}

Applying Specification Pattern in .NET Core

Example-1: Filtering Products
Let’s use the Specification Pattern to filter a list of products based on specific criteria:

// Assume we have a Product class as follows:
public class Product
{
   public string Name { get; set; }
   public string Category { get; set; }
   public decimal Price { get; set; }
}

// Sample products list
var products = new List<Product>
{
   new Product { Name = “Laptop”, Category = “Electronics”, Price = 1000 },
   new Product { Name = “Smartphone”, Category = “Electronics”, Price = 700 },
   new Product { Name = “Book”, Category = “Books”, Price = 25 }
};

// Creating specifications
var electronicsSpec = new CategorySpecification(“Electronics”);
var affordableSpec = new PriceSpecification(0, 800);

// Combining specifications
var electronicsAndAffordableSpec = new AndSpecification<Product>(electronicsSpec, affordableSpec);

// Filtering products using the composite specification
var filteredProducts = products.Where(p => electronicsAndAffordableSpec.IsSatisfiedBy(p)).ToList();

Advantages of Using Specification Pattern
    • Enhanced Readability:
      The Specification Pattern improves code readability by encapsulating complex conditions into separate classes, making business rules easier to understand.
    • Re-usability:
      With individual specifications defined as separate classes, they can be reused in various parts of the application, reducing code duplication.
    • Maintainability:
      Separating business rules from the rest of the code base simplifies maintenance and updates, as changes to specifications won’t affect other components.
Conclusion

Specification Pattern is a valuable tool for improving code maintainability, flexibility, and readability in .NET Core applications. By encapsulating business rules as separate entities and composing them using composite specifications, developers can create a more modular and extensible code base. When applied appropriately, the Specification Pattern enhances the quality and maintainability of .NET Core projects, ensuring they can easily adapt to changing requirements and business logic.

Specification Pattern in .NET Core

Leave a Reply

Your email address will not be published. Required fields are marked *

Scroll to top