Architecture at a glance
Stratara ships 24 packages: 22 runtime packages organized into three tiers (below), plus two test-support packages (Stratara.Testing, Stratara.Testing.EntityFrameworkCore) referenced only from test projects. Each tier may only depend on tiers at or below its own number. The dependency direction is enforced by ProjectReferences in the repo — no cyclic references, no consumer references.
Tier layout
Tier-A (foundational — no inbound deps from B or C)
├── Stratara.Abstractions IMediator / ICommand / IQuery / IAggregate / ITenantAggregate
│ + Persistence/Outbox/Messaging/Session/Security/Validation/Auth contracts
├── Stratara.Contracts Wire-level POCO records (EventMessage, EventBundle, CommandEnvelope, …)
├── Stratara.Diagnostics ActivitySource + Meter + LogEvent-ID schema (100_000 range)
└── Stratara.Resilience Polly named pipelines via AddResiliencePipelines()
Tier-B (builds on Tier-A only)
├── Stratara.Mediator Mediator, AuthorizingMediator, pipeline behaviors
├── Stratara.Domain Tenant aggregate + lifecycle events
├── Stratara.Shared Umbrella re-export + source-gen Logger* extensions
├── Stratara.Sessions Actor / Subject SessionContext + ASP.NET middleware
└── Stratara.ServiceDefaults OpenTelemetry + Serilog defaults (lean Tier-B)
Tier-C (builds on Tier-B + Tier-A)
├── Stratara.EventSourcing.EntityFrameworkCore WriteStore + ReadStore + IdentityStore (folded)
├── Stratara.EventSourcing.Pipeline.CommandAudit Command-audit pipeline behavior
├── Stratara.EventSourcing.WorkerDefaults 7 worker composites (incl. heavy-command lane)
├── Stratara.Validation IValidator<T> + validation pipeline behavior
├── Stratara.Projections Projection runtime + ProjectionManager
├── Stratara.Sagas ISaga interface + saga dispatcher
├── Stratara.Security Envelope IKeyStore (KEK-wrapped DEKs) + AES-GCM blob encryptor
├── Stratara.Outbox.RabbitMQ RabbitMQ outbox + worker + CommandOutboxDispatcher
├── Stratara.Outbox.AzureServiceBus Azure Service Bus IMessageBus impl
├── Stratara.Infrastructure Auth decorators + DI composition glue
├── Stratara.Identity.Core Channel-agnostic identity primitives
├── Stratara.Identity.AspNetCore ASP.NET Core identity wiring
└── Stratara.ServiceDefaults.AspNetCore ASP.NET OTel + health + endpoints
Read the tiers as a dependency-direction promise
- Tier-A is pure contracts. Adding a class here means committing to an API surface for the lifetime of the major version.
- Tier-B implements Tier-A interfaces that are general enough to not need infrastructure (mediator, domain primitives).
- Tier-C does the rest — anything touching EF Core, a broker, ASP.NET, or a third-party SDK lives in Tier-C.
A package that another packable package ProjectReferences must itself be packable — otherwise the parent's nuspec lists a dependency that doesn't exist on the feed. The Stratara.Publish.slnf solution filter enforces which projects are packable.
What lives where, in plain English
| If you want to… | Add a reference to… |
|---|---|
| Dispatch a command in-process | Stratara.Mediator |
| Dispatch a command async via outbox | Stratara.Mediator + Stratara.Outbox.RabbitMQ (or Outbox.AzureServiceBus) |
| Persist events to PostgreSQL | Stratara.EventSourcing.EntityFrameworkCore |
| Run projections on the read side | Stratara.Projections |
| Run sagas / process managers | Stratara.Sagas |
| Validate requests before the handler | Stratara.Validation |
| Enforce tenant isolation at the mediator entrance | Stratara.Mediator (AddStrataraTenantIsolation() + ITenantScopedRequest) |
| Manage keys + encrypt blobs (production) | Stratara.Security (envelope IKeyStore + AES-GCM ISecureBlobEncryptor, dependency-light) |
Encrypt sensitive properties ([EncryptData] fields) |
Stratara.Infrastructure (field/JSON path) + Stratara.Security (key store + blob encryption) |
| Plug in ASP.NET identity | Stratara.Identity.AspNetCore |
| Wire OpenTelemetry + Serilog | Stratara.ServiceDefaults (plus .AspNetCore for HTTP-host extras) |
| Just the interfaces, no impl | Stratara.Abstractions + Stratara.Contracts |
Why this layout
- Stable Tier-A surface. Consumers can pin Tier-A interfaces and let Tier-B/C bump independently within a major version.
- No consumer pollution. Anything specific to a downstream consumer stays in the consumer — Stratara absorbs none of it.
- Composable workers.
Stratara.EventSourcing.WorkerDefaultsprovides 6 named worker composites (CommandHandling, EventProjection, EventStreamHashing, OutboxHandling, SagaOrchestration, ServiceDefaults). Consumer hosts opt in à la carte.
Event flow (at a glance)
Command → CommandOutboxDispatcher → RabbitMQ → CommandWorker → Handler ← competing consumers, N nodes
→ IEventSource → Events → WriteStore → EventBundle ← ~11 ns/event apply-dispatch (compiled, no reflection)
→ ProjectionWorker → Projections → ReadStore ← push-driven (subscribed), not polled
Each stage scales independently: command, projection, and saga workers are competing consumers (any number of instances across nodes), streams partition across 4096 deterministic buckets, and projections react to pushed event bundles instead of polling. See Performance and Scaling for the design and the measured numbers, and DI Composition for the full composition story.