Skip to main content

Overview

FactoryExtensionBase is an abstract base class that allows you to hook into various phases of the Intent Architect Software Factory execution lifecycle. Use it to perform custom operations during code generation, such as validation, post-processing, file manipulation, or external tool integration.

Class Definition

namespace Intent.Modules.Common.Plugins
{
    public abstract class FactoryExtensionBase : 
        IExecutionLifeCycle, 
        IFactoryExtension, 
        ISupportsConfiguration
    {
        public abstract string Id { get; }
        public virtual int Order { get; set; }
        public virtual void Configure(IDictionary<string, string> settings);
        
        // Lifecycle hooks
        public virtual void OnStep(IApplication application, string step);
        protected virtual void OnStart(IApplication application);
        protected virtual void OnBeforeMetadataLoad(IApplication application);
        protected virtual void OnAfterMetadataLoad(IApplication application);
        protected virtual void OnBeforeTemplateRegistrations(IApplication application);
        protected virtual void OnAfterTemplateRegistrations(IApplication application);
        protected virtual void OnBeforeTemplateExecution(IApplication application);
        protected virtual void OnAfterTemplateExecution(IApplication application);
        protected virtual void OnBeforeCommitChanges(IApplication application);
        protected virtual void OnAfterCommitChanges(IApplication application);
    }
}

Properties

Id
string
required
Unique identifier for the factory extension. Use a namespace pattern like ModuleName.ExtensionName.
Order
int
default:"0"
Controls the execution order of factory extensions. Extensions with lower values execute first.

Configuration

Configure
void
Configures the factory extension with settings from the module configuration.
settings
IDictionary<string, string>
Configuration settings. The default implementation reads the Order property if provided.
public override void Configure(IDictionary<string, string> settings)
{
    base.Configure(settings); // Reads Order from settings
    
    // Add custom configuration
    if (settings.TryGetValue("MyCustomSetting", out var value))
    {
        _customSetting = value;
    }
}

Lifecycle Hooks

Override these methods to execute custom logic at specific points in the Software Factory execution process.

OnStep

OnStep
void
Called for each phase of the Software Factory execution. Override this for logic that should run on every step.
application
IApplication
The application instance containing all projects, templates, and metadata.
step
string
The current execution phase. See ExecutionLifeCycleSteps for possible values.
After OnStep is called, the corresponding step-specific method (e.g., OnBeforeTemplateExecution) is also invoked.

OnStart

OnStart
void
Called during the Start phase of Software Factory execution. This is the first phase, before metadata loading.
application
IApplication
The application instance.

OnBeforeMetadataLoad

OnBeforeMetadataLoad
void
Called before metadata models are loaded from designers.
application
IApplication
The application instance.

OnAfterMetadataLoad

OnAfterMetadataLoad
void
Called after metadata models have been loaded. Metadata is now available for inspection.
application
IApplication
The application instance with loaded metadata.

OnBeforeTemplateRegistrations

OnBeforeTemplateRegistrations
void
Called before templates are registered with the Software Factory.
application
IApplication
The application instance.

OnAfterTemplateRegistrations

OnAfterTemplateRegistrations
void
Called after all templates have been registered. Template instances are now available.
application
IApplication
The application instance with registered templates.

OnBeforeTemplateExecution

OnBeforeTemplateExecution
void
Called before templates begin generating output. Useful for decorator initialization.
application
IApplication
The application instance.

OnAfterTemplateExecution

OnAfterTemplateExecution
void
Called after all templates have executed. Generated content is available but not yet written to disk.
application
IApplication
The application instance with generated template outputs.

OnBeforeCommitChanges

OnBeforeCommitChanges
void
Called before changes are written to disk. Last chance to modify or cancel file operations.
application
IApplication
The application instance.

OnAfterCommitChanges

OnAfterCommitChanges
void
Called after all changes have been written to disk. Useful for post-processing operations like compilation or formatting.
application
IApplication
The application instance.

Examples

Basic Factory Extension

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

public class MyFactoryExtension : FactoryExtensionBase
{
    public override string Id => "MyModule.MyFactoryExtension";
    public override int Order => 0;

    protected override void OnAfterTemplateExecution(IApplication application)
    {
        // Access all generated templates
        var templates = application.OutputTargets
            .SelectMany(x => x.TemplateInstances)
            .ToList();

        // Perform custom processing
        foreach (var template in templates)
        {
            Console.WriteLine($"Generated: {template.Id}");
        }
    }
}

File Builder Integration

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

public class CSharpFileBuilderFactoryExtension : FactoryExtensionBase
{
    public override string Id => "Intent.Common.CSharp.CSharpFileBuilderFactoryExtension";
    public override int Order => int.MinValue; // Run very early

    protected override void OnAfterTemplateRegistrations(IApplication application)
    {
        var templates = application.OutputTargets
            .SelectMany(x => x.TemplateInstances)
            .OfType<ICSharpFileBuilderTemplate>()
            .ToArray();

        foreach (var template in templates)
        {
            // Ensure backwards compatibility
            template.CSharpFile.Template ??= template;
        }
    }
}

Auto-Compile Extension

using System.Diagnostics;
using Intent.Engine;
using Intent.Modules.Common.Plugins;

public class AutoCompileFactoryExtension : FactoryExtensionBase
{
    public override string Id => "Intent.ModuleBuilder.AutoCompile";
    public override int Order => 100; // Run after other extensions

    protected override void OnAfterCommitChanges(IApplication application)
    {
        var location = GetProjectLocation(application);
        if (location == null)
        {
            return;
        }

        ExecuteBuild(location);
    }

    private void ExecuteBuild(string location)
    {
        var process = new Process
        {
            StartInfo = new ProcessStartInfo
            {
                FileName = "dotnet",
                Arguments = "build",
                WorkingDirectory = location,
                RedirectStandardOutput = true,
                UseShellExecute = false
            }
        };

        process.Start();
        var output = process.StandardOutput.ReadToEnd();
        process.WaitForExit();

        if (process.ExitCode == 0)
        {
            Console.WriteLine("Build succeeded");
        }
        else
        {
            Console.WriteLine($"Build failed:\n{output}");
        }
    }

    private string GetProjectLocation(IApplication application)
    {
        return application.OutputTargets
            .FirstOrDefault(x => x.HasTemplateInstances("MyProject"))
            ?.Location;
    }
}

Template Validation

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

public class ValidationFactoryExtension : FactoryExtensionBase
{
    public override string Id => "MyModule.Validation";

    protected override void OnAfterTemplateRegistrations(IApplication application)
    {
        var templates = application.OutputTargets
            .SelectMany(x => x.TemplateInstances)
            .Where(t => t is IValidatableTemplate)
            .Cast<IValidatableTemplate>();

        foreach (var template in templates)
        {
            var errors = template.Validate();
            if (errors.Any())
            {
                foreach (var error in errors)
                {
                    Logging.Log.Warning($"Validation error in {template.Id}: {error}");
                }
            }
        }
    }
}

Best Practices

Use Unique IDs

Follow the pattern ModuleName.ExtensionName for the Id property

Set Appropriate Order

Use negative orders to run early, positive to run late. Default is 0.

Handle Errors Gracefully

Catch exceptions and log errors instead of crashing the Software Factory

Document Side Effects

Clearly document any external operations like file I/O or network calls

Registration

Factory extensions are automatically discovered and registered through assembly scanning. No manual registration is required.

Build docs developers (and LLMs) love