diff --git a/docs/events/projections/ioc.md b/docs/events/projections/ioc.md
index 0a27077811..9cada2c79b 100644
--- a/docs/events/projections/ioc.md
+++ b/docs/events/projections/ioc.md
@@ -88,3 +88,100 @@ projection object once and add it to Marten's configuration at application start
registration is `Scoped` or `Transient`, Marten uses a proxy wrapper around `IProjection` that builds
the projection object uniquely for each usage through scoped containers, and disposes the inner projection
object at the end of the operation.
+
+## Registering DI-aware projections through IConfigureMarten modules
+
+If you organize your application's Marten configuration into [modules backed by `IConfigureMarten`](/configuration/hostbuilder#composite-configuration-with-configuremarten)
+and one of those modules needs to register a projection (or subscription) that depends on IoC
+services, the answer is *not* a special "module-aware" overload of `AddProjectionWithServices` —
+it's the constructor of your `IConfigureMarten` itself. The IoC container resolves the
+`IConfigureMarten` implementation, so any service it needs can be declared as a constructor
+dependency, and then handed to a projection that you build explicitly inside `Configure`.
+
+Using the same `ProductProjection` from above (which needs an `IPriceLookup`):
+
+
+
+```cs
+// An IConfigureMarten that takes its own dependencies through the constructor and
+// uses them to build a service-aware projection. This is the standard path for
+// modular Marten configuration that needs DI services to register a projection or
+// subscription -- the IoC container resolves IPriceLookup when it constructs the
+// IConfigureMarten implementation, and the resolved instance is then passed to the
+// projection at registration time.
+internal class ProductProjectionRegistration: IConfigureMarten
+{
+ private readonly IPriceLookup _lookup;
+
+ public ProductProjectionRegistration(IPriceLookup lookup)
+ {
+ _lookup = lookup;
+ }
+
+ public void Configure(IServiceProvider services, StoreOptions options)
+ {
+ // Build the projection instance with the injected dependencies and
+ // register it on StoreOptions like any other projection
+ options.Projections.Add(new ProductProjection(_lookup), ProjectionLifecycle.Inline);
+ }
+}
+```
+
+
+The module's `IServiceCollection` extension wires both the dependency *and* the `IConfigureMarten`
+into DI. The host's `AddMarten(...)` call lives elsewhere — this module just contributes to it:
+
+
+
+```cs
+public static class ProductModuleExtensions
+{
+ ///
+ /// Module-style registration: the module owns the IPriceLookup service AND the
+ /// IConfigureMarten that uses it to register a service-aware projection. The
+ /// host's AddMarten(...) call lives elsewhere; this module just adds to it.
+ ///
+ public static IServiceCollection AddProductModule(this IServiceCollection services)
+ {
+ services.AddSingleton();
+
+ // The IConfigureMarten implementation will receive IPriceLookup through
+ // its constructor at IoC resolution time, then build the projection.
+ services.AddSingleton();
+
+ return services;
+ }
+}
+```
+
+
+The host then composes the modules:
+
+```cs
+services.AddMarten(opts =>
+{
+ opts.Connection(connectionString);
+}).ApplyAllDatabaseChangesOnStartup();
+
+services.AddProductModule();
+```
+
+::: tip
+This pattern works equally well for **subscriptions** — register an `IConfigureMarten` that takes the
+subscription's dependencies through its constructor, build the subscription instance inside
+`Configure`, and call `options.Events.Subscribe(...)`. The same constructor-injection trick lets a
+module wire any DI-resolved object into Marten's configuration without going through
+`AddProjectionWithServices`.
+:::
+
+The same shape applies if you have multiple `IDocumentStore` instances and need module-style
+registration against a specific store — implement `IConfigureMarten` instead of
+`IConfigureMarten` and register it the same way (`services.AddSingleton, …>()`).
+
+### When to use which
+
+| Need | Use |
+| --- | --- |
+| Compose Marten configuration from multiple modules, each contributing a service-aware projection | `IConfigureMarten` with constructor injection (above) |
+| Register a single service-aware projection inline against the main `AddMarten()` call | [`AddProjectionWithServices`](#projections-and-ioc-services) |
+| The projection's dependency itself has a `Scoped` lifetime that must be created per use | `AddProjectionWithServices(..., ServiceLifetime.Scoped)` so Marten builds a fresh projection (and dependency) per invocation |
diff --git a/src/ContainerScopedProjectionTests/projections_with_IoC_services.cs b/src/ContainerScopedProjectionTests/projections_with_IoC_services.cs
index 3ed41de1b5..9f23a313e3 100644
--- a/src/ContainerScopedProjectionTests/projections_with_IoC_services.cs
+++ b/src/ContainerScopedProjectionTests/projections_with_IoC_services.cs
@@ -411,6 +411,37 @@ public async Task use_multistream_projection_as_scoped_and_inline_on_martenStore
}
+ [Fact]
+ public async Task use_iconfiguremarten_module_to_register_di_aware_projection()
+ {
+ using var host = await Host.CreateDefaultBuilder()
+ .ConfigureServices(services =>
+ {
+ services.AddMarten(opts =>
+ {
+ opts.Connection(ConnectionSource.ConnectionString);
+ opts.DatabaseSchemaName = "ioc_module";
+ opts.ApplyChangesLockId = opts.ApplyChangesLockId + 30;
+ }).ApplyAllDatabaseChangesOnStartup();
+
+ // The Product module pulls in its own dependencies AND uses
+ // IConfigureMarten to register a projection that needs them.
+ services.AddProductModule();
+ })
+ .StartAsync();
+
+ var store = host.Services.GetRequiredService();
+ await using var session = store.LightweightSession();
+
+ var streamId = session.Events.StartStream(new ProductRegistered("Ankle Socks", "Socks")).Id;
+ await session.SaveChangesAsync();
+
+ var product = await session.LoadAsync(streamId);
+ product.ShouldNotBeNull();
+ product.Name.ShouldBe("Ankle Socks");
+ product.Price.ShouldBeGreaterThan(0);
+ }
+
[Fact]
public async Task use_multistream_projection_as_singleton_and_inline_on_martenStore()
{
@@ -536,6 +567,56 @@ public override Product Evolve(Product snapshot, Guid id, IEvent e)
#endregion
+#region sample_iconfiguremarten_with_di_projection
+
+// An IConfigureMarten that takes its own dependencies through the constructor and
+// uses them to build a service-aware projection. This is the standard path for
+// modular Marten configuration that needs DI services to register a projection or
+// subscription -- the IoC container resolves IPriceLookup when it constructs the
+// IConfigureMarten implementation, and the resolved instance is then passed to the
+// projection at registration time.
+internal class ProductProjectionRegistration: IConfigureMarten
+{
+ private readonly IPriceLookup _lookup;
+
+ public ProductProjectionRegistration(IPriceLookup lookup)
+ {
+ _lookup = lookup;
+ }
+
+ public void Configure(IServiceProvider services, StoreOptions options)
+ {
+ // Build the projection instance with the injected dependencies and
+ // register it on StoreOptions like any other projection
+ options.Projections.Add(new ProductProjection(_lookup), ProjectionLifecycle.Inline);
+ }
+}
+
+#endregion
+
+#region sample_addproductmodule_with_iconfiguremarten
+
+public static class ProductModuleExtensions
+{
+ ///
+ /// Module-style registration: the module owns the IPriceLookup service AND the
+ /// IConfigureMarten that uses it to register a service-aware projection. The
+ /// host's AddMarten(...) call lives elsewhere; this module just adds to it.
+ ///
+ public static IServiceCollection AddProductModule(this IServiceCollection services)
+ {
+ services.AddSingleton();
+
+ // The IConfigureMarten implementation will receive IPriceLookup through
+ // its constructor at IoC resolution time, then build the projection.
+ services.AddSingleton();
+
+ return services;
+ }
+}
+
+#endregion
+
public interface ICustomStore: IDocumentStore
{
}