Postboy Help

Middleware: Mental Model and Its Place in Postboy Architecture

Summary

Middleware in Postboy is an infrastructure mechanism that lets you intercept message processing at well-defined pipeline stages. It is designed for cross-cutting concerns (logging, metrics, access control, policy checks) without coupling those concerns to message handlers.

When to use / When not to use

Use Middleware when you need

  • Cross-cutting behavior applied to many message types (e.g., logging, tracing, metrics).

  • Centralized policy enforcement (e.g., block certain messages in specific environments).

  • Observability around message lifecycle (measure timing, record outcomes).

  • Non-invasive instrumentation: add behavior without touching handlers.

Do not use Middleware when you need

  • Business logic that is part of a specific use case (put it in the handler/executor).

  • Message transformation / routing rules that should be explicit in your domain flow.

  • Framework lifecycle integration (that belongs in ecosystem docs; core middleware should remain framework-agnostic).

  • Hidden dependencies: middleware that silently changes system behavior without clear ownership or tests.

Core idea: where Middleware sits

Postboy can be viewed as a bus that processes messages through a small “pipeline”:

  • A message is published/executed.

  • The bus invokes middleware hooks around the operation.

  • The actual message delivery/handler execution happens.

  • Middleware can observe the result and optionally interrupt (cancel) the operation before it runs.

Architectural placement

Think of middleware as a thin infrastructure layer around the bus, not around individual handlers.

  • Handlers implement “what to do” for a specific message type.

  • Middleware implements “what always needs to happen around message processing”.

  • Registrators / namespaces / lifecycle tools decide “when registrations exist” and “what gets cleaned up”.

A good mental model: middleware is to Postboy what interceptors are to an RPC pipeline or what “middleware” is to HTTP servers—except Postboy operates on typed messages and has multiple stages (publish/callback/execute).

A simple flow diagram (conceptual)

Postboy pipeline with middleware (conceptual)UserUserPostboyPostboyMiddleware chainMiddleware chainHandler / SubscribersHandler / Subscribersstart operation (publish / callback / execute)before(stage, message)Continue or Interruptalt[Interrupt]operation cancelled (error / cancellation)[Continue]deliver to subscribers / run handlerresult (if any)after(stage, message, result?)doneoperation completed (result / completion)

Vocabulary and boundaries

Message

A typed object representing an intent (event, query, command, synchronous executor). The message is the unit middleware observes and can guard.

Stage

A named point in the processing lifecycle where middleware can run. Stages let you apply different policies to different kinds of operations.

Pipeline context

A small data object passed to middleware hooks. It lets middleware inspect:

  • the current stage

  • the current message instance

Middleware should treat the context as read-only: inspection and decision-making are the primary responsibilities.

Middleware decision

The middleware can:

  • Continue: allow processing to proceed.

  • Interrupt: stop the operation before it reaches handlers/subscribers.

How Middleware differs from other Postboy mechanisms

Middleware vs Handlers

  • Handler: owns the domain behavior for a specific message type.

  • Middleware: owns cross-cutting infrastructure concerns across many message types.

Rule of thumb:

  • If it must be true for one message type → handler.

  • If it must be true for many message types → middleware.

Middleware vs RxJS operators (pipes)

RxJS pipes shape observable streams (filtering, mapping, retrying) usually within a specific subscription flow. Middleware shapes the execution boundary at the bus level and is not tied to any single subscription.

Middleware vs Registrators / Namespaces / lifecycle

Registrators and namespaces control setup and teardown (what is registered, when subscriptions exist). Middleware controls runtime interception (what happens during processing).

Example (minimal, conceptual; not code)

Imagine you want to ensure “no destructive commands run in a read-only environment”.

A guard middleware can:

  1. Check the stage (e.g., only enforce for execute-like operations).

  2. Check the message type/category (e.g., “DeleteUserCommand” or “DangerousExecutor”).

  3. Interrupt the pipeline with a cancellation reason.

Handlers remain clean and focused, and the policy is centralized.

Pitfalls / Troubleshooting

1) Putting domain logic into middleware

Symptom: system behavior becomes hard to reason about because business rules run “invisibly” before handlers. Fix: move business decisions to handlers, keep middleware for infrastructure policies and observability.

2) Overly broad middleware (canHandle always true)

Symptom: performance overhead, noisy logs, hard-to-debug cancellations. Fix: use narrow filtering rules; scope middleware to stages and message categories.

3) Order-dependent behavior without documentation

Symptom: swapping middleware registration order changes system behavior. Fix: document ordering expectations and keep critical policies early in the chain.

Best practices (short)

  • Keep middleware small and single-purpose.

  • Make filtering explicit: stage + message category/type checks.

  • Treat middleware as infrastructure: log, measure, enforce policy; avoid “doing work” that belongs to handlers.

  • Prefer transparency: when middleware interrupts, ensure the reason is diagnosable.

06 июля 2026