Skip to main content

Overview

IOutputTarget represents the destination where a template’s generated output will be written. It encapsulates information about the project structure, output paths, and provides access to the Software Factory execution context. Output targets are determined by designers with output targeting capabilities (e.g., Visual Studio, Folder Structure) and are provided to templates during instantiation.

Purpose

The output target serves several key purposes:
  • Location Configuration - Defines where template outputs are written
  • Project Context - Provides information about the target project/application
  • Role Fulfillment - Enables templates to be discovered by role
  • Accessibility - Controls which templates can access other templates

Key Properties

While IOutputTarget is defined in the Intent.Engine assembly, templates commonly interact with it through these members:
ExecutionContext
ISoftwareFactoryExecutionContext
Provides access to application-wide services including:
  • Template discovery
  • Metadata management
  • File system operations
  • Event bus
  • Application configuration

Common Usage Patterns

In Template Constructors

The output target is passed to every template during construction:
public class MyEntityTemplate : IntentTemplateBase<ClassModel>
{
    public const string TemplateId = "MyModule.EntityTemplate";

    public MyEntityTemplate(IOutputTarget outputTarget, ClassModel model)
        : base(TemplateId, outputTarget, model)
    {
        // outputTarget is automatically stored in the base class
        // and available via the OutputTarget property
    }
}

Accessing the Output Target

From within a template, access the output target through the OutputTarget property:
public override string TransformText()
{
    // Access execution context
    var metadataManager = OutputTarget.ExecutionContext.MetadataManager;
    
    // Use for template discovery
    var relatedTemplate = GetTemplate<IOtherTemplate>("OtherTemplateId");
    
    return GenerateCode();
}

Template Registration

Output targets are provided to templates via factory functions in registrations:
public class EntityTemplateRegistration : FilePerModelTemplateRegistration<ClassModel>
{
    public override ITemplate CreateTemplateInstance(IOutputTarget outputTarget, ClassModel model)
    {
        // outputTarget is provided by the Software Factory
        return new EntityTemplate(outputTarget, model);
    }
    
    // ... other members
}

Output Path Configuration

GetTemplateFileConfig

Templates configure their output path through GetTemplateFileConfig():
public override ITemplateFileConfig GetTemplateFileConfig()
{
    return new TemplateFileConfig(
        fileName: $"{Model.Name}",
        fileExtension: "cs"
    );
}
The final output location combines:
  1. The output target’s base path (configured in the designer)
  2. Any relative path specified in the file config
  3. The file name and extension

Custom Output Paths

Specify relative paths for organizing outputs:
public override ITemplateFileConfig GetTemplateFileConfig()
{
    return new TemplateFileConfig(
        fileName: $"{Model.Name}",
        fileExtension: "cs",
        relativeLocation: "Entities"
    );
}

Template Accessibility

Output targets control template accessibility - which templates can discover other templates.

Accessible Templates

By default, templates search for other templates accessible to the same output target:
// Finds templates accessible to this template's OutputTarget
var template = GetTemplate<IMyTemplate>("MyTemplateId");

Cross-Project References

To find templates in different output targets, use template dependencies with explicit accessibility:
AddTemplateDependency(
    TemplateDependency.OnTemplate("OtherTemplateId", accessibleTo: OutputTarget)
);

Output Target Extensions

Common extension methods for working with output targets:

Application Name

string appName = OutputTarget.ApplicationName();
Returns the application name configured in the designer.

Template Output ID

if (OutputTarget.TryGetTemplateOutputId(TemplateId, out var outputId))
{
    // Use outputId
}
Retrieves a unique identifier for this template’s output in this output target.

Advanced Scenarios

Multiple Output Targets

Some applications have multiple output targets (e.g., different projects in a solution). Templates can interact across these boundaries:
public class ServiceProxyTemplate : IntentTemplateBase<ServiceModel>
{
    public ServiceProxyTemplate(IOutputTarget outputTarget, ServiceModel model)
        : base(TemplateId, outputTarget, model)
    {
        // Add dependency on service in different output target
        AddTemplateDependency(
            TemplateDependency.OnModel(
                "ServiceImplementation.Template",
                model,
                accessibleTo: outputTarget
            )
        );
    }
}

Custom Output Target Selection

In module builders, you can specify which output target a template should use:
// In a Factory Extension
public class CustomFactoryExtension : FactoryExtensionBase
{
    public override void OnAfterTemplateRegistration(IApplication application)
    {
        var outputTarget = application.OutputTargets
            .FirstOrDefault(x => x.Name == "MySpecificProject");
            
        if (outputTarget != null)
        {
            // Register template for specific output target
            application.RegisterTemplate(
                "CustomTemplate",
                () => new CustomTemplate(outputTarget)
            );
        }
    }
}

FileMetadata and Output Correlation

The output target works with file metadata to track outputs across executions:
public virtual string? GetCorrelationId()
{
    return OutputTarget.TryGetTemplateOutputId(Id, out var templateOutputId)
        ? $"{Id}#{templateOutputId}"
        : Id;
}
This enables:
  • Tracking file renames and moves
  • Detecting deleted files
  • Managing file merging and soft deletion

Relationship to ISoftwareFactoryExecutionContext

The output target provides access to the execution context:
// These are equivalent:
var context1 = OutputTarget.ExecutionContext;
var context2 = ExecutionContext; // Property from IntentTemplateBase

// Common execution context operations:
context1.FindTemplateInstance<IMyTemplate>("TemplateId");
context1.MetadataManager;
context1.EventDispatcher;
context1.Settings;

Best Practices

Don't Store Output Target

The output target is already stored in IntentTemplateBase. Access it via the OutputTarget property.

Use Relative Paths

Use relative paths in GetTemplateFileConfig() to organize outputs within the output target.

Consider Accessibility

Be mindful of template accessibility when searching for templates across different projects.

Leverage Extensions

Use output target extension methods for common operations like getting the application name.

Example: Multi-Project Template

This example shows a template that generates a DTO in a contracts project based on an entity in a domain project:
public class DtoTemplate : IntentTemplateBase<ClassModel>
{
    public const string TemplateId = "MyModule.DtoTemplate";

    public DtoTemplate(IOutputTarget outputTarget, ClassModel model)
        : base(TemplateId, outputTarget, model)
    {
        // Add dependency on entity template in different output target
        AddTemplateDependency(
            TemplateDependency.OnModel(
                "MyModule.EntityTemplate",
                model,
                accessibleTo: outputTarget
            )
        );
    }

    public override ITemplateFileConfig GetTemplateFileConfig()
    {
        return new TemplateFileConfig(
            fileName: $"{Model.Name}Dto",
            fileExtension: "cs",
            relativeLocation: "DTOs"
        );
    }

    public override string TransformText()
    {
        var entityTemplate = GetTemplate<IEntityTemplate>(
            TemplateDependency.OnModel("MyModule.EntityTemplate", Model)
        );

        return $@"
namespace {OutputTarget.ApplicationName()}.DTOs
{{
    public class {Model.Name}Dto
    {{
        // Map from entity: {entityTemplate?.ClassName}
    }}
}}";
    }
}

IntentTemplateBase

Base classes using IOutputTarget

Template Registration

How output targets are provided to templates

Template Dependencies

Cross-output-target template dependencies

Build docs developers (and LLMs) love