Skip to main content

Overview

The Intent Architect Software Factory follows a well-defined execution lifecycle with multiple phases. Each phase serves a specific purpose, and factory extensions can hook into these phases to perform custom operations.

Lifecycle Phases

The Software Factory execution consists of the following phases, executed in order:

ExecutionLifeCycleSteps

The framework defines constants for each lifecycle step:
public static class ExecutionLifeCycleSteps
{
    public const string Start = "Start";
    public const string BeforeMetadataLoad = "BeforeMetadataLoad";
    public const string AfterMetadataLoad = "AfterMetadataLoad";
    public const string BeforeTemplateRegistrations = "BeforeTemplateRegistrations";
    public const string AfterTemplateRegistrations = "AfterTemplateRegistrations";
    public const string BeforeTemplateExecution = "BeforeTemplateExecution";
    public const string AfterTemplateExecution = "AfterTemplateExecution";
    public const string BeforeCommitChanges = "BeforeCommitChanges";
    public const string AfterCommitChanges = "AfterCommitChanges";
}

Phase Descriptions

Start

Start

The first phase of Software Factory execution. Use this for initialization logic that must run before any other operations.Available Data:
  • Application configuration
  • Module metadata
Not Available:
  • Designer metadata
  • Template instances

BeforeMetadataLoad

BeforeMetadataLoad

Called before the Software Factory loads metadata from visual designers.Available Data:
  • Application configuration
  • Module metadata
Common Uses:
  • Prepare metadata providers
  • Configure metadata loading

AfterMetadataLoad

AfterMetadataLoad

Called after all metadata has been loaded from designers. This is the first phase where you can access domain models, services, and other designer data.Available Data:
  • Application configuration
  • Module metadata
  • Designer metadata (models, elements)
Common Uses:
  • Validate metadata
  • Build metadata indexes
  • Precompute derived metadata

BeforeTemplateRegistrations

BeforeTemplateRegistrations

Called before templates are registered with the Software Factory.Available Data:
  • Application configuration
  • Module metadata
  • Designer metadata
Common Uses:
  • Prepare template dependencies
  • Configure template factories

AfterTemplateRegistrations

AfterTemplateRegistrations

Called after all templates have been registered. Template instances are now available for inspection and modification.Available Data:
  • Application configuration
  • Module metadata
  • Designer metadata
  • Template instances
Common Uses:
  • Validate template configuration
  • Wire up template dependencies
  • Initialize file builders

BeforeTemplateExecution

BeforeTemplateExecution

Called before templates begin generating output. This is where decorator execution hooks run.Available Data:
  • Application configuration
  • Module metadata
  • Designer metadata
  • Template instances
  • Decorators
Common Uses:
  • Initialize decorators
  • Prepare generation context
  • Set up output directories

AfterTemplateExecution

AfterTemplateExecution

Called after all templates have executed. Generated content is available but not yet written to disk.Available Data:
  • Application configuration
  • Module metadata
  • Designer metadata
  • Template instances
  • Generated outputs (in memory)
Common Uses:
  • Validate generated code
  • Post-process template outputs
  • Generate summary reports

BeforeCommitChanges

BeforeCommitChanges

Called before changes are written to disk. Last chance to modify or cancel file operations.Available Data:
  • Application configuration
  • Module metadata
  • Designer metadata
  • Template instances
  • Generated outputs (in memory)
  • Pending file changes
Common Uses:
  • Review file changes
  • Apply additional transformations
  • Cancel specific file writes

AfterCommitChanges

AfterCommitChanges

Called after all changes have been written to disk. This is the final phase of Software Factory execution.Available Data:
  • Application configuration
  • Module metadata
  • Designer metadata
  • Template instances
  • Written files
Common Uses:
  • Run external tools (formatters, linters)
  • Compile generated code
  • Generate documentation
  • Create deployment artifacts

IExecutionLifeCycle Interface

namespace Intent.Plugins.FactoryExtensions
{
    public interface IExecutionLifeCycle
    {
        void OnStep(IApplication application, string step);
    }
}
OnStep
void
Called for each phase of the execution lifecycle.
application
IApplication
The application instance containing all execution context.
step
string
The current execution phase (one of the ExecutionLifeCycleSteps constants).

Implementation Example

using Intent.Engine;
using Intent.Modules.Common.Plugins;
using Intent.Plugins.FactoryExtensions;

public class MyLifecycleExtension : FactoryExtensionBase
{
    public override string Id => "MyModule.LifecycleExtension";

    public override void OnStep(IApplication application, string step)
    {
        base.OnStep(application, step);
        
        // Log every step
        Console.WriteLine($"Executing step: {step}");
        
        // Handle specific steps
        switch (step)
        {
            case ExecutionLifeCycleSteps.Start:
                InitializeExtension();
                break;
            case ExecutionLifeCycleSteps.AfterMetadataLoad:
                ValidateMetadata(application);
                break;
            case ExecutionLifeCycleSteps.AfterCommitChanges:
                Cleanup();
                break;
        }
    }

    protected override void OnAfterTemplateExecution(IApplication application)
    {
        // This is also called automatically after OnStep
        var templateCount = application.OutputTargets
            .SelectMany(x => x.TemplateInstances)
            .Count();
            
        Console.WriteLine($"Executed {templateCount} templates");
    }
}

Execution Order

Multiple factory extensions can hook into the same lifecycle phase. They execute in order based on their Order property:
public class EarlyExtension : FactoryExtensionBase
{
    public override string Id => "MyModule.EarlyExtension";
    public override int Order => -100; // Runs first
}

public class LateExtension : FactoryExtensionBase
{
    public override string Id => "MyModule.LateExtension";
    public override int Order => 100; // Runs last
}
Extensions with the same Order value execute in an undefined sequence. Always use unique order values when execution sequence matters.

Common Patterns

Metadata Validation

protected override void OnAfterMetadataLoad(IApplication application)
{
    var models = application.MetadataManager.GetAll();
    
    foreach (var model in models)
    {
        if (!IsValid(model))
        {
            Logging.Log.Warning($"Invalid model: {model.Name}");
        }
    }
}

Template Initialization

protected override void OnAfterTemplateRegistrations(IApplication application)
{
    var myTemplates = application.OutputTargets
        .SelectMany(x => x.TemplateInstances)
        .OfType<IMyTemplate>();
        
    foreach (var template in myTemplates)
    {
        template.Initialize();
    }
}

Post-Generation Processing

protected override void OnAfterCommitChanges(IApplication application)
{
    var outputPath = application.OutputTargets.First().Location;
    
    // Run formatter
    RunFormatter(outputPath);
    
    // Run tests
    RunTests(outputPath);
}

Best Practices

Choose the Right Phase

Use the earliest phase that has the data you need

Keep Phases Fast

Avoid expensive operations that slow down generation

Handle Errors Gracefully

Don’t throw exceptions that crash the Software Factory

Log Important Events

Use Logging.Log to provide visibility into extension behavior

Build docs developers (and LLMs) love