Extensibility - rebus-org/Rebus GitHub Wiki
Rebus is very extensible. The core of Rebus is pretty small, and most of Rebus' functionality is in fact implemented by plugging in pipeline steps and inserting decorators in various places.
On this page, you can get an overview over the available extensibility points.
Rebus uses Injectionist to configure itself when you invoke the configuration API.
Injectionist is a simple, IoC container-like thing, that allows you to register implementations of various interfaces and have them resolved, recursively resolving any necessary dependencies. You can read more about Injectionist in its README.
Rebus relies heavily on decorators to do its things, and you too can leverate the Injectionist's decoration capabilities. In fact, any of Rebus' abstractions can be decorated if you like - you can even replace any of Rebus' own implementations.
In the Abstractions section you can see which abstractions (or "services") Rebus relies on, which you can provide an implementation for or customize by a decorator, and in the Pipelines section you can get some more information about the special IPipeline
service which is probably the one that is decorated the most.
Rebus (the RebusBus
class, which is at the center of everything) requires the following dependencies:
-
IWorkerFactory
- responsible for creatingIWorker
instances -
IRouter
- responsible for looking up message destinations -
ITransport
- responsible for sending/receiving messages -
IPipeline
- holds pipeline steps for incoming and outgoing messages -
IPipelineInvoker
- can be used to invoke a pipeline -
ISubscriptionStorage
- responsible for persisting subscription information -
Options
- has a few configuration options -
IRebusLoggerFactory
- responsible for creating loggers
All of them can be completely replaced by your own implementations if you like. Moreover, the following services are most likely relevant too (e.g. because they're injected into some of the default pipeline steps):
IRetryStrategy
ISagaStorage
IAsyncTaskFactory
ISerializer
IBackoffStrategy
IErrorTracker
ITimeoutManager
IHandlerActivator
The IPipeline
service mentioned above is extra-special because it provides sequences of outgoing and incoming pipeline steps. When you decorate it, it is usually because you want to insert a step in a specific place in a pipeline, and so extra effort has been made to make it easy and super-declarative to provide such a decorator.
An example can be seen below where a custom outgoing step is inserted, which will copy a specific field from a message into a message header if it exists. First, an extension method to decorate IPipeline
with a PipelineStepInjector
, which is a decorator that can inject new pipeline steps:
public static void AutomaticallySetAccountIdHeader(this OptionsConfigurer configurer)
{
configurer.Decorate<IPipeline>(c =>
{
var pipeline = c.Get<IPipeline>();
var step = new AutoAccountIdOutgoingStep();
return new PipelineStepInjector(pipeline)
.OnSend(step, PipelineRelativePosition.Before, typeof(SerializeOutgoingMessageStep));
});
}
and then the implementation of the actual step:
[StepDocumentation("Automatically copies account ID from messages into a header if possible")]
public class AutoAccountIdOutgoingStep : IOutgoingStep
{
public async Task Process(OutgoingStepContext context, Func<Task> next)
{
var message = context.Load<Message>();
var bodyWithAccountId = message.Body as IHaveAccountId;
if (bodyWithAccountId != null)
{
message.Headers["x-account-id"] = bodyWithAccountId.AccountId;
}
await next();
}
}
Remember that you can see what is in the pipelines by calling .Options(o => o.LogPipeline(verbose: true))
. All of Rebus' pipeline steps are nice enough to use the [StepDocumentation]
attribute to provide an explanation about their purpose, so you can get an overview of what is happening to your messages.