Я некоторое время изучал принципы и шаблоны CQRS/DDD и начал реализовывать пример проекта, в котором я разделил свою модель хранения на WriteModel и ReadModel. WriteModel будет использовать простую базу данных, подобную NoSQL, где агрегаты хранятся в стиле ключ-значение, а значение представляет собой просто сериализованную версию агрегата.
Сейчас я смотрю на ProtoBuf-Net для сериализации и десериализации агрегатов модели предметной области в хранилище и из хранилища. Кроме этого поста я не нашел никаких указаний или советов по используя ProtoBuf-Net в этой области. Дело в том, что (идеальные) требования к сериализации и десериализации агрегатов заключаются в том, что модель предметной области должна как можно меньше знать об этой инфраструктурной проблеме, что подразумевает следующее:
- Нет атрибутов в классах
- Никаких конструкторов, геттеров, сеттеров или любого другого фрагмента кода только ради сериализации.
- Возможность использовать любой (пользовательский) тип и сериализовать/десериализовать его.
До сих пор я реализовал только сериализацию первых версий моих агрегатов, которые отлично работают. Я использую экземпляр RuntimeTypeModel.Default
для настройки метамодели во время выполнения и везде использую экземпляр UseConstructor = false
, что позволяет мне полностью отделить механику сериализации от моей сборки домена. Я даже реализовал собственный механизм пост-десериализации, который позволяет мне своевременно инициализировать поля после того, как ProtoBuf-Net десериализовал их в действительный экземпляр. Итак, предположим, что у меня есть класс AggregateA:
[Version(1)]
public sealed class AggregateA
{
private readonly int _x;
private readonly string _y;
...
}
Затем в моей библиотеке сериализации у меня есть код примерно следующего содержания:
var metaType = RuntimeTypeModel.Default.Add(typeof(AggregateA), false);
metaType.UseConstructor = false;
metaType.AddField(1, "_x");
metaType.AddField(2, "_y");
...
Однако я понимаю, что до этого момента я реализовал только базовый сценарий, и теперь я начинаю думать о том, как подойти к управлению версиями моей модели. Меня особенно интересуют более крупные сценарии рефакторинга, где тип A был разделен на типы A1 и A2, например:
[Version(2)]
public sealed class AggregateA1
{
private readonly int _x;
...
}
[Version(2)]
public sealed class AggregateA2
{
private readonly string _y;
...
}
Предположим, у меня есть сериализованная группа экземпляров AggregateA, но теперь моя модель домена знает только AggregateA1 и AggregateA2, как бы вы справились с этим сценарием с ProtoBuf-Net?
Второй вопрос касается пункта 3: способна ли ProtoBuf-Net обрабатывать произвольные типы, если вы готовы приложить дополнительные усилия по настройке? Я читал об исключениях, возникающих при использовании типа DateTimeOffset
, что заставляет меня думать, что не все типы могут быть сериализованы фреймворком «из коробки», но могу ли я сериализовать эти типы, зарегистрировав их в RuntimeTypeModel? Должен ли я вообще захотеть пойти туда? Или лучше забыть о сериализации общих типов .NET, кроме простых?