Middleware
Two types of middleware exist: adapters and handlers. Adapters are used to adapt data flowing in and out of the channel, while handlers are used to process data coming in from the channel. Although the concept is similar, adapters and handlers are different in the way they are executed within the pipeline.
For data coming through the channel input, both middleware types can be applied.
graph LR;
channelInput((Input)) --> a1[/Adapter/]
subgraph adapters
a1 --> a2[/Adapter/]
end
a2 --> h1[/Handler/]
a2 --> h2[/Handler/]
For data going through the channel output, however, only adapters are used. An internal handler that executes at the end of this pipeline is responsible for sending the data to the channel's underlying transport.
graph LR;
channelOutput((Output)) --> a1[/Adapter/]
subgraph adapters
a1 --> a2[/Adapter/]
end
a2 --> transport([Transport])
Characteristics
Unless there are very specific needs, middleware components should inherit from the abstract classes provided instead of implementing the interfaces directly. The base class for all middleware components (adapters and handlers) does a few things for us that won't be available when implementing the interfaces directly. This includes
- Type checking
- Type mutation
Type checking is essentially making sure the type of the data received is intended for the middleware. If it's not, the middleware component is not executed. If the component is an adapter, the data is automatically forwarded to the next middleware component in the pipeline.
Type mutation is the ability to convert data to a compatible type expected by the middleware component. Built-in type mutation supports:
IEnumerable<T>to and fromTbyte[]to and fromIReadableByteBufferbyte[]to and fromIByteBuffer
Important
Converting from IReadableByteBuffer to byte[] consumes the readable buffer (reads all bytes), since byte[] does not preserve read-offset semantics.
Adapters vs Handlers
Because adapters and handlers are so similar, there might be a temptation to do everything with adapters. And while that's feasable, it's not recommended. Adapters should be used to adapt data and handlers to handle data.
Tip
Adapters shape the data. Handlers decide what to do with it.
- Adapters adapt and forward data
- Handlers handle data
- Adapters can run at any point in the pipeline
- Handlers run at the end of the pipeline
- Data with type T is forwarded to the next (single) adapter in the pipeline
- Data with type T can be forwarded to multiple handlers