Events Versioning
Events by their nature represent facts that happened in the past. They should be immutable even if they had wrong values (as we can only roughly guess what should be the missing property value).
However, in practice, it's unavoidable in the living system to not have the event schema migrations. They might come from:
- bug - eg. typo in the property name, missing event data,
- new business requirements - eg. besides storing the user email we'd like to be also storing its full name,
- refactorings - eg. renaming event class, moving to different namespace or assembly,
- etc.
Depending on the concrete business case we may use a different technique for handling such event migrations.
Namespace Migration
Marten by default tries to find the event class based on the fully qualified assembly name (it's stored in mt_dotnet_type
column of mt_events
table, read more in events schema documentation. When it is not able to find event type with the same assembly, namespace and type name then it tries to make a lookup for mapping on the event type name (stored in type
column of mt_events
table).
Such mapping needs to be defined manually:
- either by registering events with store options
Events.AddEventTypes
method, - or by defining custom mapping with
Events.MapEventType
method.
For the case of namespace migration, it's enough to use AddEventTypes
method as it's generating mapping based on the event type. As an example, change OrderStatusChanged
event from:
namespace OldEventNamespace
{
public class OrderStatusChanged
{
public Guid OrderId { get; }
public int Status { get; }
public OrderStatusChanged(Guid orderId, int status)
{
OrderId = orderId;
Status = status;
}
}
}
to:
namespace NewEventNamespace
{
public class OrderStatusChanged
{
public Guid OrderId { get; }
public int Status { get; }
public OrderStatusChanged(Guid orderId, int status)
{
OrderId = orderId;
Status = status;
}
}
}
It's enough to register new event type as follows:
var options = new StoreOptions();
options.Events.AddEventTypes(new[] {typeof(NewEventNamespace.OrderStatusChanged)});
var store = new DocumentStore(options);
After that Marten will automatically perform a matching based on the type name (that didn't change) - order_status_changed
.
Event Type Name Migration
When the event class type name has changed, Marten does not perform automatic mapping but allows to define a custom one.
To do that you need to use Events.MapEventType
method to define the type name for the new event.
Eg. for migrating OrderStatusChanged
event into ConfirmedOrderStatusChanged
namespace NewEventNamespace
{
public class ConfirmedOrderStatusChanged
{
public Guid OrderId { get; }
public int Status { get; }
public ConfirmedOrderStatusChanged(Guid orderId, int status)
{
OrderId = orderId;
Status = status;
}
}
}
it's needed to register mapping using old event type name (order_status_changed
) as follows:
var options = new StoreOptions();
options.EventGraph
.MapEventType<NewEventNamespace.ConfirmedOrderStatusChanged>("order_status_changed");
var store = new DocumentStore(options);
WARNING
In this case, both old OrderStatusChanged
and new ConfirmedOrderStatusChanged
event type names will get published with the same order_status_changed
event type.