Modular Ops
Modules let you define a command set once and mount it at multiple points in your command graph — DRY at the routing level.
Defining a module
Section titled “Defining a module”A module is a method (or class) that registers commands onto an IReplMap:
void RegisterCrudCommands<T>(IReplMap map, IStore<T> store) where T : IEntity{ map.Map("list", () => store.All()); map.Map("show {id:int}", (int id) => store.Get(id)); map.Map("delete {id:int}", (int id) => { store.Delete(id); return Results.Success($"Deleted {id}."); });}Mounting the module
Section titled “Mounting the module”app.Context("contacts", contacts => RegisterCrudCommands(contacts, contactStore));
app.Context("products", products => RegisterCrudCommands(products, productStore));Both contacts and products now expose the same list, show, and delete commands against their respective stores.
With DI and open generics
Section titled “With DI and open generics”Register a generic store and let the DI container resolve per-type:
builder.Services.AddSingleton(typeof(IStore<>), typeof(InMemoryStore<>));app.Context("contacts", contacts =>{ contacts.Map("list", static (IStore<Contact> store) => store.All()); contacts.Map("show {id:int}", static (int id, IStore<Contact> store) => store.Get(id));});Conditional modules
Section titled “Conditional modules”Mount a module and hide it from automation surfaces:
app.Context("admin", admin =>{ admin.Map("purge-all", static (IStore<Contact> store) => store.DeleteAll());}).AutomationHidden();Reusing the same module under multiple contexts
Section titled “Reusing the same module under multiple contexts”void RegisterAuditLog(IReplMap map, string entityType){ map.Map("log", (IAuditLog log) => log.GetEntries(entityType)); map.Map("log clear", (IAuditLog log) => { log.Clear(entityType); return Results.Success("Log cleared."); });}
app.Context("contacts", contacts =>{ RegisterCrudCommands(contacts, contactStore); RegisterAuditLog(contacts, "contact");});
app.Context("products", products =>{ RegisterCrudCommands(products, productStore); RegisterAuditLog(products, "product");});This composes cleanly: each context has both CRUD and audit commands without copy-pasting routes.
Further reading
Section titled “Further reading”- Modules — complete
MapModuleAPI: DI-resolved generic form, instance form, conditional and DI-injected predicates, dynamic context routes. - Dependency Injection — service lifetimes and how services are resolved inside module constructors and handlers.
- Best Practices — when to extract a module and how to keep the command graph maintainable.