PMQ.Notifications 1.1.2

dotnet add package PMQ.Notifications --version 1.1.2
                    
NuGet\Install-Package PMQ.Notifications -Version 1.1.2
                    
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="PMQ.Notifications" Version="1.1.2" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="PMQ.Notifications" Version="1.1.2" />
                    
Directory.Packages.props
<PackageReference Include="PMQ.Notifications" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add PMQ.Notifications --version 1.1.2
                    
#r "nuget: PMQ.Notifications, 1.1.2"
                    
#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.
#:package PMQ.Notifications@1.1.2
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=PMQ.Notifications&version=1.1.2
                    
Install as a Cake Addin
#tool nuget:?package=PMQ.Notifications&version=1.1.2
                    
Install as a Cake Tool

PMQ.Notifications

A package for managing notifications and business rules in .NET applications, making it easy to handle messages, errors, and domain rules in a centralized and strongly-typed way.
Designed to fit CQRS, MediatR, and DDD patterns, enabling clear separation of concerns.

NuGet
License: MIT


Installation

Install via NuGet:

dotnet add package PMQ.Notifications

Or manually add the reference in your .csproj.


How it works

The package provides a notification context (INotificationContext) to register, query, and handle business rule violations, not found errors, or any kind of custom notification, using strongly-typed notification types (NotificationType).

Main components

  • INotificationContext: Abstraction for managing notifications.
  • NotificationContext: Default implementation of INotificationContext.
  • Notification: Represents a single notification (key, message, type).
  • NotificationType: Enum-like value object for message categorization.
  • Validatable: Abstract base class that lets an object accumulate its own rule violations.
  • NotificationContextExtensions: Bridges a Validatable into an INotificationContext.

Validatable

Validatable gives domain objects a place to record why they are invalid, without throwing. A broken business rule is an expected outcome, not exceptional control flow — and unlike an exception, which stops at the first problem, a Validatable reports every failure at once.

using PMQ.Notifications;

public sealed class Product : Validatable
{
    public string Name { get; private set; }
    public decimal Price { get; private set; }

    public Product(string name, decimal price)
    {
        if (string.IsNullOrWhiteSpace(name))
            AddNotification(nameof(Name), "Product name cannot be empty.");

        if (price <= 0)
            AddNotification(nameof(Price), "Price must be greater than zero.");

        Name = name;
        Price = price;
    }
}
Member Purpose
ValidationResult All accumulated failures (FluentValidation type).
IsValid / IsInvalid Quick state check.
ClearValidation() Drops the accumulated failures.
AddNotification(property, message) Records one failure. Protected — only the object itself decides it is invalid.
AddNotifications(failures) Records several at once.

Validation members are marked [JsonIgnore], so they never leak into serialized payloads.

Promoting failures to the request

AddFrom copies a Validatable's failures into the request's notification context, so the domain object never has to know about the request it happens to be serving:

var product = new Product(request.Name, request.Price);

// NotificationType.BusinessRule → HTTP 422 with PMQ.ErrorHandling.
if (notificationContext.AddFrom(product, NotificationType.BusinessRule))
    return default;

await repository.AddAsync(product, cancellationToken);

It returns true when there were failures, and also accepts a collection when several objects must be validated together.

Namespace note: until 1.0.7 this type shipped under PMQ.Notification (singular) by mistake. It now lives in PMQ.Notifications alongside everything else; the old name still works but is marked [Obsolete] and will be removed in 2.0.


Usage Examples

Registering the service

builder.Services.AddScoped<INotificationContext, NotificationContext>();

Command + Handler with Mediator (business rule example)

In this scenario we want to add a product to a category.

  • If the category does not exist → a notification is added.
  • If the product already exists in the category → a business rule notification is added.
using MediatR;
using PMQ.Notifications;

public record AddProductCommand(string CategoryId, string ProductName) : IRequest<bool>;

public class AddProductCommandHandler : IRequestHandler<AddProductCommand, bool>
{
    private readonly INotificationContext _notifications;
    private readonly ICategoryRepository _categoryRepository;

    public AddProductCommandHandler(
        INotificationContext notifications,
        ICategoryRepository categoryRepository)
    {
        _notifications = notifications;
        _categoryRepository = categoryRepository;
    }

    public async Task<bool> Handle(AddProductCommand request, CancellationToken cancellationToken)
    {
        var category = await _categoryRepository.GetByIdAsync(request.CategoryId, cancellationToken);

        if (category is null)
        {
            _notifications.Add("Category", $"Category with id '{request.CategoryId}' was not found.", NotificationType.NotFound);
            return false;
        }

        if (category.Products.Any(p => p.Name.Equals(request.ProductName, StringComparison.OrdinalIgnoreCase)))
        {
            _notifications.Add("Product", $"The product '{request.ProductName}' already exists in this category.", NotificationType.BusinessRule);
            return false;
        }

        //Business rule passed → add product
        category.AddProduct(request.ProductName);

        await _categoryRepository.UpdateAsync(category, cancellationToken);

        return true;
    }
}

Querying notifications in application layer

if (_notifications.HasType(NotificationType.NotFound))
{
    var notFoundMessages = _notifications.GetMessages(NotificationType.NotFound);
    // handle not found logic
}

if (_notifications.HasType(NotificationType.BusinessRule))
{
    var businessRuleErrors = _notifications.GetMessages(NotificationType.BusinessRule);
    // handle business rule violation
}

if (_notifications.HasNotifications)
{
    var allMessages = _notifications.GetMessages();
    // handle all notifications
}

Clearing notifications

_notifications.Clear();

Usage in Filters (with ResultExecutingContext)

In API projects, you can use a filter to automatically map notifications into HTTP responses.

public class NotificationFilter : IResultFilter
{
    private readonly INotificationContext _context;

    public NotificationFilter(INotificationContext context) => _context = context;

    public void OnResultExecuting(ResultExecutingContext context)
    {
        if (_context.HasType(NotificationType.Validation))
        {
            context.Result = new BadRequestObjectResult(_context.GetMessages(NotificationType.Validation));
        }
        else if (_context.HasType(NotificationType.NotFound))
        {
            context.Result = new NotFoundObjectResult(_context.GetMessages(NotificationType.NotFound));
        }
        else if (_context.HasNotifications)
        {
            context.Result = new UnprocessableEntityObjectResult(_context.GetMessages());
        }
    }

    public void OnResultExecuted(ResultExecutedContext context) { }
}

Register globally:

builder.Services.AddControllers(options =>
{
    options.Filters.Add<NotificationFilter>();
});

Benefits of using NotificationContext

  • Separation of concerns: business rules and not-found checks are decoupled from controllers and services.
  • Consistency: all rule violations and messages are centralized.
  • Testability: easy to assert domain notifications in unit tests.
  • Extensibility: custom notification types can be added (e.g., Security, AccessDenied).

Keywords for Discoverability

These keywords help developers and AI tools to find this package on NuGet and GitHub:

notifications, validation, business-rules, ddd, domain-driven-design, cqrs, mediator, mediatR, clean-architecture, error-handling, result-pattern, notification-pattern, application-layer, domain-events


MIT License © 2025 Pablo Mickael Quevedo

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  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.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (2)

Showing the top 2 NuGet packages that depend on PMQ.Notifications:

Package Downloads
PMQ.ErrorHandling

A comprehensive error handling library for .NET applications, providing exception filters, notification mapping, model state validation, and centralized error response formatting for ASP.NET Core projects.

PMQ.Domain

Domain-Driven Design building blocks for .NET: entities with identity-based equality, aggregate roots, value objects and domain events. Validation is accumulated as notifications instead of thrown as exceptions.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.1.2 492 8/12/2026
1.1.1 493 8/5/2026
1.1.0 192 8/5/2026
1.0.7 297 1/26/2026
1.0.6 271 8/31/2025
1.0.4 169 8/3/2025
1.0.0 170 8/3/2025