Skip to main content

Metadata

Metadata is the foundation of Intent Architect’s code generation system. It represents the models, configurations, and design-time information that templates use to generate code.

What is Metadata?

Metadata in Intent Architect includes:
  • Designer Models - Domain entities, services, DTOs, etc.
  • Stereotypes - Additional annotations and configurations
  • Settings - Module and application-level configurations
  • Relationships - Associations between model elements
Think of metadata as the “blueprint” that describes what code should be generated.

Metadata Sources

Metadata comes from three primary sources:
1

Designers

Visual designers where users create domain models, services, and other architectural elements
2

Module Configuration

Module .imodspec files that define metadata packages, stereotypes, and settings
3

Application Settings

User-configured settings for modules and templates

Metadata Packages

Modules can install metadata packages that extend designers with new elements and stereotypes:
<metadata>
  <install target="Module Builder" 
           src="Intent.Metadata/Module Builder/Intent.Application.MediatR/Intent.Application.MediatR.pkg.config" 
           externalReference="e92495db-1b26-4389-bf06-cc1b375520b1" />
</metadata>
target
string
required
Which designer to install into (e.g., “Services”, “Domain”, “Module Builder”)
src
string
required
Path to the metadata package configuration file
externalReference
string
required
Unique GUID identifier for the metadata package

Working with Metadata in Templates

Templates access metadata through strongly-typed API models:
public class CommandHandlerTemplate : CSharpTemplateBase<CommandModel, CommandHandlerDecorator>
{
    public CommandHandlerTemplate(IOutputTarget outputTarget, CommandModel model) 
        : base(TemplateId, outputTarget, model)
    {
        // Model contains metadata about the command
        var commandName = model.Name;
        var returnType = model.TypeReference;
        var parameters = model.Parameters;
    }
}

Accessing Model Properties

The CommandModel is a strongly-typed wrapper around the metadata:
// Basic properties
string name = model.Name;
string id = model.Id;

// Type information
if (model.TypeReference != null)
{
    string typeName = GetTypeName(model.TypeReference);
}

// Parameters/Properties
foreach (var parameter in model.Parameters)
{
    string paramName = parameter.Name;
    string paramType = GetTypeName(parameter.TypeReference);
    bool isCollection = parameter.TypeReference.IsCollection;
}

// Stereotypes
if (model.HasStereotype("Authorization"))
{
    var authStereotype = model.GetStereotype("Authorization");
    string roles = authStereotype.GetProperty<string>("Roles");
}

API Models

Intent Architect generates strongly-typed API models for working with metadata. These are typically found in modules like Intent.Modelers.Services.Api:
using Intent.Metadata.Models;

public class CommandModel : IMetadataModel
{
    public CommandModel(IElement element)
    {
        Element = element;
    }

    public IElement Element { get; }
    public string Id => Element.Id;
    public string Name => Element.Name;
    public ITypeReference TypeReference => Element.TypeReference;
    
    public IList<ParameterModel> Parameters => Element.ChildElements
        .Where(x => x.SpecializationType == "Parameter")
        .Select(x => new ParameterModel(x))
        .ToList();
        
    public bool HasStereotype(string stereotypeName)
    {
        return Element.HasStereotype(stereotypeName);
    }
    
    public IStereotype GetStereotype(string stereotypeName)
    {
        return Element.GetStereotype(stereotypeName);
    }
}

Stereotypes

Stereotypes are additional metadata annotations that provide extra configuration:
// Check if stereotype exists
if (model.HasStereotype("Secured"))
{
    var stereotype = model.GetStereotype("Secured");
    
    // Get stereotype properties
    string roles = stereotype.GetProperty<string>("Roles");
    bool requireAuthentication = stereotype.GetProperty<bool>("Require Authentication", true);
    
    // Use in code generation
    @class.AddAttribute($"[Authorize(Roles = \"{roles}\")]");
}

Common Stereotypes

  • Secured - Marks operations requiring authentication
  • Authorize - Specifies authorization roles/policies
  • Anonymous - Allows anonymous access

IMetadataManager

The IMetadataManager provides access to all metadata in the application:
public class CommandHandlerTemplateRegistration : FilePerModelTemplateRegistration<CommandModel>
{
    private readonly IMetadataManager _metadataManager;

    public CommandHandlerTemplateRegistration(IMetadataManager metadataManager)
    {
        _metadataManager = metadataManager;
    }

    public override IEnumerable<CommandModel> GetModels(IApplication application)
    {
        // Query metadata for all commands
        return _metadataManager.Services(application).GetCommandModels();
    }
}

Common Queries

// Get all domain entities
var entities = _metadataManager.Domain(application).GetDomainModels();

// Get all services
var services = _metadataManager.Services(application).GetServiceModels();

// Get all DTOs
var dtos = _metadataManager.Services(application).GetDTOModels();

// Get all commands (CQRS)
var commands = _metadataManager.Services(application).GetCommandModels();

// Get all queries (CQRS)
var queries = _metadataManager.Services(application).GetQueryModels();

Type Resolution

Templates can resolve types from metadata to template-generated class names:
// Get type name from another template
string dtoTypeName = GetTypeName("Application.Contract.Dto", model.TypeReference);

// With full type reference resolution
string entityTypeName = GetTypeName(model.TypeReference);

// Check if nullable
string typeName = model.TypeReference.IsNullable 
    ? $"{GetTypeName(model.TypeReference)}?"
    : GetTypeName(model.TypeReference);

// Handle collections
if (model.TypeReference.IsCollection)
{
    string itemType = GetTypeName(model.TypeReference.Element);
    string collectionType = $"List<{itemType}>";
}
Type resolution connects metadata models to the actual generated C# types, enabling cross-template references.

Type Sources

Templates register type sources to enable type resolution:
internal static void Configure(ICSharpFileBuilderTemplate template, CommandModel model)
{
    // Register where to look for types
    template.AddTypeSource(TemplateRoles.Application.Command);
    template.AddTypeSource(TemplateRoles.Domain.Enum);
    template.AddTypeSource(TemplateRoles.Application.Contracts.Dto);
    template.AddTypeSource(TemplateRoles.Application.Contracts.Enum);
    
    // Now GetTypeName can resolve types from these templates
}

Metadata in .imodspec Files

Modules define metadata requirements in their specification:
<package>
  <id>Intent.Application.Dtos</id>
  
  <!-- Metadata this module depends on -->
  <dependencies>
    <dependency id="Intent.Common.Types" version="3.4.0" />
    <dependency id="Intent.Modelers.Services" version="4.0.0" />
  </dependencies>
  
  <!-- Metadata this module provides -->
  <metadata>
    <install target="Module Builder" 
             src="Intent.Metadata/Module Builder/Intent.Application.Dtos/Intent.Application.Dtos.pkg.config" />
  </metadata>
  
  <!-- Settings that become metadata -->
  <moduleSettings>
    <group id="aac8a446-047c-468b-809b-ca28989b558b" title="DTO Settings">
      <settings>
        <setting id="1d1a8aae-d3f7-42ee-84b3-cf62d7da4d1e" 
                 title="Type" 
                 type="select">
          <defaultValue>class</defaultValue>
          <options>
            <option value="class" description="class" />
            <option value="record" description="record" />
          </options>
        </setting>
      </settings>
    </group>
  </moduleSettings>
</package>

Accessing Module Settings

Templates can read module settings as metadata:
// Access application-level settings
var cqrsSettings = ExecutionContext.Settings.GetCQRSSettings();
bool consolidateFiles = cqrsSettings.ConsolidateCommandQueryAssociatedFilesIntoSingleFile();

// Access module-specific settings
var dtoSettings = ExecutionContext.Settings.GetDtoSettings();
string dtoType = dtoSettings.Type(); // "class" or "record"
string setterAccess = dtoSettings.PropertySetterAccessibility();

// Use settings in generation
if (dtoType == "record")
{
    @class.WithRecordType();
}

Metadata Validation

Intent Architect validates metadata before code generation:

Required Properties

Ensures all required properties are set

Type Compatibility

Validates type references are valid

Stereotype Constraints

Checks stereotype combinations are valid

Circular References

Detects circular dependencies

Best Practices

Always use API wrapper classes like CommandModel instead of working with raw IElement instances.
Always check if TypeReference, stereotypes, and properties exist before accessing them.
if (model.TypeReference?.Element != null)
{
    var typeName = GetTypeName(model.TypeReference);
}
Ensure templates register all necessary type sources for proper type resolution.
Don’t force all models to have every property. Use stereotypes for optional configurations.

Real-World Example

Here’s how metadata flows through a complete code generation scenario:
1

User Creates Model

User creates a CreateOrder command in the Services designer with parameters ProductId and Quantity
2

Metadata is Stored

Intent Architect stores this as metadata with properties, type references, and any applied stereotypes
3

Template Queries Metadata

CommandHandlerTemplateRegistration queries for all command models via IMetadataManager
4

Template Generates Code

CommandHandlerTemplate receives the CommandModel and generates:
public class CreateOrderHandler : IRequestHandler<CreateOrderCommand>
{
    public async Task Handle(CreateOrderCommand request, CancellationToken cancellationToken)
    {
        // Generated from metadata
        var productId = request.ProductId;
        var quantity = request.Quantity;
    }
}

Next Steps

Templates

Learn how templates consume metadata

Dependencies

Understand module dependencies

Build docs developers (and LLMs) love