Skip to content

Keep Pulsar tenant isolation independent

Scope every lookup and mutation to its tenant independently of authorization; queued work reconstructs actor and tenant data before it acts. Pulsar supplies guidance and generated placeholders, not a tenancy package, Context abstraction, storage recipe, middleware, or binding lifecycle.

Reconstruct durable runtime data

HTTP establishes actor and trusted tenant input at its boundary. A Job, Command, or queued Listener carries durable IDs, scalars, DTOs, or Value Objects, reconstructs actor and tenant data, rejects missing information, and then re-fetches tenant-scoped state. Never serialize a live Model as worker authority.

Scope every subject to its tenant

Permission does not make a subject belong to a tenant. Apply tenant scope to every read and mutation, then reject a mismatch independently of Gate or Policy result. A Command requires an explicit tenant or rejects unattended invocation; a retry repeats reconstruction and scoped lookup.

Keep four enforcement layers separate

LayerOwnerDoes not prove
Adapter authorizationHTTP, Job, Command, and Listener authorize before one UseCase.Tenant membership, business validity, or idempotency.
Tenant isolationAdapter reconstructs tenant and scopes every subject.Permission, invariant, or a tenancy package.
Domain invariantAction or UseCase validates state inside the UseCase transaction for every caller.Permission, membership, or deduplication.
Idempotency and reentrancyRetryable effect reuses a stable key; application owns its storage choice.Authorization, tenant scope, committed dispatch, or durable relay.
HTTP, Job, Command, and Listener audiences establish or reconstruct actor and tenant data, authorize an ability through a Gate or Policy, scope a subject to the tenant, call one UseCase, reject an invariant or mismatch, and reuse retry idempotency.
Enforce authorization and tenant isolation independently. Permission still needs tenant scope and valid Domain state; a retry still needs idempotency.

Interpretation. Actor authorization, tenant scope, Audience entry, Domain invariants, and retry idempotency are independent enforcement layers. A deny or mismatch must stop its own path rather than being inferred from another layer.

Reject mismatches and duplicates

For HTTP, a trusted tenant input scopes the lookup before the UseCase. For Job, Command, and Listener work, reconstruct actor and tenant, explicitly authorize with Gate::forUser(actor), scope the re-fetch, then call one UseCase. The UseCase validates the invariant; a retry uses the same idempotency key and a reentrancy guard where reaction loops are possible. Do not treat HTTP validation, a successful Gate, or a queue retry as deduplication.

Test the boundaries

Test permission denial, tenant mismatch, invalid Domain state, and repeated-key handling separately for each audience. The current integration fixture proves a scoped lifecycle and after-commit behavior, but not actor reconstruction, tenant filtering, broker durability, or idempotency. This page intentionally leaves discovery, binding, identifiers, and Octane work to later architecture scope.