High-performance, source-generator powered mediator / messaging framework for .NET. Eliminates runtime reflection, minimizes boilerplate, and provides an extensible module + pipeline model for cross-cutting concerns (e.g., caching, auditing).
| CI | Branch | Status |
|---|---|---|
| Prod (Stable Publish) | master |
|
| Dev (Preview Publish) | dev |
|
| Validation (PR / Feature) | PRs -> dev / master |
| Package | Stable | Preview | Downloads | Description |
|---|---|---|---|---|
| Space.Abstraction | Core abstractions + Source Generator analyzer (attributes, contexts, contracts) | |||
| Space.DependencyInjection | DI extensions & runtime implementations (ISpace) | |||
| Space.Modules.InMemoryCache | In-memory caching module + attribute integration |
Old behavior: Space.DependencyInjection implicitly brought abstractions + source generator.
New behavior: Source generator analyzer ships with Space.Abstraction. You must reference BOTH packages for full DI usage.
Migration:
- Add
Space.Abstractionto all projects that use Space attributes. - Add
Space.DependencyInjectiononly where you needISpaceand registration extensions. - Remove any direct
Space.SourceGeneratorreferences; they are now unnecessary.
dotnet add package Space.Abstraction
dotnet add package Space.DependencyInjectionOptional module:
dotnet add package Space.Modules.InMemoryCacheIf you only need compile-time generation (e.g., custom runtime) reference just Space.Abstraction.
using Microsoft.Extensions.DependencyInjection;
using Space.Abstraction;
using Space.Abstraction.Attributes;
using Space.Abstraction.Context;
using Space.Abstraction.Contracts;
var services = new ServiceCollection();
services.AddSpace(opt =>
{
opt.NotificationDispatchType = NotificationDispatchType.Parallel; // or Sequential
});
services.AddSpaceInMemoryCache(); // if caching module needed
var provider = services.BuildServiceProvider();
var space = provider.GetRequiredService<ISpace>();
public sealed record UserLoginRequest(string UserName) : IRequest<UserLoginResponse>;
public sealed record UserLoginResponse(bool Success);
public class UserHandlers
{
[Handle]
public ValueTask<UserLoginResponse> Login(HandlerContext<UserLoginRequest> ctx)
=> ValueTask.FromResult(new UserLoginResponse(true));
}
// Interface-based (optional, for method signature hint only - [Handle] attribute is still required)
public class UserHandlersInterface : IHandler<UserLoginRequest, UserLoginResponse>
{
[Handle]
public ValueTask<UserLoginResponse> Handle(HandlerContext<UserLoginRequest> ctx)
=> ValueTask.FromResult(new UserLoginResponse(true));
}
var response1 = await space.Send<UserLoginResponse>(new UserLoginRequest("demo"));
var response2 = await space.Send<UserLoginRequest, UserLoginResponse>(new UserLoginRequest("demo"));public class PricingHandlers
{
[Handle(Name = "Default")] public ValueTask<PriceResult> GetDefault(HandlerContext<PriceQuery> ctx) => ...;
[Handle(Name = "Discounted")] public ValueTask<PriceResult> GetDiscounted(HandlerContext<PriceQuery> ctx) => ...;
}
var discounted = await space.Send<PriceResult>(new PriceQuery(...), name: "Discounted");public class LoggingPipeline
{
[Pipeline(Order = 100)]
public async ValueTask<UserLoginResponse> Log(PipelineContext<UserLoginRequest> ctx, PipelineDelegate<UserLoginRequest, UserLoginResponse> next)
{
var result = await next(ctx);
return result;
}
}public sealed record UserLoggedIn(string UserName);
public class LoginNotifications
{
[Notification]
public ValueTask Log(NotificationContext<UserLoggedIn> ctx) => ValueTask.CompletedTask;
}
await space.Publish(new UserLoggedIn("demo"));public class UserQueries
{
[Handle]
[CacheModule(Duration = 60)]
public ValueTask<UserProfile?> GetUser(HandlerContext<UserId> ctx) => ...;
}[CacheModule] (from Space.Modules.InMemoryCache) inserts caching logic before user pipelines.
Space supports handlers, pipelines, notifications, and modules across multiple projects through a root aggregator model. This enables modular solutions where feature libraries can contain their own handlers without manual DI wiring.
Set exactly one project as the root aggregator (typically your host/composition root):
<PropertyGroup>
<SpaceGenerateRootAggregator>true</SpaceGenerateRootAggregator>
</PropertyGroup>All other handler libraries should either omit this property or set it to false:
<PropertyGroup>
<SpaceGenerateRootAggregator>false</SpaceGenerateRootAggregator>
</PropertyGroup>- Root project generates
DependencyInjectionExtensions.g.cswith automatic assembly discovery - Satellite libraries generate lightweight
SpaceAssemblyRegistration_<Assembly>.g.csfiles - At runtime, the root automatically discovers and registers handlers from all referenced assemblies
/MySolution
src/AppHost/ (root: SpaceGenerateRootAggregator=true)
src/Features/Users/ (satellite: handlers, pipelines)
src/Features/Billing/ (satellite: handlers, modules)
src/Infrastructure/ (satellite: notifications)
For complete details, migration guidance, and troubleshooting, see MultiProjectSetup.md.
- Zero runtime reflection for discovery (Roslyn source generator)
- Minimal boilerplate: annotate methods directly with
[Handle],[Pipeline],[Notification] - Named handlers (multiple strategies for same request/response)
- Orderable pipelines + early system module execution
- Extensible module model (e.g., cache) before user pipelines
- High-performance async signatures (
ValueTask) - Parallel or sequential notification dispatch
- Multi-project root aggregator property
<SpaceGenerateRootAggregator> - Multi-targeting (netstandard2.0 + modern .NET)
Space front-loads cost at build time to reduce runtime overhead:
- Compile-time metadata (registrations, maps)
- No reflection-based runtime scanning
- Low allocation pathways (current & planned pooling)
Benchmarks compare against other mediator libraries in tests/Space.Benchmarks.
Primary docs in docs/:
| Topic | Link |
|---|---|
| Project Overview | ProjectDoc.en.md |
| Handlers | Handlers |
| Pipelines | Pipelines |
| Notifications | Notifications |
| Modules | Modules |
| Multi-Project Setup | MultiProjectSetup |
| Developer Recommendations | DeveloperRecommendations |
| Known Issues | KnownIssues |
| Planned Improvements | PlannedImprovements |
Per-package:
See GitHub Issues for:
- Planned improvements (attribute parameters, global defaults, Options pattern)
- Known issues (initial Lazy
ISpacenull, module scoping bugs)
Contributions welcome via issues & PRs.
master: tagged semantic versions (vX.Y.Z) ? stable NuGetdev: preview releases (X.Y.Z-preview)- Feature branches: validation build only
MIT.
APIs may evolve while early preview features stabilize. Track releases for changes.
Space is a high-performance, source-generator powered mediator/messaging framework for .NET.
- Docs: see the
docs/folder - Contribution guide for modules: see docs/Contribution.md
- Handlers: docs/Handlers.md
- Pipelines: docs/Pipelines.md
- Notifications: docs/Notifications.md
- Known Issues: docs/KnownIssues.md
- Developer Recommendations: docs/DeveloperRecommendations.md
See .github/copilot-instructions.md for environment and common commands.