Skip to content

Connecting Runtimes

Your contexts are separate, but they need to talk. Deepstaging gives you two event systems — one for "something happened inside this context" and one for "the outside world needs to know."

EventQueue vs IntegrationEvents

EventQueue IntegrationEvents
Scope Within one context Across contexts
Transport In-process channel In-process (dev) or Service Bus (prod)
Durability Lost on process restart Durable (with real transport)
Handler discovery Same assembly Any subscribing assembly
Use when Reacting to internal state changes Notifying other contexts

In the Library example: LendingDomainEvents (EventQueue) handles patron loan count updates. LendingIntegrationEvents (IntegrationEvents) tells Catalog about inventory changes. Same BorrowBook command publishes to both.

Topic Classes

Integration events live in a shared Contracts project. Each publishing context gets one topic class:

Library.Contracts/LendingEvents.cs
[IntegrationEventTopic]
public sealed class LendingEvents
{
    [IntegrationEvent("lending.book-borrowed")]
    public sealed record BookBorrowed(BookId BookId, PatronId PatronId);

    [IntegrationEvent("lending.book-returned")]
    public sealed record BookReturned(BookId BookId, PatronId PatronId);
}

The string in [IntegrationEvent("...")] is the wire identity — it's what gets serialized on the transport. Pick a naming convention (context.event-name) and stick with it. Changing this string is a breaking change for any subscriber.

Publishing

The publishing context declares [IntegrationEvents(typeof(LendingEvents))] and calls Enqueue:

>> LendingIntegrationEvents.Enqueue<LendingRuntime>(new LendingEvents.BookBorrowed(bookId, patronId))

Subscribing

The subscribing context also declares [IntegrationEvents(typeof(LendingEvents))] and adds a handler class:

[IntegrationEventHandler<CatalogRuntime>(typeof(LendingEvents))]
public static partial class CatalogLendingEventHandlers
{
    public static Eff<CatalogRuntime, Unit> Handle(LendingEvents.BookBorrowed evt) =>
        CatalogDispatch.UpdateInventory(evt.BookId, -1).AsUnit();
}

The handler delegates to the dispatch layer — it doesn't touch the store directly. Same code path, same validation, same effects pipeline.

Common Mistakes

  • Forgetting [IntegrationEvent("...")] on the event record. Without the string identity, the event can't be serialized or routed.
  • Putting handlers in the wrong assembly. The handler class must be in the subscribing context's project, not the publisher's.
  • Sharing command types across contexts. Commands are internal to a context. If Catalog needs to react to a Lending event, use an integration event handler — don't import Lending's command types.