Generic Serverless CQRS Azure Functions Endpoints
Let’s imagine we have an ICommand and an IQuery
namespace Core.Common
{
public interface ICommand : IRequest
{
}
public interface IQuery<out T> : IRequest<T>
{
}
}
Then, we can implement two generic Functions which are triggered by a Http request and will send those Commands and Queries via MediatR to it’s handlers:
namespace Functions
{
public class CqrsFunctions
{
private readonly IMediator _mediator;
public CqrsFunctions(IMediator mediator)
{
_mediator = mediator;
}
[FunctionName(nameof(Command))]
public async Task Command(
[HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "command/{commandName}")]
HttpRequest req, string commandName)
{
var commandType = typeof(ICommand);
var type = commandType.Assembly.GetTypes()
.Where(x => !x.IsAbstract)
.Where(x => !x.IsInterface)
.Where(x => commandType.IsAssignableFrom(x))
.FirstOrDefault(x => x.FullName.Contains(commandName));
var body = await req.ReadAsStringAsync();
var cmd = JsonConvert.DeserializeObject(body, type);
await _mediator.Send(cmd);
}
[FunctionName(nameof(Query))]
public async Task<IActionResult> Query(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "query/{queryName}")]
HttpRequest req, string queryName)
{
var queryType = typeof(IQuery<>);
var type = queryType.Assembly.GetTypes()
.Where(x => !x.IsAbstract)
.Where(x => !x.IsInterface)
.Where(x => x.GetInterfaces().Any(x => x.IsGenericType && x.GetGenericTypeDefinition() == queryType))
.FirstOrDefault(x => x.FullName.Contains(queryName));
var queryDictionary = req.Query.ToDictionary(x => x.Key, x => x.Value.ToString());
var queryJson = JsonConvert.SerializeObject(queryDictionary);
var query = JsonConvert.DeserializeObject(queryJson, type);
var restult = await _mediator.Send(query);
return new OkObjectResult(restult);
}
}
}
Then you can simply send commands and queries from a client to the server which are executed in a serverless manner:
This implementation is extremely flexible and can be easily adjusted and extended. For example you can simple generate TypeScript representations of those commands and queries (which is what I have done in some projects).
For a more complete example (yes — it’s yet another TodoList sample), take a look at this GitHub Repository.