Documentation

adrs/002-spec-naming-convention.md

ADR-002: Use .Spec Suffix for Interface Projects (Updated: .Abstractions)

Status

Superseded by .Abstractions naming (See Update section at end of document)

Updated: 2026-01-08 (db-manager consolidation)

Context

In the traditional Polylith architecture, components have two main parts:

  • Interface: Defines the contract (what the component does)
  • Implementation: Contains the business logic (how the component does it)

In Clojure, this separation is naturally handled through namespaces. However, in .NET, we needed to decide how to structure projects to maintain this separation while working within the .NET project system and tooling.

Several naming approaches were considered:

  1. .Interface suffix: Acsis.Dynaplex.Engines.CoreData.Interface / Acsis.Dynaplex.Engines.CoreData
  2. .Spec suffix: Acsis.Dynaplex.Engines.CoreData.Spec / Acsis.Dynaplex.Engines.CoreData
  3. .Api suffix: Acsis.Dynaplex.Engines.CoreData.Api / Acsis.Dynaplex.Engines.CoreData.Impl
  4. .Spec suffix: Acsis.Dynaplex.Engines.CoreData.Spec / Acsis.Dynaplex.Engines.CoreData

The challenge was that "Interface" already has a very specific meaning in C# (the interface keyword), which could cause confusion when discussing "the component's interface" vs "a C# interface."

Decision

We decided to use the .Spec suffix for interface projects, standing for "Specification."

Project Structure:

engines/core-data/
├── src/
│   ├── Acsis.Dynaplex.Engines.CoreData.Spec/     # Component specification (interfaces, models, contracts)
│   └── Acsis.Dynaplex.Engines.CoreData/          # Component implementation

Naming Convention:

  • Specification Project: Acsis.{ComponentName}.Spec
  • Implementation Project: Acsis.{ComponentName}
  • Main Interface: I{ComponentName}Api

Example:

  • Spec Project: Acsis.Dynaplex.Engines.CoreData.Spec
  • Implementation Project: Acsis.Dynaplex.Engines.CoreData
  • Main Interface: ICoreDataApi
  • Implementation Class: CoreDataApi

Consequences

Positive

Clarity and Communication:

  • Unambiguous terminology: "Spec" doesn't conflict with C# "interface" keyword
  • Clear intent: "Specification" clearly communicates the purpose as a contract
  • Consistent naming: All components follow the same pattern
  • IDE support: Clear project names in solution explorer and IDE

Developer Experience:

  • Easy identification: Developers can quickly identify specification vs implementation
  • Logical grouping: Related files are grouped by project type
  • Reference clarity: MSBuild references clearly show $(ComponentSpec) vs implementation

Architectural Benefits:

  • Enforces separation: Physical project separation reinforces architectural boundaries
  • Dependency direction: Clear one-way dependency from implementation to specification
  • Interface evolution: Can evolve specifications independently of implementations

Negative

Learning Curve:

  • Non-standard terminology: "Spec" is not a standard .NET convention
  • Explanation required: New developers need to understand the naming convention
  • Documentation overhead: Requires clear documentation of the pattern

Tooling Considerations:

  • Custom conventions: Deviates from standard .NET project naming
  • Build complexity: Requires custom MSBuild properties for component references
  • Template creation: Need custom project templates for consistency

Potential Confusion:

  • Multiple meanings: "Spec" could be confused with "specification tests" or "API specs"
  • Inconsistent ecosystem: Other .NET projects use different conventions
  • Third-party tools: Some tools might not recognize the custom pattern

Mitigation Strategies

To address the negative consequences:

  1. Comprehensive Documentation:

    • Clear explanation in developer onboarding
    • Examples in all documentation
    • Consistent use throughout codebase
  2. MSBuild Integration:

    • Centralized properties in Directory.Build.props
    • Consistent reference patterns: $(ComponentNameSpec)
  3. IDE Support:

    • Solution folders organize projects clearly
    • Consistent naming helps with navigation
  4. Code Templates:

    • Project templates for creating new components
    • Consistent structure across all components
  5. Roslyn Analyzers:

    • Enforce that only .Spec projects can be referenced across components
    • Catch architectural violations at compile time
  • ADR-001: Overall architecture adoption
  • ADR-004: Analyzer enforcement of spec-only references
  • ADR-006: MSBuild property centralization

Alternatives Considered

.Interface Suffix

Pros: Clear intent, common in some .NET codebases
Cons: Conflicts with C# interface keyword, could cause confusion in discussions

.Spec Suffix

Pros: Clear business intent, used in some .NET architectures
Cons: Longer name, could imply legal/business contracts rather than technical contracts

.Api Suffix with .Impl

Pros: Common pattern, clear API vs implementation
Cons: "Api" is ambiguous (HTTP API vs programmatic API), requires renaming implementation

Examples

Component Reference:

<!-- In Directory.Build.props -->
<CoreDataSpec>$(ComponentsRoot)core-data/src/Acsis.Dynaplex.Engines.CoreData.Spec/Acsis.Dynaplex.Engines.CoreData.Spec.csproj</CoreDataSpec>

<!-- In consuming project -->
<ProjectReference Include="$(CoreDataSpec)" />

Code Usage:

// ✅ Allowed - referencing specification



// ❌ Forbidden - referencing implementation directly
using Acsis.Dynaplex.Engines.CoreData; // This triggers ACSIS0001 analyzer error

This naming convention has proven effective in maintaining clear architectural boundaries while working within .NET conventions and tooling.

Update: Migration to .Abstractions (August 2025)

Status Change

This ADR has been superseded by the migration to .Abstractions naming as part of the .NET Aspire microservices migration (see ADR-007).

New Naming Convention

Current Structure:

engines/core-data/
├── src/
│   ├── Acsis.Dynaplex.Engines.CoreData.Abstractions/  # Interfaces, models, contracts
│   ├── Acsis.Dynaplex.Engines.CoreData.Database/      # EF Core DbContext and entities
│   ├── Acsis.Dynaplex.Engines.CoreData/               # Service implementation
│   └── Acsis.Dynaplex.Engines.CoreData.ApiClient/     # Auto-generated HTTP client

Note (2026-01-08): Per-component .DbMigrator projects have been replaced by a centralized db-manager component. See ADR-036.

Rationale for Change

  1. Industry alignment: .Abstractions is a standard .NET convention used by Microsoft and the community
  2. Clearer intent: "Abstractions" better describes the content (interfaces, abstract classes, contracts)
  3. Microservices pattern: Aligns with common microservices patterns where abstractions define service contracts
  4. Tooling support: Better IDE and tooling recognition of the pattern

Migration Impact

  • All .Spec projects have been renamed to .Abstractions
  • MSBuild properties updated from $(ComponentNameSpec) to $(ComponentNameAbstractions)
  • No functional changes to the separation of interface and implementation
  • The architectural principle of referencing only abstractions across components remains unchanged