Skip to content

Growing Your Application

You started with one runtime. Then you added a second. At some point you'll wonder: when do I add a third? And when should I just put everything in one runtime?

When to Split

There's no formula, but some reliable signals:

  • The data has different lifetimes. Books and loans are different things. Deleting a patron shouldn't cascade-delete inventory records. If the data wouldn't share a foreign key, it probably shouldn't share a store.
  • The language changes. "Available copies" means something in Catalog. In Lending, it's "loan count." When the same word means different things, you're crossing a boundary.
  • The teams would be different. If one group owns the catalog and another manages lending policies, they'll want to deploy independently.
  • The runtime constructor is getting crowded. If you're passing 15 capabilities into one runtime, it's doing too much.

None of these are hard rules. Start with one runtime. Split when it gets uncomfortable.

The Invariant

Every context follows the same shape:

[Runtime]           — one per context
[DataStore]         — one per context, owns all entities
[DispatchModule]    — one per context, owns all commands and queries
[RestApi]           — optional, one per context

That's it. Add [EventQueue] if you have domain events. Add [IntegrationEvents] if you publish or subscribe to cross-context events. Add [Uses(typeof(JobsModule))] if you need background jobs.

Auto-Discovery vs [Uses]

Modules in the same assembly as the [Runtime] are discovered automatically. You never need to reference them explicitly — the generator finds [DataStore], [DispatchModule], [EventQueue], [RestApi], and [EffectsModule] in the same project and wires them into the runtime.

[Uses] is for modules in other assemblies:

[Runtime]
[Uses(typeof(JobsModule))]  // JobsModule is in the Deepstaging.Runtime package
public partial class LendingRuntime;

If a module is in a NuGet package or a different project, use [Uses]. If it's in the same project, don't bother.

Common Mistakes

Sharing a DataStore across runtimes. Each runtime should own its data. If two runtimes need the same entity, they're probably the same context — or they need integration events to stay in sync.

One mega-runtime. If your runtime has 30 capabilities, 50 command handlers, and 200 tests, it's time to split. A runtime should be a cohesive unit — not a dumping ground.

Referencing contexts directly. Library.Lending should never have a <ProjectReference> to Library.Catalog. They communicate through events in Library.Contracts. If you're tempted to share code, put it in Contracts.