JasperFx 0.8.2


Transactional Middleware



Marten Backed Message Persistence Edit on GitHub

To use Jasper's version of guaranteed delivery with store and forward messaging backed by Marten and the Postgresql database:

  1. Install the Jasper.Marten library via Nuget
  2. Import the MartenBackedPersistence extension in your JasperRegistry as shown in the code below

public class AppUsingMartenMessagePersistence : JasperRegistry
    public AppUsingMartenMessagePersistence()
        // Use this line to activate the Marten-backed
        // message persistence for durable, store and forward
        // messaging

        // "config" is the ASP.Net Core IConfiguration for the application
        // "options" is the Marten StoreOptions configuration object
        Settings.ConfigureMarten((context, options) =>

            // Other Marten configuration

        // Use a "durable" TCP listener at port
        // 2222 where the incoming messages will be
        // persisted with Marten upon receipt and
        // deleted only when the message is successfully
        // processed

There's also a shorthand method now that does the equivalent:

public class MartenUsingApp : JasperRegistry
    public MartenUsingApp()
        // This registers the message persistence as well as
        // configuring Marten inside your application
        Settings.PersistMessagesWithMarten((context, options) =>
            // Configure the Marten StoreOptions

"Outbox" Pattern Usage

Using the Marten-backed persistence, you can take advantage of Jasper's implementation of the "outbox" pattern where outgoing messages are persisted as part of a native database transaction before being sent to the outgoing transports. The purpose of this pattern is to achieve guaranteed messaging and consistency between the outgoing messages and the current transaction without being forced to use distributed, two phase transactions between your application database and the outgoing queues like RabbitMQ.

To see the outbox pattern in action, consider this ASP.Net Core MVC controller action method:

public async Task<IActionResult> CreateUser(
    string userId,
    [FromServices] IDocumentStore martenStore,
    [FromServices] IMessageContext context)
    // The Marten IDocumentSession represents the unit of work
    using (var session = martenStore.OpenSession())
        // This directs the current message context
        // to persist outgoing messages with this
        // Marten session.
        await context.EnlistInTransaction(session);

        var theUser = new User { Id = userId };

        await context.Send(new NewUser {UserId = userId});

        // The outgoing messages will be persisted
        // and sent to the outgoing transports
        // as a result of the transaction succeeding here
        await session.SaveChangesAsync();

        return RedirectToAction(nameof(Index));

A couple notes here:

  • The IMessageContext.EnlistInTransaction(IDocumentSession) method is an extension method in the Jasper.Marten library. When it is called, it tells the IMessageContext to register any outgoing messages to be persisted by that IDocumentSession when the Marten session is saved
  • No messages will actually be placed into Jasper's outgoing, sender queues until the session is successfully saved
  • When the session is saved, the outgoing envelopes will be persisted in the same native Postgresql database, then actually sent to the outgoing transport sending agents

Using the outbox pattern, as long as your transaction is successfully committed, the outgoing messages will eventually be sent out, even if the running system somehow manages to get shut down between the transaction being committed and the messages being successfully sent to the recipients or even if the recipient services are temporarily down and unreachable.

The outbox usage is a little bit easier to use within a Jasper message handler action decorated with the [MartenTransaction] attribute as shown below:

public static UserCreated Handle(CreateUser message, IDocumentSession session)
    session.Store(new User{Name = message.Name});

    return new UserCreated{UserName = message.Name};

By decorating the action with that attribute, Jasper.Marten will inject a little bit of code around that method to enlist the current message context into the current Marten IDocumentSession, and the outgoing UserCreated message would be persisted as an outgoing envelope when the session is successfully saved.