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:
.Interfacesuffix:Acsis.Dynaplex.Engines.CoreData.Interface/Acsis.Dynaplex.Engines.CoreData.Specsuffix:Acsis.Dynaplex.Engines.CoreData.Spec/Acsis.Dynaplex.Engines.CoreData.Apisuffix:Acsis.Dynaplex.Engines.CoreData.Api/Acsis.Dynaplex.Engines.CoreData.Impl.Specsuffix: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:
Comprehensive Documentation:
- Clear explanation in developer onboarding
- Examples in all documentation
- Consistent use throughout codebase
MSBuild Integration:
- Centralized properties in
Directory.Build.props - Consistent reference patterns:
$(ComponentNameSpec)
- Centralized properties in
IDE Support:
- Solution folders organize projects clearly
- Consistent naming helps with navigation
Code Templates:
- Project templates for creating new components
- Consistent structure across all components
Roslyn Analyzers:
- Enforce that only
.Specprojects can be referenced across components - Catch architectural violations at compile time
- Enforce that only
Related Decisions
- 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
.DbMigratorprojects have been replaced by a centralizeddb-managercomponent. See ADR-036.
Rationale for Change
- Industry alignment:
.Abstractionsis a standard .NET convention used by Microsoft and the community - Clearer intent: "Abstractions" better describes the content (interfaces, abstract classes, contracts)
- Microservices pattern: Aligns with common microservices patterns where abstractions define service contracts
- Tooling support: Better IDE and tooling recognition of the pattern
Migration Impact
- All
.Specprojects 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