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

  1. Architecture & Boundaries (ACSIS0001, 0010-0017, 0053-0057)
  2. Library Constraints (ACSIS0080-0085) ⭐ NEW
  3. Database & Entity Patterns (ACSIS0002, 0013-0015, 0019-0038)
  4. API & Service Patterns (ACSIS0039-0047)
  5. Configuration & Service Registration (ACSIS0048-0052)
  6. MQTT & Event Patterns (ACSIS0058-0060)
  7. Testing & Quality (ACSIS0005, 0008-0009, 0061-0065)
  8. Security & Validation (ACSIS0066-0068)
  9. 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) or Cmd+. (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


Contributing

Adding New Rules

  1. Define diagnostic descriptor in AnalyzerDefinitions.cs
  2. Create or update analyzer class in appropriate category
  3. Add unit tests in Acsis.RoslynAnalyzers.Tests
  4. Update this documentation
  5. 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.