Cosmos.MultiTenancy 0.1.1

dotnet add package Cosmos.MultiTenancy --version 0.1.1
                    
NuGet\Install-Package Cosmos.MultiTenancy -Version 0.1.1
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Cosmos.MultiTenancy" Version="0.1.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Cosmos.MultiTenancy" Version="0.1.1" />
                    
Directory.Packages.props
<PackageReference Include="Cosmos.MultiTenancy" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Cosmos.MultiTenancy --version 0.1.1
                    
#r "nuget: Cosmos.MultiTenancy, 0.1.1"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Cosmos.MultiTenancy@0.1.1
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Cosmos.MultiTenancy&version=0.1.1
                    
Install as a Cake Addin
#tool nuget:?package=Cosmos.MultiTenancy&version=0.1.1
                    
Install as a Cake Tool

Cosmos.MultiTenancy

Soporte ligero para multi-tenancy en aplicaciones distribuidas .NET 10.

Descripción

Este paquete proporciona una implementación simple de resolución de tenants para aplicaciones multi-tenant. El TenantResolver permite identificar y trabajar con diferentes tenants en sistemas que requieren aislamiento de datos o comportamiento específico por tenant.

Características

  • TenantResolver: Clase simple para resolver el tenant actual
  • Sin dependencias externas
  • Fácil integración con Wolverine y otros frameworks
  • Diseñado para ser extendido según las necesidades de tu aplicación

Instalación

dotnet add package Cosmos.MultiTenancy

Este paquete se incluye automáticamente al instalar Cosmos.EventSourcing.CritterStack.

Uso Básico

Registrar el TenantResolver

var builder = WebApplication.CreateBuilder(args);

// Registrar como singleton o scoped según tu caso de uso
builder.Services.AddScoped<TenantResolver>();

var app = builder.Build();

Usar el TenantResolver

using Cosmos.MultiTenancy;

public class OrderService
{
    private readonly TenantResolver _tenantResolver;
    private readonly ICommandRouter _commandRouter;

    public OrderService(TenantResolver tenantResolver, ICommandRouter commandRouter)
    {
        _tenantResolver = tenantResolver;
        _commandRouter = commandRouter;
    }

    public async Task CreateOrderAsync(CreateOrderCommand command)
    {
        // El tenant ID está disponible a través del resolver
        var tenantId = _tenantResolver.TenantId;

        Console.WriteLine($"Creando orden para tenant: {tenantId}");

        await _commandRouter.InvokeAsync(command, CancellationToken.None);
    }
}

Integración con Wolverine

El paquete Cosmos.EventSourcing.CritterStack utiliza automáticamente el TenantResolver para ejecutar comandos y queries en el contexto del tenant correcto:

public class WolverineCommandRouter : ICommandRouter
{
    private readonly IMessageBus _messageBus;
    private readonly TenantResolver _tenantResolver;

    public WolverineCommandRouter(IMessageBus messageBus, TenantResolver tenantResolver)
    {
        _messageBus = messageBus;
        _tenantResolver = tenantResolver;
    }

    public Task InvokeAsync<TCommand>(TCommand command, CancellationToken ct)
        where TCommand : class
    {
        // Usa el TenantId del resolver automáticamente
        return _messageBus.InvokeForTenantAsync(_tenantResolver.TenantId, command, ct);
    }
}

Personalización

La implementación actual proporciona un tenant ID fijo ("CosmosTenant"). En una aplicación real, querrás extender o reemplazar esta implementación para resolver el tenant de manera dinámica.

Resolver Tenant desde HTTP Header (headers confiables de gateway)

Para servicios ASP.NET Core que reciben headers confiables de un gateway, usa el paquete Cosmos.MultiTenancy.AspNetCore, que provee TrustedHeadersTenantResolver listo para usar:

// Registrar después de AgregarWolverineCommandRouter/QueryRouter
builder.Services.AgregarTenantResolverConHeadersConfiables();

// Opcionalmente, cambiar el nombre del header (default: "X-Tenant-Id")
builder.Services.AgregarTenantResolverConHeadersConfiables(options =>
{
    options.TenantIdHeaderName = "X-Custom-Tenant";
});

Ejemplo: Resolver Tenant desde JWT Claims

public class JwtTenantResolver : TenantResolver
{
    private readonly IHttpContextAccessor _httpContextAccessor;

    public JwtTenantResolver(IHttpContextAccessor httpContextAccessor)
    {
        _httpContextAccessor = httpContextAccessor;
    }

    public new string TenantId
    {
        get
        {
            var httpContext = _httpContextAccessor.HttpContext;
            var tenantClaim = httpContext?.User?.FindFirst("tenant_id");

            return tenantClaim?.Value ?? "default";
        }
    }
}

Registrar una Implementación Personalizada

// En lugar de TenantResolver, registra tu implementación
builder.Services.AddScoped<TenantResolver, HttpTenantResolver>();

// O usando una interfaz personalizada
builder.Services.AddScoped<ITenantResolver, JwtTenantResolver>();

Casos de Uso

Multi-Tenancy en Event Sourcing

public class TenantAwareEventStore
{
    private readonly IEventStore _eventStore;
    private readonly TenantResolver _tenantResolver;

    public TenantAwareEventStore(IEventStore eventStore, TenantResolver tenantResolver)
    {
        _eventStore = eventStore;
        _tenantResolver = tenantResolver;
    }

    public async Task<TAggregate?> LoadAsync<TAggregate>(string id, CancellationToken ct)
        where TAggregate : AggregateRoot, new()
    {
        var tenantId = _tenantResolver.TenantId;

        // Prefijo del tenant al ID del agregado
        var tenantScopedId = $"{tenantId}:{id}";

        return await _eventStore.GetAggregateRootAsync<TAggregate>(tenantScopedId, ct);
    }
}

Logging con Contexto de Tenant

public class TenantAwareLogger
{
    private readonly ILogger _logger;
    private readonly TenantResolver _tenantResolver;

    public TenantAwareLogger(ILogger<TenantAwareLogger> logger, TenantResolver tenantResolver)
    {
        _logger = logger;
        _tenantResolver = tenantResolver;
    }

    public void LogInfo(string message)
    {
        _logger.LogInformation(
            "[Tenant: {TenantId}] {Message}",
            _tenantResolver.TenantId,
            message
        );
    }
}

Consideraciones de Diseño

Aislamiento de Datos

El TenantResolver es solo una herramienta para identificar el tenant. El aislamiento real de datos debe implementarse en capas superiores:

  • Base de datos por tenant: Cada tenant tiene su propia base de datos
  • Schema por tenant: Cada tenant tiene su propio schema en una base de datos compartida
  • Discriminador por tenant: Todos los datos comparten tablas pero con un campo tenant_id

Thread Safety

Si usas TenantResolver en escenarios asíncronos, asegúrate de que el scope del servicio sea apropiado:

  • Scoped: Para aplicaciones web (recomendado)
  • Transient: Para cada resolución independiente
  • Singleton: Solo si el tenant es verdaderamente global

Integración con Marten

builder.Services.AddMarten(options =>
{
    options.Connection(connectionString);

    // Usar políticas de multi-tenancy de Marten
    options.Policies.AllDocumentsAreMultiTenanted();
});

// El TenantResolver puede usarse con Marten's tenant ID
builder.Services.AddScoped<IDocumentSession>(sp =>
{
    var store = sp.GetRequiredService<IDocumentStore>();
    var tenantResolver = sp.GetRequiredService<TenantResolver>();

    return store.OpenSession(tenantResolver.TenantId);
});

Requisitos

  • .NET 10.0 o superior

Dependencias

Sin dependencias externas.

Paquetes Relacionados

Licencia

Copyright © Cosmos. Todos los derechos reservados.

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net10.0

    • No dependencies.

NuGet packages (3)

Showing the top 3 NuGet packages that depend on Cosmos.MultiTenancy:

Package Downloads
Cosmos.EventSourcing.CritterStack

Implementaciones de Wolverine y Marten para EDA y event sourcing

Cosmos.EventDriven.CritterStack

Implementaciones de Wolverine para IPublicEventSender e IPrivateEventSender en Cosmos EDA

Cosmos.MultiTenancy.AspNetCore

ITenantResolver basado en headers HTTP confiables para servicios ASP.NET Core en Cosmos.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.1.1 19 5/20/2026
0.1.0 21 5/20/2026