Home / Guides / Topic Routing

Topic Routing

The Emissions.TopicRouter macro provides compile-time event-to-topic mapping. This is useful when adapters need to route events to different destinations based on their logical topic.

Defining a Router

defmodule MyApp.Topics do
  use Emissions.TopicRouter,
    mappings: %{
      orders: [:order_created, :order_updated, :order_shipped, :order_canceled],
      inventory: [:item_received, :item_adjusted, :item_moved, :item_transferred],
      users: [:user_created, :user_updated, :user_deactivated],
      shipping: [:parcel_created, :parcel_shipped, :shipment_created]
    }
end

API

topic_for_event/1

Returns the topic for a given event name, or nil if unmapped:

MyApp.Topics.topic_for_event(:order_created)
#=> :orders

MyApp.Topics.topic_for_event(:unknown_event)
#=> nil

topics/0

Returns all configured topic names:

MyApp.Topics.topics()
#=> [:orders, :inventory, :users, :shipping]

events_for_topic/1

Returns all events for a given topic:

MyApp.Topics.events_for_topic(:orders)
#=> [:order_created, :order_updated, :order_shipped, :order_canceled]

MyApp.Topics.events_for_topic(:nonexistent)
#=> []

Using with Adapters

Adapters can use the topic router to determine where to send events:

defmodule MyApp.KafkaAdapter do
  @behaviour Emissions.Adapter

  @impl true
  def init(opts) do
    {:ok, %{prefix: Keyword.get(opts, :prefix, "events")}}
  end

  @impl true
  def handle_events(events, state) do
    for event <- events do
      case MyApp.Topics.topic_for_event(event.name) do
        nil ->
          :skip

        topic ->
          kafka_topic = "#{state.prefix}.#{topic}"
          MyApp.Kafka.produce(kafka_topic, Jason.encode!(event.payload))
      end
    end

    {:ok, state}
  end
end

How It Works

The router is computed at compile time. The mappings are inverted into a flat %{event_name => topic} map stored as a module attribute. Lookups are O(1) Map.get/2 calls with zero runtime overhead.

Topic Routing is Optional

Topic routing is entirely opt-in. Adapters that don't need topic-based routing can ignore it entirely. The router is a standalone module with no coupling to the core pipeline.