What are Decorators?
Decorators are classes that:- Hook into existing template output
- Add, modify, or remove code sections
- Execute before the template writes its output
- Chain together for complex modifications
- Maintain separation of concerns between modules
When to Use Decorators
Use decorators when you need to:- Add functionality to templates from another module
- Inject cross-cutting concerns (logging, validation, etc.)
- Customize generated code based on stereotypes
- Avoid modifying or duplicating existing templates
- Keep module dependencies clean and maintainable
Decorator Architecture
A decorator consists of:- Decorator Contract Interface: Defines hooks that templates expose
- Decorator Implementation: Your code that modifies the template
- Decorator Registration: Registers the decorator with the Software Factory
Creating Your First Decorator
- Name:
MyDecorator - Decorator Contract: Select or create interface
using Intent.Modules.Common.CSharp.Builder;
using Intent.Modules.Common.Templates;
public interface IServiceDecorator : IHasDecorators<IServiceDecorator>
{
CSharpClass Class { get; }
CSharpConstructor Constructor { get; }
}
Decorators/MyDecorator.cs (your implementation)Decorators/MyDecoratorRegistration.cs (generated)using Intent.Engine;
using Intent.Modules.Common.CSharp.Builder;
using Intent.Modules.Common.Templates;
namespace MyModule.Decorators
{
public class MyDecorator : IServiceDecorator
{
public const string DecoratorId = "MyModule.MyDecorator";
private readonly IServiceDecorator _template;
private readonly IApplication _application;
public MyDecorator(
IServiceDecorator template,
IApplication application)
{
_template = template;
_application = application;
// Priority determines execution order (lower = earlier)
Priority = 100;
}
public int Priority { get; }
public void BeforeTemplateExecution()
{
// Add logging to the service
_template.Class.AddField(
"ILogger",
"_logger",
field => field.PrivateReadOnly());
_template.Constructor.AddParameter(
"ILogger",
"logger",
param => param.IntroduceField((field, p) =>
{
field.PrivateReadOnly();
}));
// Add logging to all methods
foreach (var method in _template.Class.Methods)
{
method.AddStatement(
$"_logger.LogInformation(\"Executing {method.Name}\");",
stmt => stmt.BeforeCurrentStatement());
}
}
public IServiceDecorator GetDecorator<T>() where T : IServiceDecorator
{
return _template.GetDecorator<T>();
}
public IEnumerable<IServiceDecorator> GetDecorators()
{
return _template.GetDecorators();
}
}
}
using Intent.Modules.Common.CSharp.Builder;
using Intent.Modules.Common.Templates;
public partial class ServiceTemplate : CSharpTemplateBase<ClassModel>,
IServiceDecorator
{
private CSharpClass _class;
private CSharpConstructor _constructor;
public CSharpClass Class => _class;
public CSharpConstructor Constructor => _constructor;
public override void BeforeTemplateExecution()
{
base.BeforeTemplateExecution();
// Build the class structure
_class = new CSharpClass(ClassName);
_constructor = _class.AddConstructor();
// Allow decorators to modify
ExecuteDecorators();
}
public override string TransformText()
{
return _class.ToString();
}
}
Decorator Patterns
Adding Fields and Dependencies
Modifying Methods
Conditional Decoration
Apply decorations based on metadata:Adding Attributes
Implementing Interfaces
Advanced Decorator Techniques
Chaining Decorators
Decorators can call other decorators:Accessing Other Templates
Using CSharp Builder API
Leverage the full CSharp Builder for complex modifications:Accessing Metadata in Decorators
Decorator Contract Interfaces
Standard Contract Pattern
Extended Contract with Methods
Decorator Registration
Generated registration class:Decorators/MyDecoratorRegistration.cs
Custom Registration Logic
For conditional decorator application:Testing Decorators
Unit Testing
Integration Testing
Create test application and verify decorated output:Best Practices
Keep Decorators Focused
Use Appropriate Priorities
Handle Nulls Gracefully
Document Decorator Behavior
Common Use Cases
Adding Logging
Inject logging infrastructure and statements into templates
Validation
Add input validation to methods and properties
Caching
Implement caching around expensive operations
Security
Add authorization checks and attributes
Auditing
Track creation and modification metadata
Error Handling
Wrap methods in try-catch blocks
Troubleshooting
Decorator not executing
Decorator not executing
- Verify template implements decorator contract interface
- Check decorator is registered in .imodspec
- Ensure template calls
ExecuteDecorators() - Verify
BeforeTemplateExecution()is called
Changes not appearing in output
Changes not appearing in output
- Check decorator priority order
- Verify modifications are made before
TransformText() - Ensure builder objects are being modified, not recreated
- Check for exceptions in decorator code
Conflicts between decorators
Conflicts between decorators
- Review decorator priorities
- Check for overlapping modifications
- Use decorator chaining appropriately
- Consider consolidating related decorators
Next Steps
Factory Extensions
Hook into Software Factory lifecycle
Creating Templates
Learn template development basics
Testing Modules
Test your decorators and modules
Module Builder
Back to Module Builder overview