# Steps, Stages, and Connectors
Each pipeline is composed of *steps*. Each step represents a behavior that will be executed at the given place within the pipeline. Add additional behavior to the pipeline by registering a new step or replacing the behavior of an existing step.
The pipeline is broken down into smaller composable units called *stages* and *connectors*. A stage is a group of steps acting on the same level of abstraction.
> [!NOTE]
> There currently is no way to provide custom pipelines, stages, fork connectors, or stage connectors. Existing stages, fork connectors, or stage connectors can be replaced but extreme caution has to be applied, because stages, fork connectors and stage connectors define the inner workings of NServiceBus core behavior. Wrongly replaced stages or connectors could lead to message loss. The examples below are included for completeness.
## Stages
The pipeline consists of three main parts: incoming, outgoing and recoverability. Both parts are composed of a number of stages.
The following lists describe some of the common stages that behaviors can be built for. Each stage has a context associated with it (which is used when implementing a custom behavior).
> [!NOTE]
> Data can be added to, and retrieved from, the context with the `Extensions` property of type `ContextBag`. Each following stage has access to data set in a previous stage but data set in a later stage is not available in a prior stage. The context bag is cloned at each stage transition and is not thread-safe.
In the diagram User Code can refer to a handler or a saga. If the handler or saga sends a message, publishes an event, or replies to a message, then the details from the incoming message will be added to the outgoing context.
### Incoming pipeline stages
* Transport Receive: Behaviors in this stage are responsible for signaling failure or success to the transport. In this stage no outgoing operations are supported. This stage provides `ITransportReceiveContext` to its behaviors.
* Incoming Physical Message: Behaviors on this stage have access to the raw message body before it is deserialized. This stage provides `IIncomingPhysicalMessageContext` to its behaviors.
* Incoming Logical Message: This stage provides information about the received message type and its deserialized instance. It provides `IIncomingLogicalMessageContext` to its behaviors.
* Invoke Handlers: Each received message can be handled by multiple handlers. This stage will be executed once for every associated handler and provides `IInvokeHandlerContext` to the its behaviors.
The connection between the Incoming Physical Message stage and the audit stages is an example of a fork. The message will flow down the main path and then down the fork path. The fork paths are only followed if the audit feature has been enabled.
```mermaid
graph LR
Transport((Transport))
subgraph Incoming Pipeline
TR[Transport
Receive]
IH[Invoke
Handler]
ILM[Incoming
Logical
Message]
IPM[Incoming
Physical
Message]
RUC((Receiving User Code))
subgraph Ancillary Actions
Audit[Audit]
Routing
Dispatch
end
end
AncillaryTransport[Transport]
Transport -- onMessage --> TR
IPM -- Step 1 --> ILM
IPM -. Step 2 .-> Audit
ILM --> IH
TR --> IPM
IH --> RUC
Dispatch --> AncillaryTransport
Audit --> Routing
Routing --> Dispatch
click IH "/nservicebus/handlers-and-sagas"
click Transport "/transports/"
click Audit "/nservicebus/operations/auditing"
```
### Outgoing Pipeline Stages
* Operation-specific processing: There is a dedicated stage for each context operation (e.g., Send, Publish, Subscribe, ...). Behaviors can use one of the following contexts: `IOutgoingSendContext`, `IOutgoingPublishContext`, `IOutgoingReplyContext`, `ISubscribeContext`, `IUnsubscribeContext`. Subscribe and Unsubscribe are not shown on the diagram below.
* Outgoing Logical Message: Behaviors on this stage have access to the message that should be sent. Use `IOutgoingLogicalMessageContext` in a behavior to enlist in this stage.
* Outgoing Physical Message: Enables access to the serialized message. This stage provides an `IOutgoingPhysicalMessageContext` instance to its behaviors.
* Routing: Allows the selected routing strategies for outgoing messages to be manipulated. If the incoming pipeline initiated the outgoing pipeline, this stage collects [all outgoing operations](/nservicebus/messaging/batched-dispatch.md) (except for [immediate dispatch messages](/nservicebus/messaging/send-a-message.md#dispatching-a-message-immediately)).
This stage provides an `IRoutingContext` instance to its behaviors.
* Batch Dispatch: Forwards all [collected outgoing operations](/nservicebus/messaging/batched-dispatch.md) to the dispatch stage once message processing has been completed. This stage provides access to the collection of transport operations that are to be dispatched. This stage provides an `IBatchDispatchContext` instance to its behaviors.
* Dispatch: Provides access to outgoing dispatch operations before they are handed off to the transport. This stage provides an `IDispatchContext` instance to its behaviors.
```mermaid
graph LR
UC((Initiating User Code))
subgraph Outgoing Pipeline
Outgoing{Outgoing}
OP[Outgoing
Publish]
OS[Outgoing
Send]
OR[Outgoing
Reply]
OLM[Outgoing
Logical
Message]
OPM[Outgoing
Physical
Message]
Routing[Routing]
end
subgraph Dispatch Phase
Dispatch{Dispatch}
Transport((Transport))
BD[Batch
Dispatch]
ID[Immediate
Dispatch]
end
UC --> Outgoing
Outgoing --> OP
Outgoing --> OS
Outgoing --> OR
OP --> OLM
OS --> OLM
OR --> OLM
OLM --> OPM
OPM --> Routing
Routing --> Dispatch
ID--> Transport
Dispatch --> BD
Dispatch --> ID
BD --> Transport
```
### Recoverability pipeline stages
* Recoverability: Behaviors on this stage have access the raw message that failed. This stage provides `IRecoverabilityContext` to its behaviors, which exposes the endpoint's recoverability settings, the recoverability action (e.g. `MoveToErrorQueue`), as well as the `ErrorContext` and metadata.
* Routing: Provides access to the routing strategies that have been selected for the failed message. This stage provides `IRoutingContext` to its behaviors.
* Dispatch: Provides access to outgoing dispatch operations before they are handed off to the transport. This stage provides `IDispatchContext` to its behaviors.
``` mermaid
graph LR
Transport((Transport))
Transport2((Transport))
subgraph Recoverability Pipeline
TR[Recoverability]
RR[Routing]
Dispatch[Immediate
Dispatch]
end
Transport -- onError --> TR
TR -. 0-to-n .-> RR
RR --> Dispatch
Dispatch --> Transport2
click RR "/nservicebus/recoverability"
click Transport "/transports/"
```
### Optional pipeline stages
Behaviors in the _audit_ stage have access to the message to be audited/sent to the audit queue and audit address. Behaviors should use `IAuditContext` to enlist in this stage. This stage is only entered if [message auditing](/nservicebus/operations/auditing.md) is enabled.
## Extension bag
Pipeline contexts have an extension bag that can be used to create, read, update, or delete custom state with a key identifier. For example, this can be used to *set* metadata- or pipeline-specific state in an incoming behavior that can be used in later pipeline stages if needed. State stored in the extension bag will not be available once the extension bag's scope ends at the end of the pipeline.
State set during a *forked* pipeline will not be available to the *forking* pipeline. For example, state changes during the *outgoing* pipeline will not be available in the *incoming* pipeline. If the state has to be propagated, have the *forking* pipeline set a context object that the *forked* pipeline later can get and modify as needed.
```cs
public class SetContextBehavior :
Behavior
{
public override async Task Invoke(IIncomingPhysicalMessageContext context, Func next)
{
// set the state
context.Extensions.Set(new SharedData());
await next();
}
}
public class GetContextBehavior :
Behavior
{
public override async Task Invoke(IOutgoingPhysicalMessageContext context, Func next)
{
if (context.Extensions.TryGet(out var state))
{
// work with the state
}
await next();
}
}
```
## Stage connectors
Stage connectors connect from the current stage (e.g., `IOutgoingLogicalMessageContext`) to another stage (e.g., `IOutgoingPhysicalMessageContext`). To override an existing stage, inherit from `StageConnector` and then replace an existing stage connector. Most pipeline extensions can be done by inheriting from `Behavior`. It is rarely necessary to replace existing stage connectors. When implementing a stage connector,
ensure that all required data is passed to the next stage.
```cs
public class CustomStageConnector :
StageConnector
{
public override Task Invoke(IOutgoingLogicalMessageContext context, Func stage)
{
// Finalize the work in the current stage
byte[] body = { };
RoutingStrategy[] routingStrategies = { };
// Start the next stage
return stage(this.CreateOutgoingPhysicalMessageContext(body, routingStrategies, context));
}
}
public class FeatureReplacingExistingStage :
Feature
{
protected override void Setup(FeatureConfigurationContext context)
{
var pipeline = context.Pipeline;
pipeline.Replace("SerializeMessageConnector", new CustomStageConnector());
}
}
```
## Fork connectors
```mermaid
graph LR
subgraph Root pipeline
A[TFromContext] --- B{Fork Connector}
B --- C[TFromContext]
end
subgraph Forked pipeline
B --> D[TForkContext]
end
```
Fork connectors fork from a current stage (e.g., `IIncomingPhysicalMessageContext`) to another independent pipeline (e.g., `IAuditContext`). To override an existing fork connector, inherit from `ForkConnector` and then replace an existing fork connector.
```cs
public class CustomForkConnector :
ForkConnector
{
public override async Task Invoke(IIncomingPhysicalMessageContext context, Func next, Func fork)
{
// Finalize the work in the current stage
await next();
OutgoingMessage message = null;
var auditAddress = "AuditAddress";
// Fork into new pipeline
await fork(this.CreateAuditContext(message, auditAddress, TimeSpan.FromHours(1), context));
}
}
public class FeatureReplacingExistingForkConnector :
Feature
{
protected override void Setup(FeatureConfigurationContext context)
{
var pipeline = context.Pipeline;
pipeline.Replace("AuditProcessedMessage", new CustomForkConnector());
}
}
```
## Stage Fork Connector
```mermaid
graph LR
subgraph Root stage
A[TFromContext] --- B{StageFork
Connector}
B --- C[TToContext]
end
subgraph Forked pipeline
B --> D[TForkContext]
end
```
Stage fork connectors are essentially a combination of a stage connector and a fork connector. They have the ability to connect from the current stage (e.g., `ITransportReceiveContext`) to another stage (e.g., `IIncomingPhysicalMessageContext`) and fork to another independent pipeline (e.g., `IBatchedDispatchContext`). In order to override an existing stage fork connector, inherit from `StageForkConnector
```cs
public class CustomStageForkConnector :
StageForkConnector
{
public override async Task Invoke(ITransportReceiveContext context, Func stage, Func fork)
{
// Finalize the work in the current stage
var physicalMessageContext = this.CreateIncomingPhysicalMessageContext(context.Message, context);
// Start the next stage
await stage(physicalMessageContext);
TransportOperation[] operations =
{
};
var batchDispatchContext = this.CreateBatchDispatchContext(operations, physicalMessageContext);
// Fork into new pipeline
await fork(batchDispatchContext);
}
}
public class FeatureReplacingExistingStageForkConnector :
Feature
{
protected override void Setup(FeatureConfigurationContext context)
{
var pipeline = context.Pipeline;
pipeline.Replace("TransportReceiveToPhysicalMessageProcessingConnector", new CustomStageForkConnector());
}
}
```