Flowsy.Specification 1.1.0

dotnet add package Flowsy.Specification --version 1.1.0                
NuGet\Install-Package Flowsy.Specification -Version 1.1.0                
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Flowsy.Specification" Version="1.1.0" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add Flowsy.Specification --version 1.1.0                
#r "nuget: Flowsy.Specification, 1.1.0"                
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install Flowsy.Specification as a Cake Addin
#addin nuget:?package=Flowsy.Specification&version=1.1.0

// Install Flowsy.Specification as a Cake Tool
#tool nuget:?package=Flowsy.Specification&version=1.1.0                

Flowsy Specification

The Specification Pattern is a design pattern used to encapsulate business rules or conditions and make them reusable and composable.

ISpecification<TCandidate> Interface

Represents a domain specification that may or may not be satisfied by a given candidate.

public interface ISpecification<TCandidate>
{
    bool IsSatisfiedBy(TCandidate? candidate);
    
    SpecificationEvaluation<TCandidate> Evaluate(TCandidate? candidate);
    
    ISpecification<TCandidate> And(ISpecification<TCandidate> other);
    ISpecification<TCandidate> Or(ISpecification<TCandidate> other);
    ISpecification<TCandidate> Not();
}

SpecificationEvaluation<TCandidate>

Represents the results of evaluating a given specification.

public class SpecificationEvaluation<TCandidate> : ISpecificationEvaluation
{
    public ISpecification<TCandidate> Specification { get; }
    public TCandidate? Candidate { get; }
    public bool IsSatisfied { get; }
    public string Explanation { get; }
    public IEnumerable<string> Explanations { get; }
}

Example

Although you can implement the ISpecification<TCandidate> interface directly, the AbstractSpecification class provides reusable functionality to simplify the process of creating your own specifications.

In the following example, the Customer entity holds information about a customer's credit limit and their outstanding balance:

public class Customer
{
    public string Name { get; set; } = string.Empty;
    public decimal CreditLimit { get; set; }
    public decimal OutstandingBalance { get; set; }
}

Using specifications we can create reusable objects that tell us if our business rules are satisfied.

public class CreditApprovalSpecification : AbstractSpecification<Customer>
{
    private readonly decimal _requestedCreditAmount;
    
    public CreditApprovalSpecification(decimal requestedCreditAmount)
    {
        _requestedCreditAmount = requestedCreditAmount;
    }
    
    public override bool IsSatisfiedBy(Customer? candidate)
    {
        if (candidate is null)
            return false;
        
        return candidate.OutstandingBalance + _requestedCreditAmount <= candidate.CreditLimit;
    }

    public override SpecificationEvaluation<Customer> Evaluate(Customer? candidate)
    {
        var isSatisfied = IsSatisfiedBy(candidate);
        var explanation = isSatisfied
            ? "Credit approval granted. Customer is eligible for additional credit."
            : $"Credit approval denied. Exceeded credit limit ({candidate?.CreditLimit}). Outstanding balance: {candidate?.OutstandingBalance}.";
        
        return new SpecificationEvaluation<Customer>(this, candidate, isSatisfied, explanation);
    }
}

And now we can reuse our business rules:

public class Program
{
    public static void Main()
    {
        var customers = new List<Customer>();
        // Populate customer list
        const decimal creditAmount = 1000;
        var creditApproval = new CreditApprovalSpecification(creditAmount);

        foreach (var customer in customers)
        {
            var evaluation = creditApproval.Evaluate(customer);
            Console.WriteLine(
                @"${0} USD credit approved for customer {1}? {2} ({3})",
                creditAmount,
                customer.Name,
                evaluation.IsSatisfied ? "Yes" : "No",
                evaluation.Explanation
                );
        }
    }
}
Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
.NET Core netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.1 is compatible. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • .NETStandard 2.1

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
1.1.0 120 5/25/2024
1.0.0 254 11/5/2023
0.1.3 133 10/31/2023
0.1.2 125 10/30/2023
0.1.1 132 10/30/2023
0.1.0 139 10/25/2023