A starting point for dotnet projects that use minimal api and mediatr as a CQRS platform. Designed to allow expansion without a steep learning curve.
Events are also raised after command completion
- Separation of concerns (API, Queries, Commands & event handlers)
- Shallow learning curve.
- Built in post-command event
- built in query / command duration logging
Use the solution file
You'll need docker
And dotnet 7.0 or later
Api endpoint → Query → QueryHandler → QueryResult → Api endpoint
Api endpoint → Command → CommandValidator → CommandHandler → CommandResult → Api endpoint
simple eh ?
- Add a new Endpoint
- Add a new Query Result
- Add a new Query
- Add a new Query Handler
Everything should be automatically registered
Add a new query endpoint to the AddQueries method
app.MapGet("/<my endpoint>", async (HttpContext httpContext, IMediator mediator) =>
{
var result = await mediator.Send(new <my query>());
var rtn = result.Format();
return rtn;
})
.WithName("<my query name>")
.WithOpenApi();
}Add a new query result that inherits from BaseQueryResult in a folder below /Core.Queries/Queries/
using Core.Data.Datamodels;
namespace Core.Queries.Queries.<my query>;
public class <my query>: BaseQueryResult
{
// properties to carry result values
}Add a new query that inherits from BaseQuery in a folder below /Core.Queries/Queries/
namespace Core.Queries.Queries.<my query>;
public class <my query>: BaseQuery<my query result> { }Add a new query handler that inherits from BaseQueryHandler in a folder below /Core.Queries/Queries/
using Core.Data.Datamodels;
using Microsoft.Extensions.Logging;
namespace Core.Queries.Queries.<my query>;
public class <my query handler>: BaseQueryHandler<<my query>, <my query result>>
{
public GetWidgetHandler(ILogger<<my query>> logger)
: base(logger)
{
}
protected override Task HandleQuery(<my query>query,
<my query result> result,
CancellationToken cancellationToken)
{
// do your work here
}
}- Add a new Endpoint
- Add a new Command Result
- Add a new Command
- Add a new Command Validator
- Add a new Command Handler
Everything should be automatically registered
Add a new command endpoint to the AddCommands method
app.mapPost("/<my endpoint>", async (HttpContext httpContext, IMediator mediator) =>
{
var result = await mediator.Send(new <my command>());
var rtn = result.Format();
return rtn;
})
.WithName("<my command name>")
.WithOpenApi();
}Add a new Command Result
Add a new command result that inherits from BaseComandResult in a folder below /Core.Commands/Commands/
using Core.Data.Datamodels;
namespace Core.Commands.Commands.<my command>;
public class <my command>: BaseComandResult
{
// properties to carry result values
}Add a new Command
Add a new command record that inherits from BaseComandR in a folder below /Core.Commands/Commands/
namespace Core.Commands.Commands.Widgets;
/// <summary>
/// Command used to pass in values
/// </summary>
/// <param name="Name"></param>
public record my command(string Name) : BaseCommand<My command result>;Add a new Command Validator
Add a new validator class that inherits from AbstractValidator in a folder below /Core.Commands/Commands/ This uses FluentValidation to enforce rules
namespace Core.Commands.Commands.Widgets;
/// <summary>
/// Ensures the correctness of the values passed in
/// </summary>
public class <my command>Validator : AbstractValidator<my command>
{
public <my command>Validator()
{
RuleFor(x => x.Name).NotEmpty().WithErrorCode("bad-1");
}
}Add a new Command Handler
Add a new handler class that inherits from BaseCommandHandler in a folder below /Core.Commands/Commands/ This uses FluentValidation to enforce rules
using Microsoft.Extensions.Logging;
namespace Core.Commands.Commands.Widgets;
/// <summary>
/// Executes the operation on the values in the command
/// </summary>
public class <my command>Handler : BaseCommandHandler<<my command>Command, <my command>Result>
{
public <my command>Handler(IValidator<AddWidgetCommand> validator, ILogger<AddWidgetHandler> logger, IMediator mediator)
: base(validator, logger, mediator)
{ }
protected override Task<<my command>Result> HandleCommand(<my command> request, <my command>Result result, CancellationToken cancellationToken)
{
// do work
}
}Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.
Please make sure to update tests as appropriate.