Projection Architecture¶
Projection is the single source of truth for attribute interpretation. Every attribute applied by users in Deepstaging.Abstractions is read and interpreted in exactly one place — the Projection layer. Generators, analyzers, and code fixes all consume the same models.
Pipeline Flow¶
Abstractions (Deepstaging) — marker attributes, zero behavior
↓
Projection (Deepstaging.Projection) — reads attributes, builds immutable models
↓
┌─────────────────────────────────────┐
│ Generators — emit C# source code │
│ Analyzers — report diagnostics │
│ CodeFixes — offer one-click fixes │
└─────────────────────────────────────┘
Two Entry Points¶
1. DeepstagingSymbol — per-symbol queries¶
Wraps a ValidSymbol<INamedTypeSymbol> in a zero-allocation readonly struct. Used by analyzers and within generator Map callbacks:
// In a generator Map callback
symbol.Deepstaging.Dispatch.Module() // → DispatchModel?
symbol.Deepstaging.Effects.Runtime() // → RuntimeModel?
symbol.Deepstaging.HttpClient.Client() // → HttpClientModel?
// In an analyzer
type.Deepstaging.Ids.TypedIdAttributes() // → ImmutableArray<TypedIdAttributeQuery>
2. DeepstagingContext — pipeline-level model building¶
Wraps IncrementalGeneratorInitializationContext for generators that combine multiple attribute sources:
// In a generator Initialize method
var models = context.Deepstaging().Dispatch.Models();
var runtimes = context.Deepstaging().Effects.Runtimes();
var (nonGeneric, generic) = context.Deepstaging().HttpClient.Models();
Feature Query Pattern¶
Every feature follows the same file structure in Projection:
Projection/{Feature}/
├── Attributes/{Feature}AttributeQuery.cs — read-only attribute data wrapper
├── Models/{Feature}Model.cs — immutable records (Roslyn cache-friendly)
├── {Feature}Queries.cs — public readonly struct (DeepstagingSymbol entry)
├── {Feature}Queries.Internal.cs — C# 14 extension members (implementation)
└── {Feature}Pipeline.cs — (optional) multi-source combine logic
The split ensures the public API surface is minimal (Queries.cs) while implementation details stay internal (Queries.Internal.cs).
Feature Query Structs¶
All accessible via symbol.Deepstaging.{Feature}:
| Feature | Query Struct | Key Methods |
|---|---|---|
| Dispatch | DispatchQueries |
Module(), CommandHandler(), QueryHandler() |
| Effects | EffectsQueries |
Runtime(), Modules(), Capabilities() |
| DataStore | DataStoreQueries |
Store(), Entity() |
| EventStore | EventStoreQueries |
Store(), Aggregate(), Projection() |
| EventQueue | EventQueueQueries |
Queue(), HandlerGroup() |
| IntegrationEvents | IntegrationEventsQueries |
Publisher(), Subscriber() |
| HttpClient | HttpClientQueries |
Client() |
| CacheStore | CacheStoreQueries |
Store(), CachedEntity() |
| Ids | TypedIdQueries |
TypedIdAttributes() |
| Config | ConfigQueries |
ConfigRoot(), ConfigModule() |
| Jobs | JobsQueries |
Handler() |
| Web | WebQueries, WebMethodQueries |
QueryRestApi(), CommandHandler(), QueryHandler() |
| Docs | DocsQueries |
Module() |
Pipeline Structs¶
Accessible via context.Deepstaging().{Feature}:
| Pipeline | Key Methods |
|---|---|
DispatchPipeline |
Models() — modules + merged handler groups |
EffectsPipeline |
Runtimes(), Modules(), Capabilities() |
DataStorePipeline |
Models() — stores + entities |
EventStorePipeline |
Models() — stores + aggregates + projections |
EventQueuePipeline |
Models() — queues + handler groups |
IntegrationEventsPipeline |
Publishers(), Subscribers() |
HttpClientPipeline |
Models() — generic + non-generic variants |
CacheStorePipeline |
Models() — stores + cached entities |
JobsPipeline |
Model() — all handlers → worker model |
WebPipeline |
Models() — RestApi + handlers + auth policies |
Design Guarantees¶
- Single interpretation: Each attribute is read in ONE internal extension method. Generators and analyzers share the same interpretation.
- Incremental caching: All models use immutable records and
EquatableArray<T>— Roslyn skips regeneration when inputs are unchanged. - Zero allocation: Both
DeepstagingSymbolandDeepstagingContextare readonly structs passed by value. - 100% coverage: All 17 generators query through Projection (no direct
ForAttributebypass).