Documentation
reference/analyzer-rules.md
Dynaplex Roslyn Analyzer Rules Reference
Complete Reference: ACSIS0001-ACSIS0085
Last Updated: 2025-12-17
Status: Comprehensive rule definitions with phased implementation roadmap
Overview
This document provides a complete reference for all Roslyn analyzer rules enforcing Dynaplex architectural patterns. Rules are organized by category and priority, with implementation status tracked for each rule.
Rule Categories
- Architecture & Boundaries (ACSIS0001, 0010-0017, 0053-0057)
- Library Constraints (ACSIS0080-0085) ⭐ NEW
- Database & Entity Patterns (ACSIS0002, 0013-0015, 0019-0038)
- API & Service Patterns (ACSIS0039-0047)
- Configuration & Service Registration (ACSIS0048-0052)
- MQTT & Event Patterns (ACSIS0058-0060)
- Testing & Quality (ACSIS0005, 0008-0009, 0061-0065)
- Security & Validation (ACSIS0066-0068)
- Documentation & Observability (ACSIS0069-0070)
Priority Levels
| Priority | Description | Response Time |
|---|---|---|
| 🔴 CRITICAL | Prevents runtime errors, data corruption, or security vulnerabilities | Implement immediately |
| 🟠 HIGH | Enforces important patterns, prevents common mistakes | Implement in current sprint |
| 🟡 MEDIUM | Improves consistency and maintainability | Implement within quarter |
| 🟢 LOW | Nice-to-have improvements | Implement as time allows |
Implementation Status
| Status | Icon | Meaning |
|---|---|---|
| ✅ Implemented | 🟢 | Analyzer active and enforced |
| 🚧 In Progress | 🟡 | Currently being developed |
| 📋 Planned | ⚪ | Defined, awaiting implementation |
| 💡 Proposed | 🔵 | Under consideration |
Architecture & Boundaries
ACSIS0001: Forbidden reference of non-abstraction project
Status: ✅ Implemented | Priority: 🔴 CRITICAL | Severity: Error
Rule: Components may only reference other components' .Abstractions or .ApiClient projects, never implementation projects.
Violation:
<!-- ❌ Wrong -->
<ProjectReference Include="$(CatalogImplementation)" />
Fix:
<!-- ✅ Correct -->
<ProjectReference Include="$(CatalogAbstractions)" />
<ProjectReference Include="$(CatalogApiClient)" />
Related ADRs: ADR-001, ADR-021
ACSIS0010-0011: Cross-component DbContext detection
Status: ✅ Implemented | Priority: 🟠 HIGH | Severity: Warning
Rule: Components should not directly access other components' DbContext. Use API clients instead.
Migration Period: Currently warnings, will become errors after Aspire migration complete.
ACSIS0012: DateTimeOffset is prohibited
Status: ✅ Implemented | Priority: 🟠 HIGH | Severity: Warning
Rule: Use DateTime with Kind = DateTimeKind.Utc instead of DateTimeOffset for PostgreSQL compatibility.
Violation:
// ❌ Wrong
public DateTimeOffset CreatedAt { get; set; }
Fix:
// ✅ Correct
public DateTime CreatedAt { get; set; } // Ensure Kind = Utc
Related Documentation: docs/reference/patterns/database-architecture.md
ACSIS0013-0014: DbContext schema configuration
Status: ✅ Implemented | Priority: 🔴 CRITICAL | Severity: Error
ACSIS0013: DbContext must call ConfigureSchemaIsolation(SCHEMA) in OnModelCreating.
ACSIS0014: DbContext must define public const string SCHEMA = "schema_name".
Related ADRs: ADR-009, ADR-036
ACSIS0015: DbContext in Abstractions project
Status: ✅ Implemented | Priority: 🔴 CRITICAL | Severity: Error
Rule: DbContext classes must be in .Database projects per ADR-036, not .Abstractions.
ACSIS0016: Scoped service injected into singleton
Status: ✅ Implemented | Priority: 🔴 CRITICAL | Severity: Error
Rule: Singletons (BackgroundService, IHostedService) must not inject scoped dependencies directly. Use IServiceProvider.CreateAsyncScope().
Violation:
// ❌ Wrong
public class MyBackgroundService(CatalogDb db) : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken ct)
{
await db.Items.ToListAsync(ct); // Runtime error!
}
}
Fix:
// ✅ Correct
public class MyBackgroundService(IServiceProvider services) : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken ct)
{
await using var scope = services.CreateAsyncScope();
var db = scope.ServiceProvider.GetRequiredService<CatalogDb>();
await db.Items.ToListAsync(ct);
}
}
ACSIS0017: API implementation class should be static
Status: ✅ Implemented | Priority: 🟠 HIGH | Severity: Warning
Rule: API implementation classes with [AcsisApi] should be static to enforce stateless pattern.
ACSIS0053-0057: Project structure and architectural boundaries
Status: 📋 Planned (Phase 3) | Priority: 🔴 CRITICAL | Severity: Error
ACSIS0053: .Database project referencing wrong .Abstractions (should reference .Database)
ACSIS0054: .ApiClient project referencing .Database (architectural violation)
ACSIS0055: Service references component without .ApiClient project
ACSIS0056: Namespace not matching project naming convention
ACSIS0057: Entity class in .Abstractions instead of .Database
Related ADRs: ADR-036
Library Constraints
These rules enforce ADR-046 library project constraints, ensuring libraries remain pure, stateless utilities.
ACSIS0080: Library references component project
Status: ✅ Implemented | Priority: 🔴 CRITICAL | Severity: Error
Rule: Library projects (Acsis.Dynaplex.Strata.*) cannot reference component projects (Acsis.Dynaplex.Engines.*).
Why: Libraries must remain independent of components to maintain architectural purity. If code needs component dependencies, it belongs in a component, not a library.
Violation:
<!-- In Acsis.Dynaplex.Strata.MyLibrary.csproj -->
<!-- ❌ Wrong -->
<ProjectReference Include="$(CatalogAbstractions)" />
Fix: Move the code to a component's .Abstractions or service project.
ACSIS0081: Library uses Entity Framework Core
Status: ✅ Implemented | Priority: 🔴 CRITICAL | Severity: Error
Rule: Library projects cannot reference Entity Framework Core packages.
Why: Libraries cannot own persistence. Database access belongs in component .Database projects.
Violation:
<!-- In Acsis.Dynaplex.Strata.MyLibrary.csproj -->
<!-- ❌ Wrong -->
<PackageReference Include="Microsoft.EntityFrameworkCore" />
Fix: Move database code to a component's .Database project.
ACSIS0082: Library uses ASP.NET Core
Status: ✅ Implemented | Priority: 🔴 CRITICAL | Severity: Error
Rule: Library projects cannot reference ASP.NET Core packages.
Why: Libraries cannot expose HTTP endpoints. Endpoint code belongs in component service projects.
Violation:
<!-- In Acsis.Dynaplex.Strata.MyLibrary.csproj -->
<!-- ❌ Wrong -->
<PackageReference Include="Microsoft.AspNetCore.Http" />
Fix: Move endpoint code to a component service project.
ACSIS0083: Library uses HttpClient directly
Status: ✅ Implemented | Priority: 🟠 HIGH | Severity: Warning
Rule: Library projects should not use HttpClient directly.
Why: Libraries should be pure and side-effect-free. HTTP calls belong in component services that handle resilience, retries, and service discovery.
Violation:
// In Acsis.Dynaplex.Strata.MyLibrary
// ⚠️ Warning
var client = new HttpClient();
await client.GetAsync("https://api.example.com");
Fix: Move HTTP communication to a component service project.
ACSIS0084: Library references Database project
Status: ✅ Implemented | Priority: 🔴 CRITICAL | Severity: Error
Rule: Library projects cannot reference .Database projects.
Why: Libraries cannot depend on database entities or DbContexts.
Violation:
<!-- In Acsis.Dynaplex.Strata.MyLibrary.csproj -->
<!-- ❌ Wrong -->
<ProjectReference Include="$(CatalogDatabase)" />
Fix: If you need database access, the code belongs in a component's service or .Database project.
ACSIS0085: Library references ApiClient project
Status: ✅ Implemented | Priority: 🔴 CRITICAL | Severity: Error
Rule: Library projects cannot reference .ApiClient projects.
Why: Libraries cannot call other services. Service communication belongs in component service projects.
Violation:
<!-- In Acsis.Dynaplex.Strata.MyLibrary.csproj -->
<!-- ❌ Wrong -->
<ProjectReference Include="$(CatalogApiClient)" />
Fix: Move code that calls services to a component service project.
Library Rules Summary
| Rule | Severity | Description |
|---|---|---|
| ACSIS0080 | Error | Cannot reference component projects |
| ACSIS0081 | Error | Cannot use Entity Framework Core |
| ACSIS0082 | Error | Cannot use ASP.NET Core |
| ACSIS0083 | Warning | Should not use HttpClient directly |
| ACSIS0084 | Error | Cannot reference .Database projects |
| ACSIS0085 | Error | Cannot reference .ApiClient projects |
Related ADRs: ADR-046
Database & Entity Patterns
ACSIS0002: Journal entity class not configured correctly
Status: ✅ Implemented | Priority: 🔴 CRITICAL | Severity: Error
Rule: Journal entities must inherit from JournalBase and mirror all properties from source entity.
Code Fix: Auto-generates missing properties
ACSIS0018: Entity missing TenantId index
Status: ✅ Implemented | Priority: 🟠 HIGH | Severity: Warning
Rule: Entities with TenantId property must have database index configured for query performance.
ACSIS0019-0022: Entity constant patterns
Status: ✅ Implemented | Priority: 🟠 HIGH | Severity: Warning/Info
ACSIS0019: Entity missing TABLE_NAME constant
ACSIS0020: [Table] attribute should use TABLE_NAME constant
ACSIS0021: Passport-enabled entity missing PTID constant
ACSIS0022: PlatformTypeId should use PTID constant
Code Fix: Available for all four rules
Related Documentation: docs/reference/patterns/entity-constants.md
ACSIS0023: OnModelCreating method calls not in correct order
Status: 📋 Planned (Phase 1) | Priority: 🔴 CRITICAL | Severity: Warning
Rule: ConfigureSchemaIsolation(SCHEMA) must be called LAST in OnModelCreating to prevent external entities from being included in migrations.
Violation:
// ❌ Wrong order
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ConfigureSchemaIsolation(SCHEMA); // Too early!
modelBuilder.HasDefaultSchema(SCHEMA);
base.OnModelCreating(modelBuilder);
// Entity configurations...
}
Fix:
// ✅ Correct order
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.HasDefaultSchema(SCHEMA);
base.OnModelCreating(modelBuilder);
// Entity configurations...
modelBuilder.AddExternalReference<Passport>(...);
// LAST - after all configurations
modelBuilder.ConfigureSchemaIsolation(SCHEMA);
}
Code Fix: Yes - Reorder method calls
ACSIS0024-0026: Schema isolation enforcement
Status: 📋 Planned (Phase 1) | Priority: 🔴 CRITICAL | Severity: Error
ACSIS0024: DbSet references external entity without AddExternalReference configuration
ACSIS0025: DbSet for entity from different schema without exclusion configuration
ACSIS0026: DbContext missing HasDefaultSchema call
Why Critical: These rules prevent schema pollution - the #1 cause of migration errors in Dynaplex.
Code Fix: ACSIS0024 (generate AddExternalReference), ACSIS0026 (insert HasDefaultSchema)
Related Documentation: docs/reference/patterns/database-architecture.md
ACSIS0027-0031: Naming conventions
Status: 📋 Planned (Phase 2-4) | Priority: 🟡 MEDIUM | Severity: Warning/Info
ACSIS0027: Foreign key constraint naming: fk__{source_schema}__{source_table}__{target_schema}__{target_table}__{column}
ACSIS0028: Primary key constraint naming: pk__{schema}__{table}
ACSIS0029: Index naming: ix__{schema}__{table}__{columns}
ACSIS0030: Column names must be lowercase snake_case
ACSIS0031: Table names must be lowercase plural
Code Fix: Available for most rules
ACSIS0032-0033: Passport pattern enforcement
Status: 📋 Planned (Phase 1) | Priority: 🔴 CRITICAL | Severity: Error
ACSIS0032: Passport entity missing [DatabaseGenerated(None)] attribute
ACSIS0033: Passport entity missing foreign key relationship to prism.passports
Why Critical: Prevents EF Core from auto-generating Guid/PTID values, which breaks Passport pattern.
Code Fix: ACSIS0032 (add attribute)
ACSIS0034-0038: Journal table patterns
Status: 📋 Planned (Phase 1-2) | Priority: 🔴 CRITICAL | Severity: Error/Warning
ACSIS0034: Journal entity not inheriting from JournalTable
ACSIS0035: Journal entity table not in prism schema
ACSIS0036: Journal entity name not following convention ({component}_{table}_journal)
ACSIS0037: Journal entity missing composite key configuration
ACSIS0038: Entity with journal table missing trigger registration
Related ADRs: ADR-035
Code Fix: Available for ACSIS0034, 0035, 0036, 0038
API & Service Patterns
ACSIS0039-0047: Minimal API conventions
Status: 📋 Planned (Phase 2-4) | Priority: 🟠 HIGH | Severity: Warning/Info
ACSIS0039: Endpoint method not following naming convention (Get*, Post*, etc.)
ACSIS0040: Endpoint handler missing TypedResults return type (per ADR-014)
ACSIS0041: Endpoint parameter missing binding source attribute
ACSIS0042: Endpoint missing .WithName() call [CRITICAL for Kiota]
ACSIS0043: Endpoint missing .WithDisplayName() call
ACSIS0044: Endpoint group missing .WithTags() call
ACSIS0045: Endpoint missing authorization requirement [Security]
ACSIS0046: API handler method should be private static
ACSIS0047: MapEndpoints extension method not following convention
Related ADRs: ADR-014 (TypedResults), ADR-021 (Kiota)
Code Fix: Available for ACSIS0041-0047
Configuration & Service Registration
ACSIS0048-0052: Options pattern and service registration
Status: 📋 Planned (Phase 3-4) | Priority: 🟠 HIGH | Severity: Warning/Info
ACSIS0048: Options class missing validation
ACSIS0049: Options class missing SectionName constant
ACSIS0050: Options configuration missing .ValidateOnStart()
ACSIS0051: Service registration missing interface
ACSIS0052: HttpClient registration missing BaseAddress configuration
Code Fix: Available for ACSIS0049, 0050
MQTT & Event Patterns
ACSIS0058-0060: MQTT and event patterns
Status: 📋 Planned (Phase 4) | Priority: 🟡 MEDIUM | Severity: Warning/Info/Error
ACSIS0058: MQTT topic not following naming convention (acsis/{component}/{entity}/{action})
ACSIS0059: MQTT processor missing [MqttTopic] attribute
ACSIS0060: Event class should be immutable record type
Code Fix: Available for ACSIS0060
Testing & Quality
ACSIS0005, 0008-0009: Async/Await patterns
Status: ✅ Implemented | Priority: 🟡 MEDIUM | Severity: Warning/Info
ACSIS0005: Avoid async void methods
ACSIS0008: Async methods should have 'Async' suffix
ACSIS0009: Avoid string concatenation in loops
ACSIS0061-0063: Test naming conventions
Status: 📋 Planned (Phase 4) | Priority: 🟢 LOW | Severity: Info/Warning
ACSIS0061: Test class not ending with "Tests" (plural)
ACSIS0062: Public method in test class missing test attribute
ACSIS0063: Test method not following naming convention ({Method}_{Scenario}_{Expected})
Code Fix: Available for ACSIS0061, 0062
ACSIS0064-0065: Async optimization patterns
Status: 📋 Planned (Phase 4) | Priority: 🟢 LOW | Severity: Info/Warning
ACSIS0064: Missing ConfigureAwait(false) in library code (Optional/Controversial)
ACSIS0065: Async method not awaiting any operations
Code Fix: Available for both
Security & Validation
ACSIS0066-0068: Security patterns
Status: 📋 Planned (Phase 2-3) | Priority: 🟠 HIGH | Severity: Warning/Error
ACSIS0066: Endpoint missing input validation
ACSIS0067: Sensitive data in log statement [CRITICAL - Prevents credential leakage]
ACSIS0068: Sensitive endpoint missing authorization
Code Fix: Available for ACSIS0068
Documentation & Observability
ACSIS0069-0070: Developer experience
Status: 📋 Planned (Phase 4) | Priority: 🟡 MEDIUM | Severity: Warning/Info
ACSIS0069: Public API missing XML documentation
ACSIS0070: Long-running method missing Activity span
Code Fix: Available for ACSIS0070
Implementation Roadmap
Phase 1: Critical Database Integrity (Sprint 1)
Focus: Prevent schema pollution and migration errors
- ✅ ACSIS0001, 0013-0015 (Already implemented)
- 🚧 ACSIS0023-0026 (Schema isolation enforcement)
- 🚧 ACSIS0032-0037 (Passport and journal patterns)
Expected Impact: Eliminate 90%+ of migration errors
Phase 2: API Type Safety & Security (Sprint 2-3)
Focus: Kiota client generation and security by default
- ACSIS0040 (TypedResults per ADR-014)
- ACSIS0042 (WithName for Kiota) [CRITICAL]
- ACSIS0045, 0068 (Authorization enforcement)
- ACSIS0067 (Sensitive data in logs)
Expected Impact: 100% Kiota-compatible endpoints, security by default
Phase 3: Configuration & Architecture (Sprint 4)
Focus: Fail-fast startup and architectural boundaries
- ACSIS0048-0051 (Options pattern)
- ACSIS0053-0054, 0057 (Project boundary enforcement)
Expected Impact: Faster feedback loop, architectural compliance
Phase 4: Quality of Life (Backlog)
Focus: Developer experience improvements
- ACSIS0027-0031 (Naming conventions)
- ACSIS0038-0047 (API conventions)
- ACSIS0058-0063 (MQTT, events, tests)
- ACSIS0064-0065, 0069-0070 (Optimizations, documentation)
Expected Impact: Consistent code style, better onboarding
Usage Guide
Enabling Analyzers
Analyzers are automatically enabled when the Acsis.RoslynAnalyzers NuGet package is referenced:
<ItemGroup>
<PackageReference Include="Acsis.RoslynAnalyzers" Version="1.0.0" />
</ItemGroup>
Suppressing Rules
For legitimate exceptions, use #pragma warning disable:
#pragma warning disable ACSIS0019 // Justification: Legacy IoT entity
public class TnDevice
{
// ... no TABLE_NAME constant for legacy reasons
}
#pragma warning restore ACSIS0019
Note: Suppressions should be rare and require code review justification.
Configuring Severity
Override severity in .editorconfig:
# Treat info-level warnings as build errors in release
[*.cs]
dotnet_diagnostic.ACSIS0020.severity = error # Production enforcement
dotnet_diagnostic.ACSIS0039.severity = none # Disable naming suggestions
IDE Integration
Visual Studio / Rider:
- Violations appear as squiggles
- Press
Ctrl+.(Windows/Linux) orCmd+.(Mac) for Quick Actions - Select code fix to auto-remediate
Command Line:
# Show all analyzer warnings
dotnet build
# Treat warnings as errors
dotnet build /p:TreatWarningsAsErrors=true
# Suppress specific rule
dotnet build /p:NoWarn=ACSIS0020
Related Documentation
- Entity Constants Pattern - ACSIS0019-0022
- Database Architecture - ACSIS0013-0014, 0023-0038
- Journal Tables - ACSIS0002, 0034-0038
- ADR-009: Database Schema per Service
- ADR-014: TypedResults for Minimal APIs
- ADR-021: Kiota API Client Generation
- ADR-035: Prism Journal Tables
- ADR-036: Database Project Separation
Contributing
Adding New Rules
- Define diagnostic descriptor in
AnalyzerDefinitions.cs - Create or update analyzer class in appropriate category
- Add unit tests in
Acsis.RoslynAnalyzers.Tests - Update this documentation
- Consider creating code fix provider
Rule Numbering Convention
- ACSIS0001-0099: Architecture and database patterns
- ACSIS0100-0199: (Reserved for future use)
- ACSIS1000+: Component-specific rules
Summary
Total Rules: 70 (ACSIS0001-0070)
Implemented: 22 (31%)
Planned: 48 (69%)
By Priority:
- 🔴 CRITICAL: 18 rules (prevent runtime errors, data corruption)
- 🟠 HIGH: 15 rules (important patterns, security)
- 🟡 MEDIUM: 20 rules (consistency, maintainability)
- 🟢 LOW: 17 rules (nice-to-have improvements)
The Dynaplex analyzer suite provides compile-time enforcement of architectural patterns, significantly reducing runtime errors and improving developer productivity through immediate feedback and auto-fixes.