I just finished the work in Jasper I’d described in Changing Jasper’s Direction and released a new Jasper 0.9 Nuget and a matching dotnet new
template package with the newly streamlined structure. Hopefully, this represents the first big step on the march to a 1.0 release and production readiness by the end of this year. I’m going to start blogging a lot more examples on Jasper in hopes of building some community and possibly attracting some other contributors.
Today I want to talk about using Jasper as an in memory bus (like MediatR) within an ASP.Net Core application. To make Jasper less scary, let’s start with an ASP.Net Core application built with the dotnet new webapi
template. After adding a dependency to Jasper via Nuget, you can add Jasper to your new application with this code:
public class Program { public static void Main(string[] args) { CreateWebHostBuilder(args).Build().Run(); } public static IWebHostBuilder CreateWebHostBuilder(string[] args) => WebHost.CreateDefaultBuilder(args) .UseStartup() // This is all you have to do to use Jasper // with all its default behavior .UseJasper(); }
Do note that this quietly replaces the default ASP.net Core DI container with Lamar that is absolutely required for Jasper usage. Also note that this is choosing to use Jasper with all its default behavior. Lastly, this simplified configuration approach will only discover message handlers within the same assembly with your main application. See this page for more information on configuring Jasper.
Now, let’s say we need to have an HTTP endpoint that will receive a command to create a new user within our system like this:
public class CreateUser { public string FirstName { get; set; } public string LastName { get; set; } public string Email { get; set; } }
Since we’ve decided that we want the actual work to be done by a Jasper message handler instead of just burying it within the normal MVC controller cruft, let’s write our first Jasper message handler that will handle this message by just writing a message to the console:
public class UserHandler { public void Handle(CreateUser user) { Console.WriteLine("Hey, I got a new user!"); } }
Or to stay with using async anytime the handler involved any kind of IO, use an asynchronous version:
public class UserHandler { public Task Handle(CreateUser user) { return Console.Out.WriteLineAsync("Hey, I got a user"); } }
Now, switching to the MVC Core side, let’s add the controller action that receives a JSON POST for CreateUser
and delegates to Jasper:
public class UserController : ControllerBase { private readonly IMessageContext _messages; public UserController(IMessageContext messages) { _messages = messages; } [HttpPost("user")] public async Task CreateUser([FromBody] CreateUser user) { // Process the message NOW MISTER await _messages.Invoke(user); // OR // Put the incoming message in the in // memory worker queues and Jasper // will process it when it can await _messages.Enqueue(user); // OR, not sure why you'd want to do this, but say // you want this processed in 5 minutes await _messages.Schedule(user, 5.Seconds()); return Ok(); } }
The IMessageContext
interface is the entry point for all messaging within Jasper and is automatically registered in your application’s Lamar container as ContainerScoped()
.
Alrighty then, it’s not super exciting in functionality, but this is actually a working application that hopefully shows off a decent getting started story for adding Jasper to an ASP.net Core system. In my next set of posts we’ll embellish the message handling to show off Jasper’s durable messaging, “outbox” support, and middleware.
In the meantime, please feel free to kick the tires on Jasper and let the community know what you think works and what needs some improvement in the Gitter room.
Jasper vs. MediatR
As strictly an in-memory messaging bus, Jasper supports a superset of MediatR’s functionality, but with a very different runtime architecture. In terms of implementation, MediatR is purposely simplistic and mostly relies on Inversion of Control (IoC) containers’ support for discovering and resolving generic types implementing its adapter interfaces likeIRequestHandler
. Jasper, on the other hand, does its own command discovery (using Lamar’s built in type scanning) and eschews the usage of adapter interfaces or base classes in favor of naming conventions.
Selfishly speaking, MediatR has been a big headache to me with StructureMap and Lamar support because it encourages users to be too ambitious with generics to work around MediatR’s limited support for runtime composition. I certainly hope that Jasper’s middleware strategy — if it ever gets significant usage in the wild — generates far less user confusion.
As far as advantages, MediatR is obviously more mature, heavily used, and well understood by folks (as long as they stay sane on the generics usage). Plus some folks will prefer MediatR’s more explicit interfaces versus Jasper’s usage of conventions. In my opinion, Jasper’s potential advantages are:
- Cleaner code because Jasper adapts itself to your code rather than forcing you to use its constructs
- Higher performance, especially when it’s idiomatic Jasper HTTP handlers versus the combination of ASP.Net Core controller actions + MediatR.
- Jasper supports quite a bit more functionality, and gives you much more control over message priority, error handling, and durability
- Jasper composition and middleware design allows users to intelligently order middleware and provides many more extensibility scenarios
- It’s rudimentary right now (just dump out the generated code), but Jasper provides out of the box diagnostics to show you what middleware is applied for which message types