Skip to content

Effects Composition

The Tenancy module integrates with the effects system through CorrelationContext. While there is no standalone [EffectsModule] for tenancy, the tenant ID flows automatically through all effect pipelines via the ambient context.

How Tenant Flows Through Effects

Every Eff<RT, T> pipeline inherits the current CorrelationContext, which includes TenantId. This means:

  1. TenantMiddleware sets CorrelationContext.Current.TenantId at the start of the request
  2. All downstream effect methods see the tenant automatically
  3. Outbound HTTP calls propagate ds-tenant-id header via CorrelationDelegatingHandler

Reading Tenant in Effects

Access the current tenant through CorrelationContext:

var tenantScopedOperation =
    from tenant in liftEff<AppRuntime, string?>(rt =>
        CorrelationContext.Current?.TenantId)
    from _ in Guard<AppRuntime>(tenant is not null,
        Error.New(400, "No tenant context"))
    from data in AppStore.TenantData.GetByTenantId<AppRuntime>(new TenantId(tenant!))
    select data;

Tenant-Scoped Queries

DataStore implementations automatically scope queries by tenant when TenantId is present in CorrelationContext:

// This query is automatically tenant-scoped — no manual filtering needed
var getOrders =
    from orders in AppStore.Orders.GetAll<AppRuntime>()
    select orders;
// Returns only orders for the current tenant

TypedId

TenantId uses a custom TypedId profile:

[TypedId(Profile = "tenant")]
public readonly partial struct TenantId;

The tenant profile uses BackingType.String since tenant identifiers are often external strings (e.g., from JWT claims or headers).