0031. Bus Ring Split¶
bus/ was the last top-level package outside adapters/ that held more than
one technology's implementation behind a shared interface -- exactly the
shape ADR 0019, ADR 0024, ADR 0025, ADR 0026, and ADR 0029 had already
resolved for stores, projections, outbox, and locks/read models. This ADR
moves EventBus onto the ports ring, moves the four backend implementations
(InMemoryEventBus, RedisEventBus, KafkaEventBus, RabbitMQEventBus)
and their collaborators onto the adapters ring, and deletes bus/ and its
facade __init__.py outright.
| Old import | New import | Ring |
|---|---|---|
eventsource.bus.interface.EventBus |
eventsource.ports.bus.EventBus |
ports |
eventsource.bus.base.BaseEventBus |
eventsource.adapters._bus.BaseEventBus |
adapters (internal) |
eventsource.bus.registry.SubscriptionRegistry |
eventsource.adapters._bus.SubscriptionRegistry |
adapters (internal) |
eventsource.bus.memory.InMemoryEventBus |
eventsource.adapters.memory.bus.InMemoryEventBus |
adapters |
eventsource.bus.redis.RedisEventBus |
eventsource.adapters.redis.bus.RedisEventBus |
adapters |
eventsource.bus.redis.RedisEventBusConfig |
eventsource.adapters.redis.bus.RedisEventBusConfig |
adapters |
eventsource.bus.kafka.* (KafkaEventBus + consumer/publisher/connection/config/dlq/metrics/models collaborators) |
eventsource.adapters.kafka.* |
adapters |
eventsource.bus.rabbitmq.* (RabbitMQEventBus + consumer/publisher/connection/config/dlq/topology/serialization/models/death_headers collaborators) |
eventsource.adapters.rabbitmq.* |
adapters |
REDIS_AVAILABLE, KAFKA_AVAILABLE, and RABBITMQ_AVAILABLE -- the guarded
optional-import flags -- are preserved verbatim in their new adapter
packages.
Status¶
Accepted. Implemented in src/eventsource/ports/bus.py,
src/eventsource/adapters/_bus/, src/eventsource/adapters/memory/bus.py,
src/eventsource/adapters/redis/, src/eventsource/adapters/kafka/,
src/eventsource/adapters/rabbitmq/. src/eventsource/bus/ -- including its
facade __init__.py -- is deleted outright. import eventsource.bus, and
every eventsource.bus.* submodule import, now raises ModuleNotFoundError.
No shim, no deprecation warning: the same pre-1.0, no-external-consumers
standing rule ADR 0025, ADR 0026, ADR 0029, and ADR 0030 already applied to
stores/, repositories/, locks/, readmodels/, and the other five
top-level modules applies here without qualification. Top-level
from eventsource import ... imports are unaffected -- the barrel
re-exports from the new homes directly and always did.
This same deletion completes the "Remove bus facade compat shims (P2)"
backlog item. That entry asked for the ~90 white-box test call sites that
reached through bus._connection_manager.*-style facade properties to be
migrated onto direct collaborator access ahead of a scheduled 0.8.0 shim
removal. Splitting the package onto the ring map retargets every one of
those call sites onto the new adapter-internal collaborator modules in the
same pass -- there is no facade left to shim, so the scheduled removal work
and this migration are the same commit, not two.
Amended by ADR 0031: ADR 0007,
ADR 0010,
ADR 0011, and
ADR 0020. All four
ADRs' Decisions stand untouched -- delivery semantics (0007), the uniform
EventBus contract (0010), no-ack error isolation (0011), and the
per-backend collaborator decomposition into ConnectionManager /
Publisher / Consumer / DLQAdmin / topology objects (0020) are exactly
as designed and exactly as tested. What changes is purely where the classes
those ADRs describe live: EventBus moves from eventsource.bus.interface
to eventsource.ports.bus, and the Rabbit/Kafka collaborator packages ADR
0020 introduced move from eventsource.bus.{rabbitmq,kafka} to
eventsource.adapters.{rabbitmq,kafka}. Each ADR's body is left as written;
this paragraph is the only pointer added to each.
Context¶
docs/core-surface.md and .claude/rules/architecture.md's ring map both
carried bus/ under interface adapters' transitional list -- "adapters/
(during transition also stores/, bus/ backend modules)" -- as the last
multi-backend package still sitting outside adapters/ after ADR 0025
retired stores/ and ADR 0029 closed out locks/ and readmodels/.
BACKLOG.md carried the move as a P2 "campaign residue" entry: "bus/
colocates the EventBus interface with InMemory, Redis, RabbitMQ, and Kafka
backends. Ring migration: EventBus port into ports/, backends into
adapters/<backend>/bus.py... Coordinate with the existing 'Remove bus
facade compat shims (P2)' entry."
EventBus is an output port in exactly the sense EventPublisher
(eventsource.ports.bus, moved there by the ports/value-object surface
work earlier in this cycle) already is: the use-case ring calls it, adapters
implement it. Leaving EventBus in a standalone bus/ package while
EventPublisher sat in ports/bus split one conceptual port -- publish plus
subscribe/deliver -- across two packages for no reason but history.
BaseEventBus (shared retry/backoff/handler-dispatch machinery) and
SubscriptionRegistry (the handler-routing table ADR 0013 introduced) are
not ports and not a single technology's adapter -- they are implementation
detail shared by every backend adapter, the same relationship
adapters/_sql/ already has to adapters/sql/, adapters/postgresql/, and
adapters/sqlite/. They move to eventsource.adapters._bus, an
underscore-internal adapters-ring package, rather than into any one
backend's directory.
Redis previously had no per-technology package the way PostgreSQL and
SQLite do for stores; bus.redis was the only Redis-shaped module in the
codebase. This ADR gives it one -- eventsource.adapters.redis/ -- so the
adapters ring's per-technology-directory convention (adapters/postgresql/,
adapters/sqlite/, adapters/kafka/, adapters/rabbitmq/, and now
adapters/redis/) is uniform across every backend, not four out of five.
Decision¶
EventBus(the ABC) moves fromeventsource.bus.interfaceintoeventsource.ports.bus, alongside theEventPublisherport already there. This is a pure relocation -- the ABC's contract, method signatures, and the guarantees ADR 0007/0010/0011 describe are unchanged.BaseEventBusandSubscriptionRegistrymove intoeventsource.adapters._bus, a new adapters-ring internal package (same pattern asadapters/_sql): shared collaborator code that every backend adapter composes, owned by no single technology.InMemoryEventBusmoves toeventsource.adapters.memory.bus, alongside the other in-memory adapters already underadapters/memory/.RedisEventBusandRedisEventBusConfigmove to a neweventsource.adapters.redispackage (adapters/redis/bus.py), Redis's first per-technology adapter directory.KafkaEventBusand every collaborator ADR 0020 introduced for it (consumer, publisher, connection, config, dlq, metrics, models) move toeventsource.adapters.kafka, unchanged in internal shape.RabbitMQEventBusand every collaborator ADR 0020 introduced for it (consumer, publisher, connection, config, dlq, topology, serialization, models, death_headers) move toeventsource.adapters.rabbitmq, unchanged in internal shape.REDIS_AVAILABLE,KAFKA_AVAILABLE, andRABBITMQ_AVAILABLE-- the guarded-import flags each backend exports so callers can check availability at runtime without importing the optional dependency -- move with their respective packages, unchanged in behavior.eventsource/bus/-- all of it, including the facade__init__.pythat re-exported all four backends and their collaborators from one module -- is deleted. No shim, no deprecation warning, no back-compat alias: the library is unreleased, the same standing rule ADR 0025/0026/0029/0030 already applied without qualification.- Top-level
from eventsource import ...re-exports (EventBus,InMemoryEventBus,RedisEventBus,KafkaEventBus,RabbitMQEventBus, and the rest of the twenty-name bus import surface) are unaffected -- the barrel resolves them from the new homes directly.
Alternatives Considered¶
- Keep
bus/as a facade overadapters/*re-exports: would preserve the ~90 white-box test call sites unchanged, but recreates exactly the shim-maintenance cost (a second source of truth for where a name lives, plus the guarded-import fallback machinery duplicated at the facade layer) that ADR 0025/0026/0029/0030 already rejected for every other retired package. Rejected. - One
adapters/bus/package holding all four backends together: keeps a bus-shaped grouping but violates the adapters-ring convention every other multi-backend concern already follows (adapters/memory/,adapters/postgresql/,adapters/sqlite/,adapters/kafka/,adapters/rabbitmq/are per-technology, not per-concern). Rejected. - Give
BaseEventBus/SubscriptionRegistrytheir own top-levelbus_shared/or similar: adds a seventh transitional-looking package for what is, in every other ring, adapter-internal shared code (adapters/_sqlis the direct precedent). Rejected in favor ofadapters/_bus.
Consequences¶
bus/no longer exists as a ring exception; the adapters-ring transitional list in.claude/rules/architecture.mddrops its last "bus/backend modules" phrase.- The "Remove bus facade compat shims (P2)" backlog entry is resolved by this deletion rather than by a separate 0.8.0 migration -- see Status.
- The "Migrate bus/ interface and backends to ports/adapters (P2)" backlog entry is resolved by this ADR.
- Redis gains a per-technology adapter directory
(
eventsource.adapters.redis) for the first time, closing the last backend-directory asymmetry between Redis and the other four backends. - Delivery, retry, and error-isolation semantics (ADR 0007, ADR 0010, ADR 0011) and the collaborator decomposition (ADR 0020) are unchanged; the conformance suite passes unmodified against the relocated adapters.