Skip to content

ADR-002: Dispatcher, router, and observer split

  • Status: Accepted
  • Owners: maintainers

Context

The desired developer experience resembles aiogram, but Google Chat event semantics and response modes differ from Telegram. Registration, matching, middleware, dependency injection, propagation, and application lifecycle need separate responsibilities.

Decision

Router owns named event observers and composes child routers. An observer owns ordered handlers for one event category. Dispatcher is the root router and dispatch entry point. Each handler has filters and flags; middleware wraps a resolved handler plan. Dependency injection resolves explicit event/context data by annotation/name and reports ambiguity. The default propagation rule is first successful matching handler, with any fan-out behavior requiring an explicit policy.

Router ownership is a single-parent acyclic tree traversed pre-order and depth-first. Event selection uses a global specific-observer pass followed by a generic event fallback. SkipHandler continues with the next candidate and StopPropagation ends search; both are dedicated non-error control flow. One post-filter middleware layer wraps invocation, with parent middleware wrapping descendant middleware. Error observers receive an ErrorEvent once and do not recurse.

feed_update() accepts framework domain events. Lifecycle and transport startup remain outside dispatch.

Consequences

Nested feature routers stay reusable, dispatch is transport-neutral, and tests can exercise routing without Google credentials. Attached router instances cannot be shared across parents. Ordered matching and inclusion order are observable and therefore covered by compatibility tests.

Alternatives considered

  • One global handler registry: simpler initially, poor modular composition.
  • Reuse an HTTP router: route paths do not model event/filter semantics.
  • Broadcast to every matching handler: convenient for event buses but unsafe as a bot default because it can duplicate side effects and responses.

Sources

aiogram Dispatcher, routers and observers, middleware.