Skip to content

Commit 96a815a

Browse files
committed
Add mediator pattern interfaces and utility classes
Introduced a set of interfaces and classes to implement the mediator pattern, enabling asynchronous messaging and request/response handling. - Added `INotificationHandler<TNotification>` for handling notifications asynchronously. - Added `INotificationPublisher` for publishing notifications to handler executors. - Added `IRequestExceptionAction<TRequest, TException>` for custom exception handling actions. - Added `IRequestExceptionHandler<TRequest, TResponse, TException>` for managing exceptions in request pipelines. - Added `IRequestHandler<TRequest, TResponse>` and its overload for handling requests and responses. - Added `IRequestPreProcessor<TRequest>` and `IRequestPostProcessor<TRequest, TResponse>` for pre- and post-processing logic. - Introduced `NotificationHandlerExecutor` to encapsulate notification handlers and their delegates. - Added `RequestExceptionHandlerState<TResponse>` to manage exception handling state and responses. These changes provide a flexible and extensible foundation for building asynchronous, event-driven systems.
1 parent bc23519 commit 96a815a

9 files changed

Lines changed: 254 additions & 0 deletions
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
using System.Threading;
2+
using System.Threading.Tasks;
3+
4+
namespace EasyExtensions.Mediator.Contracts
5+
{
6+
/// <summary>
7+
/// Defines a handler for processing notification messages of a specified type asynchronously.
8+
/// </summary>
9+
/// <typeparam name="TNotification">The type of notification to handle. Must implement the <see cref="INotification"/> interface.</typeparam>
10+
public interface INotificationHandler<in TNotification> where TNotification : INotification
11+
{
12+
/// <summary>
13+
/// Handles the specified notification asynchronously.
14+
/// </summary>
15+
/// <param name="notification">The notification to process. Cannot be null.</param>
16+
/// <param name="cancellationToken">A cancellation token that can be used to cancel the operation.</param>
17+
/// <returns>A task that represents the asynchronous operation.</returns>
18+
Task Handle(TNotification notification, CancellationToken cancellationToken = default);
19+
}
20+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
using System.Threading;
2+
using System.Threading.Tasks;
3+
using System.Collections.Generic;
4+
5+
namespace EasyExtensions.Mediator.Contracts
6+
{
7+
/// <summary>
8+
/// Defines a contract for asynchronously publishing notifications to one or more notification handler executors.
9+
/// </summary>
10+
/// <remarks>Implementations of this interface are responsible for delivering notifications to the
11+
/// provided handler executors. The publish operation may be subject to cancellation via the supplied cancellation
12+
/// token. Thread safety and delivery guarantees depend on the specific implementation.</remarks>
13+
public interface INotificationPublisher
14+
{
15+
/// <summary>
16+
/// Publishes a notification to the specified collection of notification handler executors asynchronously.
17+
/// </summary>
18+
/// <param name="handlerExecutors">A collection of executors responsible for handling the notification. Cannot be null.</param>
19+
/// <param name="notification">The notification instance to be published. Cannot be null.</param>
20+
/// <param name="cancellationToken">A cancellation token that can be used to cancel the publish operation.</param>
21+
/// <returns>A task that represents the asynchronous publish operation.</returns>
22+
Task Publish(IEnumerable<NotificationHandlerExecutor> handlerExecutors, INotification notification, CancellationToken cancellationToken = default);
23+
}
24+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
using System;
2+
using System.Threading;
3+
using System.Threading.Tasks;
4+
5+
namespace EasyExtensions.Mediator.Contracts
6+
{
7+
/// <summary>
8+
/// Defines an action to be performed when an exception occurs during the processing of a request.
9+
/// </summary>
10+
/// <remarks>Implement this interface to provide custom exception handling logic for specific request and
11+
/// exception types. The action is typically invoked by a request processing pipeline or middleware when an
12+
/// exception of the specified type occurs.</remarks>
13+
/// <typeparam name="TRequest">The type of the request associated with the exception. This type must not be null.</typeparam>
14+
/// <typeparam name="TException">The type of exception that triggered the action. This type must derive from Exception.</typeparam>
15+
public interface IRequestExceptionAction<in TRequest, in TException> where TRequest : notnull where TException : Exception
16+
{
17+
/// <summary>
18+
/// Executes the operation using the specified request and exception context.
19+
/// </summary>
20+
/// <param name="request">The request object that provides the necessary data for the operation.</param>
21+
/// <param name="exception">The exception instance that triggered the execution. Cannot be null.</param>
22+
/// <param name="cancellationToken">A cancellation token that can be used to cancel the operation.</param>
23+
/// <returns>A task that represents the asynchronous execution operation.</returns>
24+
Task Execute(TRequest request, TException exception, CancellationToken cancellationToken = default);
25+
}
26+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
using System;
2+
using System.Threading;
3+
using System.Threading.Tasks;
4+
5+
namespace EasyExtensions.Mediator.Contracts
6+
{
7+
/// <summary>
8+
/// Defines a contract for handling exceptions that occur during the processing of a request and determining how the
9+
/// request pipeline should proceed.
10+
/// </summary>
11+
/// <remarks>Implementations of this interface can be used to customize exception handling logic within a
12+
/// request processing pipeline. The handler can inspect the exception and request, and decide whether to mark the
13+
/// exception as handled and optionally provide a response. This enables scenarios such as logging, transforming
14+
/// exceptions into responses, or allowing the pipeline to continue after certain exceptions.</remarks>
15+
/// <typeparam name="TRequest">The type of the request message being processed. Must not be null.</typeparam>
16+
/// <typeparam name="TResponse">The type of the response message that may be produced by the handler.</typeparam>
17+
/// <typeparam name="TException">The type of exception to handle. Must derive from Exception.</typeparam>
18+
public interface IRequestExceptionHandler<in TRequest, TResponse, in TException> where TRequest : notnull where TException : Exception where TResponse : class
19+
{
20+
/// <summary>
21+
/// Handles an exception that occurred during the processing of a request and determines how the request
22+
/// pipeline should proceed.
23+
/// </summary>
24+
/// <param name="request">The request message that was being processed when the exception was thrown.</param>
25+
/// <param name="exception">The exception instance that was caught during request processing.</param>
26+
/// <param name="state">The handler state object used to indicate whether the exception has been handled and to provide a response
27+
/// if appropriate.</param>
28+
/// <param name="cancellationToken">A cancellation token that can be used to cancel the exception handling operation.</param>
29+
/// <returns>A task that represents the asynchronous exception handling operation.</returns>
30+
Task Handle(TRequest request, TException exception, RequestExceptionHandlerState<TResponse> state, CancellationToken cancellationToken = default);
31+
}
32+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
using System.Threading;
2+
using System.Threading.Tasks;
3+
4+
namespace EasyExtensions.Mediator.Contracts
5+
{
6+
/// <summary>
7+
/// Defines a handler for processing a request and returning a response asynchronously.
8+
/// </summary>
9+
/// <remarks>Implement this interface to define custom logic for handling specific request types in a
10+
/// request/response pattern. The handler is typically invoked by a mediator or dispatcher component.
11+
/// Implementations should be thread-safe if they are shared across multiple requests.</remarks>
12+
/// <typeparam name="TRequest">The type of the request to handle. Must implement <see cref="IRequest{TResponse}"/>.</typeparam>
13+
/// <typeparam name="TResponse">The type of the response returned by the handler.</typeparam>
14+
public interface IRequestHandler<in TRequest, TResponse> where TRequest : IRequest<TResponse>
15+
{
16+
/// <summary>
17+
/// Handles the specified request and returns a response asynchronously.
18+
/// </summary>
19+
/// <param name="request">The request message to process. Cannot be null.</param>
20+
/// <param name="cancellationToken">A cancellation token that can be used to cancel the operation.</param>
21+
/// <returns>A task that represents the asynchronous operation. The task result contains the response to the request.</returns>
22+
Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken = default);
23+
}
24+
25+
/// <summary>
26+
/// Defines a handler for processing a request of a specified type asynchronously.
27+
/// </summary>
28+
/// <remarks>Implement this interface to define custom logic for handling requests of type <typeparamref
29+
/// name="TRequest"/>. The handler is typically invoked by a mediator or dispatcher component. Implementations
30+
/// should ensure thread safety if the handler will be used concurrently.</remarks>
31+
/// <typeparam name="TRequest">The type of request to handle. Must implement the <see cref="IRequest"/> interface.</typeparam>
32+
public interface IRequestHandler<in TRequest> where TRequest : IRequest
33+
{
34+
/// <summary>
35+
/// Handles the specified request asynchronously.
36+
/// </summary>
37+
/// <param name="request">The request message to be processed. Cannot be null.</param>
38+
/// <param name="cancellationToken">A cancellation token that can be used to cancel the operation.</param>
39+
/// <returns>A task that represents the asynchronous handling operation.</returns>
40+
Task Handle(TRequest request, CancellationToken cancellationToken = default);
41+
}
42+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Text;
4+
using System.Threading;
5+
using System.Threading.Tasks;
6+
7+
namespace EasyExtensions.Mediator.Contracts
8+
{
9+
/// <summary>
10+
/// Defines a post-processor for requests, allowing additional actions to be performed after a request handler has
11+
/// processed a request and produced a response.
12+
/// </summary>
13+
/// <remarks>Implement this interface to execute logic that should run after the main request handler
14+
/// completes, such as logging, auditing, or modifying the response. Post-processors are invoked after the request
15+
/// handler and can observe both the request and its response.</remarks>
16+
/// <typeparam name="TRequest">The type of the request message. Must not be null.</typeparam>
17+
/// <typeparam name="TResponse">The type of the response message produced by the request handler.</typeparam>
18+
public interface IRequestPostProcessor<in TRequest, in TResponse> where TRequest : notnull
19+
{
20+
/// <summary>
21+
/// Processes the specified request and response asynchronously.
22+
/// </summary>
23+
/// <param name="request">The request object containing the data to be processed. Cannot be null.</param>
24+
/// <param name="response">The response object to be populated or modified during processing. Cannot be null.</param>
25+
/// <param name="cancellationToken">A token that can be used to request cancellation of the operation.</param>
26+
/// <returns>A task that represents the asynchronous processing operation.</returns>
27+
Task Process(TRequest request, TResponse response, CancellationToken cancellationToken = default);
28+
}
29+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
using System.Threading;
2+
using System.Threading.Tasks;
3+
4+
namespace EasyExtensions.Mediator.Contracts
5+
{
6+
/// <summary>
7+
/// Defines a contract for processing a request asynchronously before it is handled.
8+
/// </summary>
9+
/// <remarks>Implement this interface to perform pre-processing logic on requests, such as validation,
10+
/// logging, or modification, before the main request handler executes. Pre-processors can be used to enforce
11+
/// cross-cutting concerns or prepare requests for further handling.</remarks>
12+
/// <typeparam name="TRequest">The type of the request to be processed. Must not be null.</typeparam>
13+
public interface IRequestPreProcessor<in TRequest> where TRequest : notnull
14+
{
15+
/// <summary>
16+
/// Processes the specified request asynchronously.
17+
/// </summary>
18+
/// <param name="request">The request to be processed. Cannot be null.</param>
19+
/// <param name="cancellationToken">A cancellation token that can be used to cancel the operation.</param>
20+
/// <returns>A task that represents the asynchronous processing operation.</returns>
21+
Task Process(TRequest request, CancellationToken cancellationToken = default);
22+
}
23+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
using System;
2+
using System.Threading;
3+
using System.Threading.Tasks;
4+
5+
namespace EasyExtensions.Mediator.Contracts
6+
{
7+
/// <summary>
8+
/// Provides a container for an asynchronous notification handler and its associated delegate.
9+
/// </summary>
10+
/// <remarks>Use this class to encapsulate a notification handler instance along with a delegate that
11+
/// processes notifications asynchronously. This enables flexible assignment and execution of notification handling
12+
/// logic, typically within a messaging or event-driven system.</remarks>
13+
public class NotificationHandlerExecutor
14+
{
15+
/// <summary>
16+
/// Gets the instance of the handler associated with this object.
17+
/// </summary>
18+
public object HandlerInstance { get; } = null!;
19+
20+
/// <summary>
21+
/// Gets the delegate that handles notification processing asynchronously.
22+
/// </summary>
23+
/// <remarks>The delegate accepts an <see cref="INotification"/> instance and a <see
24+
/// cref="CancellationToken"/>, and returns a <see cref="Task"/> representing the asynchronous operation. Assign
25+
/// this property to specify custom logic for handling notifications.</remarks>
26+
public Func<INotification, CancellationToken, Task>? HandlerCallback { get; }
27+
}
28+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
namespace EasyExtensions.Mediator.Contracts
2+
{
3+
/// <summary>
4+
/// Represents the state of an exception handler for a request, including whether the exception has been handled and
5+
/// an optional response value.
6+
/// </summary>
7+
/// <typeparam name="TResponse">The type of the response associated with the handled request. Must be a reference type.</typeparam>
8+
public class RequestExceptionHandlerState<TResponse>
9+
{
10+
/// <summary>
11+
/// Gets a value indicating whether the event has been marked as handled.
12+
/// </summary>
13+
public bool Handled { get; private set; }
14+
15+
/// <summary>
16+
/// Gets the response returned by the operation, if available.
17+
/// </summary>
18+
public TResponse Response { get; private set; } = default!;
19+
20+
/// <summary>
21+
/// Marks the request as handled and sets the response to the specified value.
22+
/// </summary>
23+
/// <param name="response">The response value to associate with the handled request.</param>
24+
public void SetHandled(TResponse response)
25+
{
26+
Handled = true;
27+
Response = response;
28+
}
29+
}
30+
}

0 commit comments

Comments
 (0)