Thanks to visit codestin.com
Credit goes to github.com

Skip to content

salihcantekin/Space

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Space

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).


Status & Packages

CI Branch Status
Prod (Stable Publish) master Prod CI
Dev (Preview Publish) dev Dev CI
Validation (PR / Feature) PRs -> dev / master Validation Build

NuGet Packages

Package Stable Preview Downloads Description
Space.Abstraction NuGet NuGet (pre) Downloads Core abstractions: attributes, contexts, contracts
Space.DependencyInjection NuGet NuGet (pre) Downloads DI extensions + source generator bootstrap
Space.Modules.InMemoryCache NuGet NuGet (pre) Downloads In-memory caching module + attribute integration

Install (Minimal)

dotnet add package Space.DependencyInjection (brings Space.Abstraction)

Optional module:

dotnet add package Space.Modules.InMemoryCache

Quick Start

using Microsoft.Extensions.DependencyInjection;

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);
/*
The response type can be defined using IRequest<T>.
*/
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));
}

 /*
 The same functionality can be implemented using the IHandler<UserLoginRequest,  UserLoginResponse> interface.
 The primary difference from the attribute-based approach is that the method name will be explicitly defined in the interface implementation.
*/

public class UserHandlers : IHandler<UserLoginRequest, UserLoginResponse>
{
    public ValueTask<UserLoginResponse> Handle  (UserLoginRequest request, CancellationToken  cancellationToken)
        => ValueTask.FromResult(new UserLoginResponse   (true));
}

var response = await space.Send<UserLoginResponse>(new UserLoginRequest("demo"));
/*
A more efficient solution can be achieved in the Send method by utilizing both the request and response types.
*/
var response = await space.Send<UserLoginRequest, UserLoginResponse>(new UserLoginRequest("demo"))

Named Handlers

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(...), handlerName: "Discounted");

Pipelines

public class LoggingPipeline
{
    [Pipeline(Order = 100)]
    public async ValueTask<TResponse> Log<TRequest, TResponse>(PipelineContext<TRequest> ctx)
    {
        // pre-execution 
        var result = await ctx.Next(ctx);
        // post-execution
        return result;
    }
}

Notifications

public sealed record UserLoggedIn(string UserName);
public class LoginNotifications
{
    [Notification]
    public ValueTask Log(NotificationContext<UserLoggedIn> ctx) => ValueTask.CompletedTask;
}
await space.Publish(new UserLoggedIn("demo"));

Caching Module Example

public class UserQueries
{
    [Handle]
    [CacheModule(DurationSeconds = 60)]
    public ValueTask<UserProfile?> GetUser(HandlerContext<UserId> ctx) => ...;
}

[CacheModule] (from Space.Modules.InMemoryCache) inserts caching logic before user pipelines.


Multi-Project Setup

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.

Configuration

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>

How It Works

  • Root project generates DependencyInjectionExtensions.g.cs with automatic assembly discovery
  • Satellite libraries generate lightweight SpaceAssemblyRegistration_<Assembly>.g.cs files
  • At runtime, the root automatically discovers and registers handlers from all referenced assemblies

Example Structure

/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.


Key Features

  • 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-targeting (netstandard2.0 + modern .NET)

Performance Philosophy

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 (planned) will compare against other mediator libraries.


Documentation

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:


Roadmap & Issues

See GitHub Issues for:

  • Planned improvements (attribute parameters, global defaults, Options pattern)
  • Known issues (initial Lazy ISpace null, module scoping on named handlers)

Contributions welcome via issues & PRs.


Versioning & Releases

  • master: tagged semantic versions (vX.Y.Z) ? stable NuGet
  • dev: continuous preview (X.Y.(Patch+1)-preview.<run>)
  • Feature branches: validation build only

License

MIT.


Disclaimer

APIs may evolve while early preview features stabilize. Track releases for changes.

About

High-performance, source-generator powered mediator and messaging framework for .NET.

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors 7

Languages