---
title: Register migrations explicitly
editUrl: https://github.com/Cratis/Chronicle/edit/main/Documentation/migrations/explicit-registration.mdx
description: Nominate migration recipes when event generations are registered at the composition root.
---

import { Tabs, TabItem } from '@astrojs/starlight/components';


Explicit registration lets you keep event contracts free of client markers while declaring their generation relationship in the application. Register both endpoint event types with one stable id and adjacent generations, then nominate the migration recipe.

<Tabs syncKey="chronicle-client">
<TabItem label="C#">

```csharp
using Cratis.Chronicle;
using Cratis.Chronicle.Events;
using Cratis.Chronicle.Events.Migrations;

namespace ExplicitMigrationExample
{
    public record Previous(string OldName);
    public record Current(string NewName);

    public class PlainRename
    {
        public void Upcast(IEventMigrationBuilder<Current, Previous> builder) =>
            builder.Properties(_ => _.RenamedFrom(current => current.NewName, previous => previous.OldName));

        public void Downcast(IEventMigrationBuilder<Previous, Current> builder) =>
            builder.Properties(_ => _.RenamedFrom(previous => previous.OldName, current => current.NewName));
    }

    public class TypedRename(IEventTypes eventTypes) : EventTypeMigration<Current, Previous>(eventTypes)
    {
        public override void Upcast(IEventMigrationBuilder<Current, Previous> builder) =>
            builder.Properties(_ => _.RenamedFrom(current => current.NewName, previous => previous.OldName));

        public override void Downcast(IEventMigrationBuilder<Previous, Current> builder) =>
            builder.Properties(_ => _.RenamedFrom(previous => previous.OldName, current => current.NewName));
    }

    public static class Composition
    {
        public static ChronicleOptions CreateOptions()
        {
            var options = new ChronicleOptions();
            options.ExplicitArtifacts
                .RegisterEventType<Previous>(_ => _.WithId("person-registered").Generation(1))
                .RegisterEventType<Current>(_ => _.WithId("person-registered").Generation(2))
                .RegisterMigration<PlainRename>();
            // Register TypedRename instead to use the registry-taking base constructor.
            return options;
        }
    }
}
```

[View C# snippet source on GitHub](https://github.com/Cratis/Chronicle/blob/main/Documentation/client-snippets/migrations/explicit-registration/recipes.md)

</TabItem>
</Tabs>

The example nominates the plain recipe. The typed alternative uses the same builder methods and receives the owning store's finalized event registry through its base constructor. Choose one recipe for that bridge, not both: duplicate migration bridges are rejected.

Registration still produces the declarative migration definitions evaluated by the kernel. It does not change multi-version storage, append-time migration or backfill behavior described in [Migrations](/chronicle/migrations/).

## Preparation and validation

Every explicitly nominated recipe is validated before registration effects, including recipes that discovery would otherwise never reach. Nomination does not implicitly register missing endpoints. Plain roles must be synchronous instance methods with the exact typed builders; static, async void, overloaded or ambiguous roles are rejected.

Legacy typed migrations using the parameterless base constructor keep declaration-based endpoint validation. For explicit identities, use the registry-taking constructor or the plain recipe form. The [.NET API reference](/chronicle/clients/dotnet/explicit-artifacts/#typed-and-plain-migrations) documents the precise signatures, custom-provider compatibility and runtime activation limits.
