Cosmos.MultiTenancy
0.1.1
dotnet add package Cosmos.MultiTenancy --version 0.1.1
NuGet\Install-Package Cosmos.MultiTenancy -Version 0.1.1
<PackageReference Include="Cosmos.MultiTenancy" Version="0.1.1" />
<PackageVersion Include="Cosmos.MultiTenancy" Version="0.1.1" />
<PackageReference Include="Cosmos.MultiTenancy" />
paket add Cosmos.MultiTenancy --version 0.1.1
#r "nuget: Cosmos.MultiTenancy, 0.1.1"
#:package Cosmos.MultiTenancy@0.1.1
#addin nuget:?package=Cosmos.MultiTenancy&version=0.1.1
#tool nuget:?package=Cosmos.MultiTenancy&version=0.1.1
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
- Cosmos.EventSourcing.CritterStack: Usa
TenantResolverpara operaciones multi-tenant
Licencia
Copyright © Cosmos. Todos los derechos reservados.
| Product | Versions 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. |
-
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.