My previous blog post Wolverine is for the developers was an introduction to the Wolverine package, which is created by Jeremy D. Miller. In the blog post, we only discovered the surface, but with this post, we'll delve a bit deeper to fully experience Wolverine.
To keep things as simple as possible I like to use vertical slices because it helps to remove unnecessary abstractions and layers. The examples that are used in the previous blog post show the what and the how of Wolverine, but there's some ceremony involved to orchestrate the flow.
To refresh our minds, the following example was used in the previously.
Within this code, we notice two things:
- There's the
/cartsPOST endpoint that invokes the "Wolverine Bus" and sends it a command
- There's a
CartHandlerclass that handles the incoming command
I specifically want to highlight the first point. While this doesn't seem like a big thing, it adds a little noise. Using the WolverineFx.HTTP NuGet package, we can further reduce some moving parts. Not only does this result in a better experience in dealing with HTTP requests, it's also an improvement to the performance (although this might be negligible).
In the next sections, we'll cover the possibilities.
Using a minimal API the above code can be refactored using the
MapPostToWolverine method (or the
This will do the exact same thing, but shorter.
The bus is invoked using the request body of the request. This will use the generic type to parse the body into an object.
More information can be found in the HTTP Services Documentation.
Endpoints use the Request EndPoint Response (REPR) approach
Using the Wolverine attributes (
[WolverineVerb]), which are equivalent to the ASP.NET attributes (
[HTTPVerb]), an endpoint turns into a handler and vice versa.
For example, a POST endpoint using
HttpPost turns into
Just as handlers, endpoints have the additional benefit that some orchestration code can be extracted into a
In the example below we use the
Before to orchestrate the next flow:
- when a cart already exists, Wolverine short-circuits the request and a
Createmethod is not invoked in this case;
- when the cart doesn't exist, the
Createmethod is invoked because the
Beforemethod returns a
Although this isn't a perfect example, using the
Before helper method keeps the handler's logic of handling the request to the point and easy to test.
In other words, it turns the handler into a pure function (in most of the cases).
A better example is that
Before returns the associated entity to the handler.
Because the handler receives the entity (instead of having to fetch it), which is fetched in
Before and passed along, this results in a simpler test. Within the test case, a mock implementation of the database is probably unneeded with this approach.
Because the handler doesn't require a fetched entity, the above implementation can be refactored using the Problem Details specification.
Instead of using
WolverineContinue.Result() to continue the request, we have to use
The last change to make the enpoint cleaner, as Jeremy pointed out in the comments, is to use the
The presence of this attribute on top of the endpoint results in an empty response with the 204 status code.
The returned values are ignored in the response body, but are still published to the bus to be processed.
Within our example, we don't have to manually return an
IResult anymore, which simplifies the signature and further removes some noise.
More info can be found in the Endpoint Documentation.
The last option is to use
MapWolverineEndpoints can be used to configure the Wolverine HTTP pipeline, which we won't go into in this post.
Within the callback, endpoints can be configured to send or publish incoming requests. Just as with the MapVERBToWolverine approach, these methods expect a generic type to parse the body before it's sent/published to the bus. These are just fire-and-forget endpoints that don't return a response, but an acknowledgment that the request is received (202 Accepted).
More info can be found in the HTTP Messaging Documentation.
In this blog post, we covered some basic functionality of the
Using this package results in a simpler architecture by removing the need to manually communicate with the bus.
An indirect result, which isn't brought up, is that the Wolverine HTTP package helps to keep results (using ProblemDetails) and result codes consistent across the whole API.
The handler keeps the same benefits that were discussed in Wolverine is for the developers.
A warm thank you to the contributors of this blog post
I appreciate it if you would support me if have you enjoyed this post and found it useful, thank you in advance.