4

Suppose I have the following interface used for publishing messages onto a message queue:

interface IMessageProducer
{
    void Publish<TMessage>(TMessage message);
}

Normally, an implementer of this interface will attempt to publish a message onto the message queue immediately. However, I've found that sometimes I need the messages to be deferred being published until the database transaction has been committed (this ensures that data used in the message handler is available in the database).

Is it appropriate to make this behavior explicit to the developer? For example, extending the interface:

interface IMessageProducer
{
    void Publish<TMessage>(TMessage message);
    void DeferPublishUntilTransactionCommit<TMessage>(ITransaction transaction, TMessage message);
}

Or by making an additional interface that adds this behavior (or even an extension method on the IMessageProducer interface):

interface IDeferUntilTransactionCommitMessageProducer
{
    void DeferPublish<TMessage>(ITransaction transaction, TMessage message);
}

Is my assumption wrong? Is this really an implementation detail where there's no reason to expose it to the developer? I've found it very useful (increasing clarity) to make the interface explicit about the intent so that the developer understands why it's important to defer publishing until the transaction has committed. Otherwise, if it's a cross-cutting concern and an implementation detail, it becomes a configuration problem that may significantly impact the running application. I've seen other software/libraries that make these choices (like using two phase commit) an implementation detail of the interface rather than explicit.

1 Answer 1

1

I'd suggest encapsulating the deferral behaviour into a decorator:

public class TransactionDeferral : IMessageProducer
{
    public TransactionDeferral(IMessageProducer decoratedMessageProducer, ITransaction deferringTransaction)
    {
        _decoratedMessageProducer = decoratedMessageProducer;
        _deferringTransaction = deferringTransaction;
    }

    public void Publish<TMessage>(TMessage message)
    {
        if (_deferringTransaction.IsCommitted)
            _decoratedMessageProducer.Publish(message);
        else
            _deferredMessages.Add(message);
    }

    private void DeferringTransaction_Committed(obsect sender, TransactionCommitedEventArgs e)
    {
        foreach (var message in _deferredMessages)
        {
            _decoratedMessageProducer.Publish(message);
        }
    }
}

The consumer of this interface may be then injected an IMessagePublisher that is in reality an instance of TransactionDeferral decorating the true message publisher, thus becoming completely agnostic of the transaction.

If the consumer has to be aware of the completion of individual message publishing operations, then you could probably modify your original interface for its Publish method to return a Task that can be eventually awaited:

public interface IMessageProducer
{
    Task Publish<TMessage>(TMessage message);
}
1
  • Yeah a decorator is definitely possible if I choose to abstract it away (there's many solutions). But I'm more curious about if it's actually useful to explicitly have deferred publishing as a concept on the existing/new interface. You could argue that this question is an example of what the interface should be: ignore all implementation details or expose some of them where it's very beneficial to the programmer writing the code. Nov 17, 2016 at 23:46

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.