Design patterns in use
- Message inheritance.
Requestderives fromMessageand implementsIRequest. The base class supplies the target domain and the params bag. - Routing by key.
RequestBusextendsMessageBus<IRequest, Domain>and returns each request'sTargetDomainas its routing key. The base class stores one handler per domain and queues messages for domains that are not ready. - Callback-based reply. The sender supplies an
Action<Response>when creating theRequest. The dispatcher invokes it when the reply arrives. - Correlation by ID.
RequestTrackerassigns each request an integer ID and stores it in a dictionary. EachResponsecarries the ID of the request it answers, and the tracker uses it to find that request. - Interface contracts.
IRequestdescribes what a request exposes to receivers.IRequestHandlerdescribes a handler with aHandleRequestmethod.
Connections to other systems
- Contracts. The
Domainenum identifies the system that owns a request. Only the enum is relevant here. - Params.
RequestinheritsParamsfromMessage.IRequestexposes it so handlers can read extra data stored as typed params. - Message.
Requestis aMessage, and itsPayloadis typed asMessage, usually a command. - BusRegistry. Holds the
RequestDispatcherinstance. Other systems retrieve it throughServiceLocator. - BusUtility.
Send(Request)forwards toRequestDispatcher.Ask.SendMessage(Message)also routesRequestinstances to that overload. - MessageBus. Supplies routing, handler storage, and the queue that holds requests while a domain has no handler.
- SignalBus. Used indirectly by
MessageBusfor ready signals. This system never sets a ready signal on its bus, so requests are delivered as soon as a handler is registered for the domain.
Key concepts and terminology
- Request. A message asking the system that owns a domain to act on the sender's behalf.
- Response. The reply from the receiving system. It holds the request ID, a
RequestStatus, and a reason when rejected. - Target domain. The
Domainvalue a request is routed by. - Verb. An integer identifying what the request asks for. The source documents it as a domain-specific value, but the enum that defines it is not part of the source.
- Payload. The
Messagethe receiving system acts on if it accepts, usually a command. - Request ID. An integer assigned when the request is sent. It starts at 0 and increases for the session.
- Pending request. A request that has been sent and tracked but not yet answered.
- Handler. An
Action<IRequest>registered per domain withRegister.
Event and data flow
- The sender creates a
Requestwith a target domain, a verb, an optional payload, and an optional response callback. It can also set values inParams. - The sender calls
BusUtility.Send(request), orRequestDispatcher.Ask(request)directly. AskcallsRequestTracker.Track. The tracker assigns the next ID, sets it on the request, and stores the request in its pending dictionary.Askpasses the request toRequestBus.Send, which routes it byTargetDomain.- If a handler is registered for the domain, the handler receives the request immediately. If no handler exists,
MessageBuslogs a warning and queues the request. Queued requests are delivered in order when a handler is registered. - The handler decides whether to accept. It calls
RequestDispatcher.ReplywithResponse.Accepted(request)orResponse.Rejected(request, reason). ReplycallsRequestTracker.TryResolve, which removes the pending request with the matching ID. If found, the dispatcher invokes that request's callback with the response. If not found, for example for a duplicate or late reply, it logs a warning.