DotnetCqrsPgTemplate.Api 1.0.5

dotnet add package DotnetCqrsPgTemplate.Api --version 1.0.5
                    
NuGet\Install-Package DotnetCqrsPgTemplate.Api -Version 1.0.5
                    
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="DotnetCqrsPgTemplate.Api" Version="1.0.5" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="DotnetCqrsPgTemplate.Api" Version="1.0.5" />
                    
Directory.Packages.props
<PackageReference Include="DotnetCqrsPgTemplate.Api" />
                    
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 DotnetCqrsPgTemplate.Api --version 1.0.5
                    
#r "nuget: DotnetCqrsPgTemplate.Api, 1.0.5"
                    
#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 DotnetCqrsPgTemplate.Api@1.0.5
                    
#: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=DotnetCqrsPgTemplate.Api&version=1.0.5
                    
Install as a Cake Addin
#tool nuget:?package=DotnetCqrsPgTemplate.Api&version=1.0.5
                    
Install as a Cake Tool

Command Query Responsibility Segregation (CQRS) Pattern with Postgres Database .Net API Template

DotnetCqrsPgTemplate is a comprehensive, ready-to-use template for building modern .NET APIs with Postgres Database in CQRS pattern. Designed to simplify and accelerate API development, this template integrates essential tools and follows best practices, making it ideal for developers looking for a solid foundation for their projects.

Getting Started

Features

  • Includes Postgres support
  • Supports Swagger documentation
  • Includes API versioning
  • Includes token-based authentication and RBAC-PBAC support
  • Automatically resolves XML comments from project files
  • Includes annotations for better documentation
  • Includes a sample controller and handlers
  • Includes a sample model

Requirements

  • .NET 8.0
  • Compatible with ASP.NET Core applications
  • Postgres server

Installation

To install the api project Template globally on your machine via NuGet:

  1. Open a terminal or command prompt.
  2. Run the following command:
dotnet new install DotnetCqrsPgTemplate.Api

Usage

To create a new api project using the template:

  1. Open a terminal or command prompt.
  2. Run the following command:
dotnet new dotnet-cqrs-pg-api-v8 -n MyApi

Replace MyApi with your desired project name.

Project Structure and Use Cases

IRequestHandler<TRequest, TResponse>

The IRequestHandler<TRequest, TResponse> interface is a core component in the CQRS (Command Query Responsibility Segregation) pattern. It defines a contract for handling requests (commands or queries) and returning a response. This pattern helps to separate the logic for processing different types of requests, improving maintainability and testability. To use the IRequestHandler<TRequest,TResponse> interface, your request needs to inherit from either: IQuery<TResponse> for queries or ICommand<TResponse> for commands. or you can use the IRequest<TResponse> interface for both commands and queries.

public class AddUserRequest:ICommand<ApiResponse<UserResponse>>
{
    public string Name { get; set; }
    public string Email { get; set; }
}

public class AddUserRequestHandler(IUserRepository userRepository, ILogger<AddUserRequestHandler>logger): IRequestHandler<AddUserRequest, ApiResponse<UserResponse>>
{
    public async Task<ApiResponse<UserResponse>> Handle(AddUserRequest request, CancellationToken cancellationToken)
    {
        var user = new User
        {
            Name = request.Name,
            Email = request.Email
        };

        await userRepository.AddAsync(user, cancellationToken);
        return CommonResponse.CreatedResponse(user.Adapt<UserResponse>(), "User created successfully");
    }
}

IValidator<TRequest>

The IValidator<TRequest> interface is designed to encapsulate validation logic for request objects in a CQRS or API-based application. It allows you to define and enforce business rules and data integrity checks before a request is processed by the application. The TRequest of the IValidator<> interface inherits from either: IRequest<>, ICommand<>, or IQuery<> interfaces. Example:

public class AddUserRequestValidation(IUserRepository userRepository):IValidator<AddUserRequest>
{
    public async Task<IEnumerable<ErrorResponse>> ValidateAsync(EditUserRequest request, CancellationToken cancellationToken)
    {
        var errors = new List<ErrorResponse>();
        var emailExist = await userRepository.GetQueryable()
            .AnyAsync(item => string.Equals(request.Email, item.Email),cancellationToken);
        if (emailExist)
        {
            errors.Add(new ErrorResponse(nameof(request.Email), "Email already exists"));
        }

        return errors;
    }
}

The AddUserRequestHandler will only be executed if the AddUserRequestValidation passes without errors.

IAuditableCommandHandler<TRequest, TResponse>

The IAuditableCommandHandler<TRequest, TResponse> interface is designed to provide hooks for auditing command operations in a CQRS-based application. It allows you to execute custom logic before and after a command is handled, typically for logging, compliance, or traceability purposes. To use this interface, implement it in a class that handles a specific command. For example, the AddEmployeeAuditHandler implements auditing for the AddEmployeeRequest command:

public class AddEmployeeAuditHandler : IAuditableCommandHandler<AddEmployeeRequest, ApiResponse<Employee>>
{
    public async Task PreAuditAsync(AddEmployeeRequest request, CancellationToken cancellationToken)
    {
        // Logic to execute before the command is handled (e.g., logging the request)
    }

    public async Task PostAuditAsync(AddEmployeeRequest request, ApiResponse<Employee> response, CancellationToken cancellationToken)
    {
        // Logic to execute after the command is handled (e.g., logging the request and response)
    }
}

ICorrelationHandler

The ICorrelationHandler interface is designed to provide hooks for tracing and correlating requests and responses throughout the lifecycle of an operation in a CQRS-based or distributed application. It enables consistent tracking of requests, responses, and their associated metadata, which is essential for auditing, debugging, and monitoring. To use this interface, implement it in a class that will handle the tracing or correlation logic. For example, the ApplicationTraceHandler class implements ICorrelationHandler to log and persist trace information for each request:

    public class ApplicationTraceHandler : ICorrelationHandler
    {
        public async Task OnStartAsync(string traceId, object request, CancellationToken cancellationToken)
        {
            // Logic to execute at the start of a request (e.g., logging, persisting trace info)
        }
    
        public async Task OnCompleteAsync(string traceId, object request, object response, long durationMs, CancellationToken cancellationToken)
        {
            // Logic to execute at the completion of a request (e.g., logging, updating trace info)
        }
    }

Benefits

  • Observability: Enables end-to-end tracing of requests for monitoring and diagnostics.
  • Auditing: Captures detailed information about each request and response for compliance and analysis.
  • Extensibility: Can be customized to include additional metadata or integrate with external tracing systems.

IRequestPreProcessor<TRequest>

The IRequestPreProcessor<TRequest> interface is used to define logic that should be executed before a request (such as a command or query) is handled in a CQRS-based application. This is useful for implementing cross-cutting concerns like logging, validation, authorization, or enrichment of the request. To use this interface, implement it for a specific request type. The Process method will be called before the main request handler executes.

public class UserOnLoginRequestHandler : IRequestPreProcessor<LoginRequest>
{
    public async Task Process(LoginRequest request, CancellationToken cancellationToken)
    {
        // Pre-processing logic, such as logging or validation
    }
}

Benefits

  • Separation of Concerns: Keeps pre-processing logic separate from business logic.
  • Reusability: Can be reused for different request types.
  • Extensibility: Easily add more pre-processing steps as needed.

Typical Use Cases

  • Logging incoming requests
  • Validating or enriching requests
  • Checking permissions or authorization
  • Setting up context or correlation IDs

IRequestPostProcessor<TRequest, TResponse>

The IRequestPostProcessor<TRequest, TResponse> interface is used to define logic that should be executed after a request (such as a command or query) has been handled in a CQRS-based application. This is useful for implementing cross-cutting concerns like logging, auditing, notifications, or post-processing actions. To use this interface, implement it for a specific request and response type. The Process method will be called after the main request handler has executed and produced a response.

public class UserLoggedInRequestHandler : IRequestPostProcessor<LoginRequest, ApiResponse<AuthResponse>>
{
    public async Task Process(LoginRequest request, ApiResponse<AuthResponse> response, CancellationToken cancellationToken)
    {
        // Post-processing logic, such as logging or updating user statistics
    }
}

Benefits

  • Separation of Concerns: Keeps post-processing logic separate from business logic.
  • Reusability: Can be reused for different request/response types.
  • Extensibility: Easily add more post-processing steps as needed.

Typical Use Cases

  • Logging responses or important events
  • Auditing actions and outcomes
  • Sending notifications or emails
  • Updating statistics or triggering workflows

ILoggingHook<TRequest, TResponse>

The ILoggingHook<TRequest, TResponse> interface is designed to provide hooks for logging the lifecycle events of a request in a CQRS-based application. It enables you to log when a request starts processing, completes, or encounters an exception, supporting observability and troubleshooting.

To use this interface, implement it for a specific request and response type. The methods will be called at different stages of the request lifecycle:

  • OnLogStartAsync: Invoked before the request is processed.
  • OnLogCompleteAsync: Invoked after the request is successfully processed.
  • OnLogExceptionAsync: Invoked if an exception occurs during processing.
public class UserLoginLoggingHook : ILoggingHook<LoginRequest, ApiResponse<AuthResponse>>
{
    public async Task OnLogStartAsync(LoginRequest request, CancellationToken cancellationToken)
    {
        // Logic to log the start of processing the request
    }

    public async Task OnLogCompleteAsync(LoginRequest request, ApiResponse<AuthResponse> response, CancellationToken cancellationToken)
    {
        // Logic to log the successful completion of the request
    }

    public async Task OnLogExceptionAsync(LoginRequest request, Exception exception, CancellationToken cancellationToken)
    {
        // Logic to log any exceptions that occur during processing
    }
}

Benefits

  • Observability: Provides detailed logs for request processing, aiding in monitoring and debugging.
  • Separation of Concerns: Keeps logging logic separate from business logic.
  • Extensibility: Can be implemented for any request/response type as needed.

Typical Use Cases

  • Logging request and response data for auditing or diagnostics
  • Tracking the timing and status of request processing
  • Capturing and logging exceptions for error analysis.

This approach ensures consistent and maintainable logging for all request lifecycles in your application.

IAuthorizedRequest

The IAuthorizedRequest interface is used to define authorization requirements for a request in a CQRS-based application. It allows you to specify which user roles and permissions are required to execute a particular request, enabling fine-grained access control. This interface provides a swift implementation of Role-Based Access Control (RBAC) and Policy-Based Access Control (PBAC) in your application. To use this interface, implement it in your request class and specify the required roles and permissions. For example:

public class AddUserRequest : ICommand<ApiResponse<UserResponse>>, IAuthorizedRequest
{
    public string Name { get; set; }
    public string Email { get; set; }
    
    public string[] RequiredRoles => [UserRole.DefaultSuperAdmin];
    public string[] RequiredPermissions => [Permissions.DefaultCreate];
}

The permissions of the authenticated user will be extracted from the JWT token in the ClaimType of "permission" and checked against the RequiredPermissions of the request.<br> The roles of the authenticated user will be extracted from the JWT token in the ClaimType of "role" and checked against the RequiredRoles of the request.

Benefits

  • Security: Ensures only authorized users can execute sensitive operations.
  • Separation of Concerns: Keeps authorization logic declarative and separate from business logic.
  • Extensibility: Easily add or modify roles and permissions for different requests.

Typical Use Cases

  • Restricting access to administrative actions
  • Enforcing permission checks for create, update, or delete operations
  • Associating user context with requests for auditing or logging

INotificationHandler<TRequest>

The INotificationHandler<TRequest> interface is used to handle notification events in a CQRS or MediatR-based application. Unlike request handlers, notification handlers are designed for one-way messages that do not expect a response. They are ideal for implementing event-driven or publish/subscribe patterns, where multiple handlers can react to the same event. To use this interface, implement it for a specific notification type. The Handle method will be called whenever the notification is published. Your TRequest type should implement the INotification interface, which indicates that it is a notification rather than a request that expects a response.

public class UserCreatedNotification : INotification
{
    public UserResponse User { get; set; }
}

public class UserCreatedNotificationHandler : INotificationHandler<UserCreatedNotification>
{
    public async Task Handle(UserCreatedNotification notification, CancellationToken cancellationToken)
    {
        // Logic to handle the notification, such as sending an email or logging
    }
}

To publish the notification, You will call the Publish method of the IMediator interface, which is typically injected into your service or controller:

Benefits

  • Event-Driven Architecture: Supports publish/subscribe patterns, allowing multiple handlers to respond to the same event.
  • Separation of Concerns: Keeps event handling logic separate from business logic.
  • Scalability: Easily add new handlers for the same notification without modifying existing code.

Typical Use Cases

  • Logging or auditing events
  • Sending notifications or emails
  • Updating statistics or triggering workflows
  • Integrating with external systems

Using INotificationHandler<TRequest> with DomainEventBase and EventEntity for Domain Driven Design

In a Domain Driven Design (DDD) context, you can use INotificationHandler<TRequest> in conjunction with DomainEventBase and EventEntity to handle domain events effectively. This allows you to decouple your domain logic from the event handling logic, promoting a clean architecture. Example: Given that you want to send notification to a user when a new login attempt is made on their account.


// The request that will trigger the domain event must inherit from `EventEntity`:
public class LoginRequest :EventEntity, ICommand<ApiResponse<EmptyDto>>
{
    public string Email { get; set; }
    public string Password { get; set; }
    
      //Declaration of the `OnLoggedIn` method to handle the login event:
      public void OnLoggedIn(UserResponse user)
      {
        AddDomainEvent(new UserLoggedInEventRequest
        {
          User = user,
        });
      }

    // The OccurredOn property is inherited from the EventEntity class
      public DateTime OccurredOn { get; }= DateTime.UtcNow;
}

// The INotificationHandler request type should implement the `DomainEventBase` class:
public class UserLoggedInEventRequest:DomainEventBase
{
    public UserResponse?User { get; set; }
}


//LoginRequestHandler will handle the login request and trigger the domain event:
public class UserLoggedInEventHandler : IRequestHandler<LoginRequest, ApiResponse<EmptyDto>>
{
    public async Task Handle(LoginRequest request, CancellationToken cancellationToken)
    {
        //The login request handler will perform it traditional login logic. 
        //for the context of this example, we will assume that the user is authenticated successfully.
        
        // After successful login, trigger the domain event
        request.OnLoggedIn(new UserResponse
        {
            //.... details of the user
        });
    }
}

// The INotificationHandler will handle the domain event:
public class UserLoggedInNotificationHandler : INotificationHandler<UserLoggedInEventRequest>
{
    public async Task Handle(UserLoggedInEventRequest notification, CancellationToken cancellationToken)
    {
        // Logic to handle the user logged in event, such as sending a notification
        var user = notification.User;
        if (user != null)
        {
            // Send notification to the user
            Console.WriteLine($"User {user.Name} logged in at {notification.OccurredOn}");
        }
    }
}

With this setup, when the LoginRequest is processed, it will trigger the UserLoggedInEventRequest domain event, which will be handled by the UserLoggedInNotificationHandler. This allows you to keep your domain logic clean and focused on business rules while handling events separately.

Benefits

  • Decoupling: Keeps domain logic separate from event handling, promoting a clean architecture.
  • Scalability: Easily add new event handlers without modifying existing code.
  • Flexibility: Allows for complex event handling scenarios, such as chaining multiple handlers or integrating with external systems.
  • Observability: Provides a clear audit trail of domain events and their handling.
  • Extensibility: Easily extend the domain event system to include additional metadata or custom logic as needed.
  • Testability: Simplifies unit testing by allowing you to mock or stub event handlers independently of the domain logic.

IStreamRequestHandler<TRequest, TResponse>

The IStreamRequestHandler<TRequest, TResponse> interface is designed for handling streaming requests in a CQRS or MediatR-based application. It enables the implementation of handlers that return a sequence of responses asynchronously, which is useful for scenarios where data needs to be processed or delivered in chunks or as a live stream.

To use this interface, implement it for a specific request and response type. The Handle method should return an IAsyncEnumerable<TResponse>, allowing consumers to asynchronously iterate over the results as they become available. Example:

public class StreamUserRequest : IStreamRequest<UserResponse>
{
    public int Page { get; set; }
    public int PageSize { get; set; }
}

public class StreamUserRequestHandler : IStreamRequestHandler<StreamUserRequest, UserResponse>
{
    private readonly IUserRepository _userRepository;

    public StreamUserRequestHandler(IUserRepository userRepository)
    {
        _userRepository = userRepository;
    }

    public async IAsyncEnumerable<UserResponse> Handle(StreamUserRequest request)
    {
        var users =await _userRepository.GetUsers(request.Page, request.PageSize);
        foreach (var user in users)
        {
            yield return user.Adapt<UserResponse>();// Simulate delay for streaming
        }
    }
}

Implementing Retry using IResilientRequest: for Intentional Retry Pattern

The IResilientRequest interface is designed to enable intentional retry logic for requests in your CQRS-based application. This pattern is useful for handling transient failures, such as network issues or temporary unavailability of external services, by automatically retrying the operation according to configurable parameters. The interface provides properties to define the maximum number of retries and the delay between retries. To implement retry logic using IResilientRequest, you can create a request class that implements the interface and specify the retry parameters. If and exception eg. TimeoutException occurs during the request handling, the system will automatically retry the operation based on the defined parameters. Your request handler will be called multiple times until it succeeds or the maximum retry count is reached.

//Example. Verifying Employee KYC details with retry logic
public class VerifyEmployeeKycRequest : ICommand<ApiResponse<EmptyDto>>, IResilientRequest
{
    public string? EmployeeId { get; set; }
    //other properties...
    
    // Retry parameters
    public int RetryCount { get; } = 3;//Number of retry attempts
    public int RetryDelayMilliseconds { get; }= 1000; //Time interval between retry attempts
    public int TimeoutSeconds { get; }= 30;
    
}

// The request handler that implements the kyc verification logic
public class VerifyEmployeeKycRequestHandler : IRequestHandler<VerifyEmployeeKycRequest, ApiResponse<EmptyDto>>
{
    private readonly IKycService _kycService;

    public VerifyEmployeeKycRequestHandler(IKycService kycService)
    {
        _kycService = kycService;
    }

    public async Task<ApiResponse<EmptyDto>> Handle(VerifyEmployeeKycRequest request, CancellationToken cancellationToken)
    {
        try
        {
            // Call the KYC service to verify employee details
            await _kycService.VerifyAsync(request.EmployeeId, cancellationToken);
            return CommonResponse.OkResponse(new EmptyDto(), "KYC verification successful");
        }
        catch (TimeoutException ex)
        {
            // Handle timeout exception and allow retry logic to kick in
            throw new TimeoutException("KYC verification timed out", ex);
        }
    }
}

Bear in mind that the retry logic is applied at the request level, meaning that if the request fails due to a transient error, it will be retried according to the specified parameters. And the retry logic is automatically handled by the ScopeBus pipeline, so you don't need to implement it manually in your request handler. Also if your request doesn't implement the IResilientRequest interface, it will not be retried, and any exceptions will be propagated immediately.

Conclusion

This template provides a solid foundation for building modern .NET APIs using the CQRS pattern with Postgres Database. By leveraging the features and best practices outlined in this documentation, you can accelerate your API development process while ensuring maintainability, scalability, and security.

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 was computed.  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

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.0.5 32 7/18/2025
1.0.4 29 7/18/2025
1.0.3 110 7/17/2025
1.0.2 108 7/17/2025
1.0.1 110 7/17/2025
1.0.0 113 7/17/2025