Buses (CQRS / events)
Recover precise routing through a command, query or event mediator.
A CQRS or event-driven app dispatches through a mediator: a command/query/ event bus stores handlers in a map and looks them up by name at runtime.
bus.Register(CreateOrder{}.CommandName(), NewCreateOrderHandler(...))
bus.Dispatch(ctx, CreateOrder{ID: id}) // routed by name, at runtimeA static call graph can't follow that map. It sees Dispatch calling the
handler interface, so it fans every dispatch out to every handler — a star
that loses the real command → handler routing, and pollutes the graph with the
bus internals and marker methods.
Turn it on
av, _ := archview.New(archview.Options{ DetectBuses: true })archview then:
- Reads the registration sites (
Register,Subscribe, …) to learn which message type routes to which concrete handler, and which types are buses. - Stops the call graph at the bus so the over-approximated fan-out is
dropped (and marker methods like
CommandName()fall away as dead nodes). - Resolves each dispatch site back to its exact handler, drawing a precise
caller → handlerdispatch edge (amber).
Result
Without DetectBuses | With DetectBuses |
|---|---|
| every command → every handler | each command → its own handler |
| event → every subscriber | event → only its real subscribers |
| bus + marker methods clutter the graph | gone |
Event fan-out stays precise too: a handler that publishes OrderCreated links
only to the subscribers of that event, not to every event handler.
Detection keys off the idiomatic shape — a Register/Subscribe-style
method that binds a Msg{}.Name() key to a handler value, dispatched on the
same bus type. Buses built purely on reflection or func(any) closures, with
no typed registration, remain invisible.
See the CQRS example.