Skip to content

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

  1. Single interpretation: Each attribute is read in ONE internal extension method. Generators and analyzers share the same interpretation.
  2. Incremental caching: All models use immutable records and EquatableArray<T> — Roslyn skips regeneration when inputs are unchanged.
  3. Zero allocation: Both DeepstagingSymbol and DeepstagingContext are readonly structs passed by value.
  4. 100% coverage: All 17 generators query through Projection (no direct ForAttribute bypass).