0% found this document useful (0 votes)
32 views

implementing-decorators-upon-command-and-query-handlers-slides

Uploaded by

lounes.lou20
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
32 views

implementing-decorators-upon-command-and-query-handlers-slides

Uploaded by

lounes.lou20
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 31

Implementing Decorators upon

Command and Query Handlers

Vladimir Khorikov

@vkhorikov www.enterprisecraftsmanship.com
Agenda

Enrichment
Commands
of the
and handlers
handlers
New Requirement

Database retry
Decorator Pattern

Implemented the first


decorator

Re-runs the same command


handler for 3 times
Decorator is a class or a
method that modifies the
behavior of an existing class
or method without changing
its public interface.
Decorator Pattern
public sealed class DatabaseRetryDecorator<TCommand> : ICommandHandler<TCommand>
where TCommand : ICommand
{
private readonly ICommandHandler<TCommand> _handler;

public Result Handle(TCommand command) {


for (int i = 0; ; i++) {
try {
Result result = _handler.Handle(command);
return result;
}
catch (Exception ex) {
if (i >= _config.NumberOfDatabaseRetries || !IsDatabaseException(ex))
throw;
}
}
}
}
Didn’t require to modify the
existing command handlers
Decorator Pattern

public sealed class Messages


{
private readonly IServiceProvider _provider;

public Result Dispatch(ICommand command) DatabaseRetry


{
Type type = typeof(ICommandHandler<>);
Decorator
Type[] typeArgs = { command.GetType() };
Type handlerType = type.MakeGenericType(typeArgs);
EditPersonalInfo
CommandHandler
dynamic handler = _provider.GetService(handlerType);
Result result = handler.Handle((dynamic)command);

return result;
}
}
Decorator Pattern

Startup.cs

services.AddTransient<ICommandHandler<EditPersonalInfoCommand>>(provider =>
new DatabaseRetryDecorator<EditPersonalInfoCommand>(
new EditPersonalInfoCommandHandler(provider.GetService<SessionFactory>()),
provider.GetService<Config>()));
Decorator Pattern

Decorators

Introduce cross-cutting
concerns

Avoid code duplication

Adhering to the single


responsibility principle
Decorator Pattern

Technical
issues

Business use
cases

Decorators Handlers
Decorator Pattern

Pipeline

D D Rich functionality

D
H D Simple components
Recap: Streamlining the Decorator
Configuration

[DatabaseRetry]
[AuditLog]
public sealed class EditPersonalInfoCommandHandler :
ICommandHandler<EditPersonalInfoCommand>
{
}

Simple and declarative

Cohesive
Recap: Streamlining the Decorator
Configuration
[DatabaseRetry]
[AuditLog]
public sealed class EditPersonalInfoCommandHandler : Several log
ICommandHandler<EditPersonalInfoCommand> records
{
}

[AuditLog]
[DatabaseRetry]
public sealed class EditPersonalInfoCommandHandler : One log
ICommandHandler<EditPersonalInfoCommand> records
{
}

Use a DI library like Simple Injector


Decorators vs. ASP.NET Middleware
public sealed class ExceptionHandler { public sealed class DatabaseRetryDecorator<T>
private readonly RequestDelegate _next; : ICommandHandler<T> where T : ICommand
{
public ExceptionHandler(RequestDelegate next) { private readonly ICommandHandler<T> _handler;
_next = next;
} public DatabaseRetryDecorator(ICommandHandler<T> handler) {
_handler = handler;
public async Task Invoke(HttpContext context) { }
try {
await _next(context); public Result Handle(T command) {
} for (int i = 0; ; i++) {
catch (Exception ex) { try {
await HandleExceptionAsync(context, ex); return _handler.Handle(command); }
} catch (Exception ex) {
} if (i >= _config.NumberOfDatabaseRetries
} || !IsDatabaseException(ex))
throw;
}
} } }

Decorator pattern Decorator pattern

Accept “next” Accept “handler”


Decorators vs. ASP.NET Middleware

Decorators = ASP.NET Middleware

Controllers = Handlers
Decorators vs. ASP.NET Middleware

Why not use ASP.NET


middleware instead of
decorators?
Decorators vs. ASP.NET Middleware

Decorators vs. Middleware

Additional control

Separation of concerns

Easy to apply selectively


Decorators vs. ASP.NET Middleware

Hard to tell ASP.NET to which API


endpoints to apply the middleware

if (context.Request.Path.Value.StartsWith("/api/students/"))

[AuditLog]
[DatabaseRetry]
internal sealed class EditPersonalInfoCommandHandler

ASP.NET action filters


Decorators vs. ASP.NET Middleware

Decorators vs. Middleware

Additional control Good for ubiquitous


and ASP.NET-related
cross-cutting concerns
Separation of concerns

Easy to apply selectively


Decorators vs. ASP.NET Middleware
public sealed class ExceptionHandler {
private readonly RequestDelegate _next;

public ExceptionHandler(RequestDelegate next) {


_next = next;
}

public async Task Invoke(HttpContext context) {


try {
await _next(context);
}
catch (Exception ex) {
await HandleExceptionAsync(context, ex);
}
}
}

Ubiquitous

ASP.NET-related
Decorators vs. ASP.NET Middleware

Middleware = Ubiquitous and ASP.NET-


related concerns

Decorators = Everything else


Decorators

Examples

Caching : IQueryHandler

Transaction handling : UnitOfWork


Command and Query Handlers Best Practices

How should you


organize commands,
queries, and handlers?

Orphaned commands
and queries
Don't reuse command
handlers.
Command and Query Handlers Best Practices

Command
External call
handler 1

Command
External call
handler 1
Command and Query Handlers Best Practices
public sealed class UnregisterCommandHandler : ICommandHandler<UnregisterCommand>
{
public Result Handle(UnregisterCommand command)
{
var unitOfWork = new UnitOfWork(_sessionFactory);
var repository = new StudentRepository(unitOfWork);
Student student = repository.GetById(command.Id);
if (student == null)
return Result.Fail($"No student found for Id {command.Id}");

_messages.Dispatch(new DisenrollCommand(student.Id, 0, "Unregistering"));


_messages.Dispatch(new DisenrollCommand(student.Id, 1, "Unregistering"));

repository.Delete(student);
unitOfWork.Commit();

return Result.Ok();
}
}
Misuse of commands
Command and Query Handlers Best Practices

Commands should not


Command
External call
handler 1
beget other commands

Command
External call
handler 1
Command and Query Handlers Best Practices

Command
External call
handler 1

Domain
model

Command
External call
handler 1
Used decorators to extend command and
query handlers

Decorator is a class that modifies the


behavior of an another class without
Summary changing its public interface
- Allows for introducing cross-cutting
concerns without code duplication
- Adherence to the single responsibility
principle
- Chaining multiple decorators together
allows for introduction of complex
functionality

Streamlined the configuration of the


decorators using attributes
- Great flexibility
Commonalities between the decorators and the
ASP.NET middleware
- Both implement the decorator pattern
- Use middleware for ASP.NET-related
functionality

Summary - Use the decorators for everything else

Benefits in using hand-written decorators over


ASP.NET middleware:
- Additional control over your code
- Separation of the application and ASP.NET
concerns
- Better flexibility

Best practices around working with command


and query handlers
- Put command and query handlers inside their
respective commands and queries
- Don't reuse command handlers
In the Next Module

Simplifying the read model

You might also like