Skip to main content

Overview

Decorator execution in Intent Architect follows a well-defined lifecycle that integrates with the Software Factory’s template execution process. Understanding this lifecycle is essential for building decorators that interact correctly with templates and other decorators.

Execution Flow

Decorators are executed as part of the template generation process:

IDecoratorExecutionHooks

The IDecoratorExecutionHooks interface allows decorators to execute logic before template execution begins.
namespace Intent.Modules.Common
{
    public interface IDecoratorExecutionHooks
    {
        void BeforeTemplateExecution();
    }
}

BeforeTemplateExecution

Called for all decorators before any template’s TransformText method is invoked.
BeforeTemplateExecution
void
Hook method called during the BeforeTemplateExecution phase of the Software Factory execution lifecycle. Use this to perform initialization or preparation work before template generation begins.

Implementing Execution Hooks

using Intent.Engine;
using Intent.Modules.Common;
using Intent.Modules.Common.Templates;

public class MyDecoratorWithHooks : DecoratorBase, 
    IMyDecorator, 
    IDecoratorExecutionHooks
{
    private readonly IMyTemplate _template;
    private readonly IApplication _application;
    private bool _isInitialized;

    public MyDecoratorWithHooks(IMyTemplate template, IApplication application)
    {
        _template = template;
        _application = application;
    }

    public void BeforeTemplateExecution()
    {
        // Perform initialization work
        // This runs once before all templates execute
        if (!_isInitialized)
        {
            PrepareResources();
            _isInitialized = true;
        }
    }

    private void PrepareResources()
    {
        // Load data, validate configuration, etc.
    }

    public string GetDecoratorContent()
    {
        // This is called during template execution
        return "// Decorator content";
    }
}

DecoratorExecutionHooksFactoryExtension

The framework includes a built-in factory extension that orchestrates decorator execution hooks.
namespace Intent.Modules.Common.Plugins
{
    public class DecoratorExecutionHooksFactoryExtension : FactoryExtensionBase
    {
        public override string Id => "Intent.Common.DecoratorExecutionHooks";
        public override int Order => -1;

        protected override void OnBeforeTemplateExecution(IApplication application)
        {
            application.Projects
                .SelectMany(x => x.TemplateInstances)
                .Where(HasDecorators)
                .SelectMany(GetDecorators)
                .OfType<IDecoratorExecutionHooks>()
                .ToList()
                .ForEach(x => x.BeforeTemplateExecution());

            base.OnBeforeTemplateExecution(application);
        }
    }
}
This factory extension has an Order of -1, ensuring it runs early in the execution lifecycle.

Decorator Dispatcher

The DecoratorDispatcher<TDecorator> class manages decorator invocation within templates.
namespace Intent.Modules.Common.Templates
{
    public class DecoratorDispatcher<TDecorator> 
        where TDecorator : ITemplateDecorator
    {
        public DecoratorDispatcher(Func<IEnumerable<TDecorator>> loadDecorators);
        
        public string Dispatch(Func<TDecorator, string> doIt);
        public TResult Dispatch<TResult>(Func<IEnumerable<TDecorator>, TResult> doIt);
        public void Dispatch(Action<TDecorator> doIt);
        public IEnumerable<TDecorator> GetDecorators();
    }
}

Methods

Dispatch
string
Executes a function on each decorator and aggregates string results.
doIt
Func<TDecorator, string>
Function to execute on each decorator that returns a string.
Dispatch<TResult>
TResult
Executes a function with all decorators and returns a result.
doIt
Func<IEnumerable<TDecorator>, TResult>
Function to execute with the collection of decorators.
Dispatch
void
Executes an action on each decorator.
doIt
Action<TDecorator>
Action to execute on each decorator.
GetDecorators
IEnumerable<TDecorator>
Returns all decorators, loading them if not already loaded.

Using DecoratorDispatcher

public class MyTemplate : CSharpTemplateBase<IClass>, 
    IHasDecorators<IMyDecorator>
{
    private readonly DecoratorDispatcher<IMyDecorator> _decorators;

    public MyTemplate(IOutputTarget outputTarget, IClass model) 
        : base("MyTemplate", outputTarget, model)
    {
        _decorators = new DecoratorDispatcher<IMyDecorator>(
            () => GetDecorators());
    }

    public override string TransformText()
    {
        var usings = _decorators.Dispatch(d => d.GetUsings());
        var methods = _decorators.Dispatch(d => d.GetMethods());
        
        // Perform custom logic with all decorators
        _decorators.Dispatch(decorators => 
        {
            foreach (var decorator in decorators)
            {
                decorator.Initialize();
            }
            return 0;
        });
        
        return $@"
{usings}

namespace {Namespace}
{{
    public class {ClassName}
    {{
{methods}
    }}
}}
";
    }
}

Execution Order

Decorators execute in priority order (lowest to highest):
public IEnumerable<IMyDecorator> GetDecorators()
{
    return ExecutionContext.Decorators
        .OfType<IMyDecorator>()
        .Where(x => /* filter for this template */)
        .OrderBy(x => x.Priority);  // Critical for correct order
}
Always sort decorators by Priority when returning them from GetDecorators(). Failure to do so may result in unpredictable execution order.

Template Decorator Extensions

The framework provides helper methods for aggregating decorator outputs:
namespace Intent.Modules.Common.Templates
{
    public static class TemplateDecoratorExtensions
    {
        public static string Aggregate<TDecorator>(
            this IEnumerable<TDecorator> decorators, 
            Func<TDecorator, string> property)
            where TDecorator : ITemplateDecorator;
            
        public static string Aggregate<TDecorator>(
            this IEnumerable<TDecorator> decorators, 
            Func<TDecorator, string[]> property)
            where TDecorator : ITemplateDecorator;
    }
}

Usage Example

// Aggregate single string values
var usings = decorators.Aggregate(d => d.GetUsings());

// Aggregate string arrays
var methods = decorators.Aggregate(d => d.GetMethods());

Best Practices

Initialize Early

Use BeforeTemplateExecution for expensive initialization that should happen once

Lazy Loading

DecoratorDispatcher loads decorators lazily on first use

Order Matters

Always sort decorators by Priority in GetDecorators()

Avoid Side Effects

Decorator methods should be idempotent when possible

Common Patterns

Content Aggregation

var content = _decorators.Dispatch(d => d.GetContent());

Conditional Execution

var decorators = _decorators.GetDecorators()
    .Where(d => d.ShouldApply())
    .ToList();

Chained Operations

_decorators.Dispatch(d => 
{
    d.Initialize();
    d.Process();
});

Build docs developers (and LLMs) love