SOLID Architecture - REslava.Result.SourceGenerators v1.9.4+
The REslava.Result.SourceGenerators library has been completely refactored in v1.9.4 to follow SOLID principles, eliminating duplicate generation issues and creating a maintainable, extensible codebase.
Before v1.9.4 - Problems
// ❌ Single class doing everything
[Generator]
public class ResultToIResultGenerator : IIncrementalGenerator
{
// Attribute generation
// Code generation
// Pipeline coordination
// Configuration parsing
// Error handling
// All mixed together!
}
After v1.9.4 - Solution
// ✅ Each class has one responsibility
public interface IAttributeGenerator
{
SourceText GenerateAttribute();
}
public interface ICodeGenerator
{
SourceText GenerateCode(Compilation compilation, object config);
}
public interface IOrchestrator
{
void Initialize(IncrementalGeneratorInitializationContext context);
}
// Separate classes for each responsibility
public class GenerateResultExtensionsAttributeGenerator : IAttributeGenerator { }
public class ResultToIResultExtensionGenerator : ICodeGenerator { }
public class ResultToIResultOrchestrator : IOrchestrator { }
Extensible Design
// ✅ Open for extension, closed for modification
public interface ICodeGenerator
{
SourceText GenerateCode(Compilation compilation, object config);
}
// New generators can be added without changing existing code
public class CustomExtensionGenerator : ICodeGenerator
{
public SourceText GenerateCode(Compilation compilation, object config)
{
// New functionality without modifying existing generators
}
}
Before v1.9.4 - Tight Coupling
// ❌ Depends on concrete implementations
public class ResultToIResultGenerator
{
private readonly SomeConcreteHelper _helper = new SomeConcreteHelper();
private readonly AnotherConcreteService _service = new AnotherConcreteService();
}
After v1.9.4 - Loose Coupling
// ✅ Depends on abstractions
public class ResultToIResultOrchestrator : IOrchestrator
{
private readonly IAttributeGenerator _attributeGenerator;
private readonly ICodeGenerator _codeGenerator;
// Constructor injection
public ResultToIResultOrchestrator(
IAttributeGenerator attributeGenerator,
ICodeGenerator codeGenerator)
{
_attributeGenerator = attributeGenerator;
_codeGenerator = codeGenerator;
}
}
Focused Interfaces
// ✅ Each interface has a single, focused responsibility
public interface IAttributeGenerator
{
SourceText GenerateAttribute();
}
public interface ICodeGenerator
{
SourceText GenerateCode(Compilation compilation, object config);
}
public interface IOrchestrator
{
void Initialize(IncrementalGeneratorInitializationContext context);
}
// No fat interfaces with multiple responsibilities
SourceGenerator/Generators/ResultToIResult/
├── Interfaces/
│ ├── IAttributeGenerator.cs # Attribute generation contract
│ ├── ICodeGenerator.cs # Code generation contract
│ └── IOrchestrator.cs # Orchestration contract
├── Attributes/
│ ├── GenerateResultExtensionsAttributeGenerator.cs # Generates attributes only
│ └── MapToProblemDetailsAttributeGenerator.cs # Generates mapping attributes only
├── CodeGeneration/
│ └── ResultToIResultExtensionGenerator.cs # Generates extension methods only
├── Orchestration/
│ └── ResultToIResultOrchestrator.cs # Coordinates pipeline only
├── ResultToIResultConfig.cs # Configuration model
└── ResultToIResultRefactoredGenerator.cs # Main entry point
| Class | Responsibility | SOLID Principle |
|---|---|---|
GenerateResultExtensionsAttributeGenerator |
Generate [GenerateResultExtensions] attribute |
SRP |
MapToProblemDetailsAttributeGenerator |
Generate [MapToProblemDetails] attribute |
SRP |
ResultToIResultExtensionGenerator |
Generate HTTP extension methods | SRP |
ResultToIResultOrchestrator |
Coordinate generation pipeline | SRP |
ResultToIResultRefactoredGenerator |
Delegate to orchestrator | SRP |
IAttributeGenerator |
Attribute generation contract | ISP |
ICodeGenerator |
Code generation contract | ISP |
IOrchestrator |
Orchestration contract | ISP |
graph TD
A[ResultToIResultRefactoredGenerator] --> B[ResultToIResultOrchestrator]
B --> C[GenerateResultExtensionsAttributeGenerator]
B --> D[MapToProblemDetailsAttributeGenerator]
B --> E[ResultToIResultExtensionGenerator]
C --> F[GenerateResultExtensionsAttribute.g.cs]
D --> G[MapToProblemDetailsAttribute.g.cs]
E --> H[ResultToIResultExtensions.g.cs]
- Attribute Generation: Attributes generated immediately for use in user code
- Code Generation: Extension methods generated based on compilation analysis
- Single Execution: Each file generated once per compilation
- Clean Output: No duplicate classes or attributes
For Developers
- Zero Duplicate Errors: No more CS0101 or CS0579 compilation errors
- Faster Compilation: Single execution instead of multiple generators
- Cleaner Generated Code: Consistent patterns and proper separation
- Easier Debugging: Clear separation makes issues easier to identify
For Maintainers
- Maintainable Code: Each class has a single, clear responsibility
- Extensible Design: New generators can be added without modifying existing code
- Testable Components: Each generator can be tested in isolation
- Clear Documentation: Well-defined interfaces and responsibilities
For Users
- Reliable Generation: Consistent, error-free code generation
- Better Performance: Faster compilation times
- Smaller Packages: Reduced package size without losing functionality
- Future-Proof: Architecture designed for long-term maintainability
v1.9.4 Package Contents
REslava.Result.SourceGenerators.1.9.4.nupkg/
├── analyzers/
│ └── dotnet/
│ └── cs/
│ ├── REslava.Result.SourceGenerators.dll # Main generator
│ └── REslava.Result.SourceGenerators.Core.dll # Core infrastructure
├── content/
│ └── MapToProblemDetailsAttribute.cs # Content file
└── build/
└── REslava.Result.SourceGenerators.props # Build integration
Package Configuration
<PropertyGroup>
<DevelopmentDependency>true</DevelopmentDependency>
<SuppressDependenciesWhenPacking>true</SuppressDependenciesWhenPacking>
<IsRoslynComponent>true</IsRoslynComponent>
</PropertyGroup>
<ItemGroup>
<None Include="$(OutputPath)\$(AssemblyName).dll"
Pack="true"
PackagePath="analyzers/dotnet/cs"
Visible="false" />
</ItemGroup>
Unit Testing
[Test]
public void GenerateResultExtensionsAttributeGenerator_ShouldCreateValidAttribute()
{
var generator = new GenerateResultExtensionsAttributeGenerator();
var result = generator.GenerateAttribute();
Assert.That(result.ToString(), Contains.String("[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false)]"));
Assert.That(result.ToString(), Contains.String("public sealed class GenerateResultExtensionsAttribute"));
}
Integration Testing
[Test]
public void Orchestrator_ShouldGenerateAllFiles()
{
var orchestrator = new ResultToIResultOrchestrator();
var context = new TestIncrementalGeneratorInitializationContext();
orchestrator.Initialize(context);
Assert.That(context.GeneratedFiles, Contains.Item("GenerateResultExtensionsAttribute.g.cs"));
Assert.That(context.GeneratedFiles, Contains.Item("MapToProblemDetailsAttribute.g.cs"));
Assert.That(context.GeneratedFiles, Contains.Item("ResultToIResultExtensions.g.cs"));
}
Breaking Changes
- None - v1.9.4 is a drop-in replacement with zero breaking changes
Internal Changes
- Removed: Old duplicate generator classes
- Added: New SOLID-based architecture
- Improved: Package configuration and structure
Migration Steps
<!-- Old version -->
<PackageReference Include="REslava.Result.SourceGenerators" Version="1.9.3" />
<!-- New version -->
<PackageReference Include="REslava.Result.SourceGenerators" Version="1.9.4" />
dotnet nuget locals http-cache --clear
dotnet clean && dotnet build
Adding New Generators
// 1. Create new generator implementing ICodeGenerator
public class CustomExtensionGenerator : ICodeGenerator
{
public SourceText GenerateCode(Compilation compilation, object config)
{
// Custom generation logic
}
}
// 2. Register in orchestrator
public class CustomOrchestrator : IOrchestrator
{
public void Initialize(IncrementalGeneratorInitializationContext context)
{
var customGenerator = new CustomExtensionGenerator();
// Register custom generator
}
}
Adding New Attributes
// 1. Create new attribute generator
public class CustomAttributeGenerator : IAttributeGenerator
{
public SourceText GenerateAttribute()
{
// Custom attribute generation
}
}
// 2. Register in orchestrator
context.RegisterPostInitializationOutput(ctx =>
{
ctx.AddSource("CustomAttribute.g.cs", customGenerator.GenerateAttribute());
});
The SOLID architecture in v1.9.4 represents a fundamental improvement in the REslava.Result.SourceGenerators library. By following SOLID principles, we've eliminated the duplicate generation issues that plagued previous versions while creating a maintainable, extensible codebase that will serve as a foundation for future enhancements.
The architecture is now production-ready, testable, and designed for long-term maintainability. 🚀