Skip to content

The search box in the website knows all the secrets—try it!

For any queries, join our Discord Channel to reach us faster.

JasperFx Logo

JasperFx provides formal support for Wolverine and other JasperFx libraries. Please check our Support Plans for more details.

Marten as Saga Storage

Marten is an easy option for persistent sagas with Wolverine. Yet again, to opt into using Marten as your saga storage mechanism in Wolverine, you just need to add the IntegrateWithWolverine() option to your Marten configuration as shown in the Marten Integration Getting Started section.

When using the Wolverine + Marten integration, your stateful saga classes should be valid Marten document types that inherit from Wolverine's Saga type, which generally means being a public class with a valid Marten identity member. Remember that your handler methods in Wolverine can accept "method injected" dependencies from your underlying IoC container.

See the Saga with Marten sample project.

Strong-Typed Identifiers 5.14

Wolverine supports using strong-typed identifiers (e.g., OrderId, InvoiceId) as the identity of a Marten saga document. This works the same way that strong-typed identifiers are supported for Marten aggregate types. As long as Marten can resolve the identity type, Wolverine will correctly extract the saga identity from your messages.

Here's an example using the StronglyTypedId library:

cs
[StronglyTypedId(Template.Guid)]
public readonly partial struct OrderSagaId;

public class OrderSagaWorkflow : Wolverine.Saga
{
    public OrderSagaId Id { get; set; }

    public string CustomerName { get; set; }
    public bool ItemsPicked { get; set; }
    public bool PaymentProcessed { get; set; }
    public bool Shipped { get; set; }

    public static OrderSagaWorkflow Start(StartOrderSaga command)
    {
        return new OrderSagaWorkflow
        {
            Id = command.OrderId,
            CustomerName = command.CustomerName
        };
    }

    public void Handle(PickOrderItems command)
    {
        ItemsPicked = true;
        checkForCompletion();
    }

    public void Handle(ProcessOrderPayment command)
    {
        PaymentProcessed = true;
        checkForCompletion();
    }

    public void Handle(ShipOrder command)
    {
        Shipped = true;
        checkForCompletion();
    }

    public void Handle(CancelOrderSaga command)
    {
        MarkCompleted();
    }

    private void checkForCompletion()
    {
        if (ItemsPicked && PaymentProcessed && Shipped)
        {
            MarkCompleted();
        }
    }
}

// Messages using the strong-typed identifier
public record StartOrderSaga(OrderSagaId OrderId, string CustomerName);
public record PickOrderItems(OrderSagaId OrderSagaWorkflowId);
public record ProcessOrderPayment(OrderSagaId OrderSagaWorkflowId);
public record ShipOrder(OrderSagaId OrderSagaWorkflowId);
public record CancelOrderSaga(OrderSagaId OrderSagaWorkflowId);

// Message using [SagaIdentity] attribute with strong-typed ID
public class CompleteOrderStep
{
    [SagaIdentity] public OrderSagaId TheOrderId { get; set; }
}

snippet source | anchor

The standard saga identity resolution conventions still apply:

  1. Properties decorated with [SagaIdentity]
  2. A property named {SagaTypeName}Id (e.g., OrderSagaWorkflowId)
  3. A property named SagaId
  4. A property named Id

Any strong-typed identifier type that Marten can resolve will work, including types generated by StronglyTypedId, Vogen, or hand-crafted value types.

Optimistic Concurrency 3.0

Marten will automatically apply numeric revisioning to Wolverine Saga storage, and will increment the Version while handling Saga commands to use Marten's native optimistic concurrency protection.

Released under the MIT License.