Transport development
AnyProtocol transport contracts are capability-oriented. IMessagingProtocol
contains only shared lifecycle and capability metadata; an implementation opts
into each operation it actually supports.
API boundary classification
- Application API:
LinkBuilder, contract attributes, client/event abstractions, fault and validation types, message metadata/schema contracts, payload policy options, andILargePayloadStore. - Provider SPI:
IMessagingProtocol, operation capability interfaces,TransportEnvelope,TransportSemantics, codecs, and subscription contracts. - Hosting SPI:
RuntimePlan,MessageDispatcher,TransportRegistry,OutboundOperationExecutor, andOutboundOperationLifetime. ASP.NET Core, MCP, and Microsoft DI adapters compose these types; application code normally does not. - Implementation detail: dispatch path components, route tables, subscription ownership, and
RuntimeLifetimeOwnerremain internal. Cross-assembly framework access uses explicit friend assemblies instead of public visibility.
Choose the smallest contracts
ISendTransport— publish an envelope to a logical channel.ISubscriptionTransport— consume envelopes from a logical channel.IRequestReplyTransport— perform native request/reply.IStreamingTransport— perform native streaming.INativeServerTransport— the host owns server dispatch, so the transport does not expose send or subscription operations.
Most broker transports implement both ISendTransport and
ISubscriptionTransport. A request-only HTTP client can implement
ISendTransport and IRequestReplyTransport; it does not need to implement
ISubscriptionTransport. A native server adapter can implement only
IMessagingProtocol, INativeServerTransport, and any readiness contract it
supports.
public sealed class ExampleRequestTransport :
ISendTransport,
IRequestReplyTransport
{
public TransportCapabilities Capabilities => TransportCapabilities.NativeHeaders;
public TransportSemantics Semantics { get; } = new()
{
DeliveryGuarantee = TransportDeliveryGuarantee.AtMostOnce,
Ordering = TransportOrdering.None,
Durability = TransportDurability.Volatile,
SupportsCancellation = true
};
public ValueTask SendAsync(
string channel,
TransportEnvelope envelope,
CancellationToken cancellationToken = default)
=> throw new NotSupportedException("Example omitted.");
public ValueTask<TransportEnvelope> RequestAsync(
string channel,
TransportEnvelope request,
CancellationToken cancellationToken = default)
=> throw new NotSupportedException("Example omitted.");
public ValueTask DisposeAsync() => ValueTask.CompletedTask;
}
Capability interfaces are the source of truth for callable operations. The link
validator derives native request/reply and streaming support from
IRequestReplyTransport and IStreamingTransport. TransportCapabilities
remains for wire and broker properties that are not represented by an operation
interface. For emulated request/reply or streaming, the transport must expose
both send and subscription contracts. Missing contracts fail during
LinkBuilder.Build, before the bus starts.
ISubscriptionTransport.SubscribeAsync returns an ITransportSubscription.
Implement StopAcceptingAsync as the transport-specific quiesce operation and
keep it separate from DisposeAsync: the bus uses the former during the
Draining phase and the latter only after the bounded drain/forced-cancellation
window. Document whether quiescing preserves, requeues, or cancels an in-flight
callback. New callbacks must not be admitted after quiesce begins.
If a callback races with the shared admission gate and throws
MessageAdmissionRejectedException, durable transports must requeue or leave the
delivery uncommitted; it is a shutdown signal, not a handler fault or dead-letter case.