Integration with Sagas
Http endpoints can start Wolverine sagas by just using a return value for a Saga
value.
Let's say that we have a stateful saga type for making online reservations like this:
public class Reservation : Saga
{
public string? Id { get; set; }
// Apply the CompleteReservation to the saga
public void Handle(BookReservation book, ILogger<Reservation> logger)
{
logger.LogInformation("Completing Reservation {Id}", book.Id);
// That's it, we're done. Delete the saga state after the message is done.
MarkCompleted();
}
// Delete this Reservation if it has not already been deleted to enforce a "timeout"
// condition
public void Handle(ReservationTimeout timeout, ILogger<Reservation> logger)
{
logger.LogInformation("Applying timeout to Reservation {Id}", timeout.Id);
// That's it, we're done. Delete the saga state after the message is done.
MarkCompleted();
}
}
To start the Reservation
saga, you could use an HTTP endpoint method like this one:
[WolverinePost("/reservation")]
public static (
// The first return value would be written out as the HTTP response body
ReservationBooked,
// Because this subclasses from Saga, Wolverine will persist this entity
// with saga persistence
Reservation,
// Other return values that trigger no special handling will be treated
// as cascading messages
ReservationTimeout) Post(StartReservation start)
{
return (new ReservationBooked(start.ReservationId, DateTimeOffset.UtcNow), new Reservation { Id = start.ReservationId }, new ReservationTimeout(start.ReservationId));
}
Remember in Wolverine.HTTP that the first return value of an endpoint is assumed to be the response body by Wolverine, so if you are wanting to start a new saga from an HTTP endpoint, the Saga
return value either has to be a secondary value in a tuple to opt into the Saga mechanics. Alternatively, if all you want to do is create a new saga, but nothing else, you can return the Saga
type and force Wolverine to use the return value as a new Saga
as shown in the snippet below. Please note that when creating a Saga
entity in this manner, if it has a static Start()
method, it will not be invoked. Other message handlers in the Saga
will behave as usual.
[WolverinePost("/reservation2")]
// This directs Wolverine to disregard the Reservation return value
// as the response body, and allow Wolverine to use the Reservation
// return as a new saga
[EmptyResponse]
public static Reservation Post2(StartReservation start)
{
return new Reservation { Id = start.ReservationId };
}