Skip to content

GH-4507: declare a module's ancillary store once per namespace - #4508

Merged
jeremydmiller merged 1 commit into
JasperFx:mainfrom
uniquelau:lg/ancillary-storage-by-namespace
Sep 22, 2026
Merged

jeremydmiller merged 1 commit into
JasperFx:mainfrom
uniquelau:lg/ancillary-storage-by-namespace

Conversation

@uniquelau

Copy link
Copy Markdown
Contributor

Closes #4507

TL;DR

Adds a namespace-scoped sibling of #4477's assembly policy, for modular monoliths that keep several modules in one assembly. Additive only: no existing code changes.

opts.Policies.UseAncillaryStorageFromNamespaceContaining<OrdersModule>(typeof(IOrdersStore));

What changed

File Change
Persistence/AncillaryStorageByNamespacePolicy.cs New. Sibling of AncillaryStorageByAssemblyPolicy: same skip rule, same UseAncillaryStorage call, same gRPC reach. Only the match differs.
IPolicies.cs, WolverineOptions.Policies.cs Two members next to the assembly pair, named after MessagesFromNamespace / MessagesFromNamespaceContaining<T>.
3 test files CoreTests (routing rules), MartenTests (end to end), Grpc.Tests (reach).
4 docs pages A short "or by namespace" addition beside each assembly section.

Behaviour

Case Result
Handler in Orders ✅ routed
Handler in Orders.Slices (child) ✅ routed
Handler in OrdersArchive (shares a prefix) ❌ left alone (dot boundary)
Handler in Shipping (sibling) ❌ left alone
Two namespace policies in one assembly ✅ each module gets its own store
Explicit [Storage] on the handler attribute wins, and the policy adds no frame

Matching uses the namespace of the handler / endpoint / gRPC service type, not the message.

Review guide (about 5 min)

  1. AncillaryStorageByNamespacePolicy.cs: diff it mentally against the assembly policy. isInNamespace is the only new logic.
  2. IPolicies.cs: the public API and XML docs.
  3. ancillary_storage_by_namespace_policy.cs: the table above, as tests.

Worth a look

  • IPolicies gains two members. WolverineOptions is its only implementer in the repo, and GH-4477: declare a module's ancillary store once per assembly #4477 extended it the same way.
  • Version badge. The docs additions sit under the existing 6.39 badge headings. Adjust if you want a badge for the release this ships in.
  • Mixed-module chains. A combined chain whose handlers span two scoped namespaces gets the first matching policy's store. That's the same rule the assembly policy already has, so nothing new is introduced.
Test runs
Suite Result
CoreTests ~ancillary_storage 14/14 on net9.0 and net10.0 (6 existing + 8 new)
MartenTests ~ancillary_storage_by 3/3 on net10.0 (2 existing + 1 new)
Wolverine.Grpc.Tests ~ancillary_storage 5/5 on net9.0 (4 existing + 1 new)

The Marten docs snippet block is written in mdsnippets' output format (region L57–L64), so a regeneration should produce no diff.

🤖 Generated with Claude Code

UseAncillaryStorageFromAssembly (JasperFx#4477) assumes one module per assembly. Modular monoliths that start
as one project, with a namespace per module so the modules can be split into assemblies later, can't
use it: every module in the assembly would get the same store.

    opts.Policies.UseAncillaryStorageFromNamespaceContaining<OrdersModule>(typeof(IOrdersStore));

Purely additive. AncillaryStorageByNamespacePolicy is a sibling of AncillaryStorageByAssemblyPolicy
with the same precedence (an explicit [Storage] or an earlier policy wins) and the same reach
(handlers, HTTP endpoints, and gRPC via IChainSourceType). Only the match differs: the handler or
endpoint type's namespace, including child namespaces, with a dot boundary so "Orders" does not
match "OrdersArchive". Named to match MessagesFromNamespace / MessagesFromNamespaceContaining<T>.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings September 21, 2026 10:00

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟢 Approval recommended

No unresolved review issues were identified.

Review effort: Lite
Findings: None

What changed in this PR

Adds namespace-scoped ancillary storage policies for modular monoliths sharing an assembly.

Changes:

  • Adds namespace-based policy APIs and implementation.
  • Adds Core, Marten, and gRPC test coverage.
  • Updates ancillary storage documentation.
File Description
src/​Wolverine/​WolverineOptions.Policies.cs Registers namespace policies.
src/​Wolverine/​Persistence/​AncillaryStorageByNamespacePolicy.cs Implements namespace matching.
src/​Wolverine/​IPolicies.cs Defines public policy APIs.
src/​Wolverine.Grpc.Tests/​ancillary_storage_by_namespace_reaches_grpc.cs Tests gRPC routing.
src/​Testing/​CoreTests/​Persistence/​ancillary_storage_by_namespace_policy.cs Tests routing behavior and boundaries.
src/​Persistence/​MartenTests/​AncillaryStores/​ancillary_storage_by_namespace.cs Adds Marten coverage.
docs/​tutorials/​modular-monolith.md Documents namespace-scoped usage.
docs/​guide/​durability/​polecat/​ancillary-stores.md Documents Polecat support.
docs/​guide/​durability/​marten/​ancillary-stores.md Documents Marten support.
docs/​guide/​durability/​fisher/​index.md Documents Fisher support.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Declare a module's ancillary store once per namespace, for modules that share an assembly

3 participants