Подходит ли protobuf-net для сериализации произвольных моделей объектов/доменов?

Я некоторое время изучал принципы и шаблоны CQRS/DDD и начал реализовывать пример проекта, в котором я разделил свою модель хранения на WriteModel и ReadModel. WriteModel будет использовать простую базу данных, подобную NoSQL, где агрегаты хранятся в стиле ключ-значение, а значение представляет собой просто сериализованную версию агрегата.

Сейчас я смотрю на ProtoBuf-Net для сериализации и десериализации агрегатов модели предметной области в хранилище и из хранилища. Кроме этого поста я не нашел никаких указаний или советов по используя ProtoBuf-Net в этой области. Дело в том, что (идеальные) требования к сериализации и десериализации агрегатов заключаются в том, что модель предметной области должна как можно меньше знать об этой инфраструктурной проблеме, что подразумевает следующее:

  1. Нет атрибутов в классах
  2. Никаких конструкторов, геттеров, сеттеров или любого другого фрагмента кода только ради сериализации.
  3. Возможность использовать любой (пользовательский) тип и сериализовать/десериализовать его.

До сих пор я реализовал только сериализацию первых версий моих агрегатов, которые отлично работают. Я использую экземпляр 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, кроме простых?


person Wim.van.Gool    schedule 22.05.2013    source источник


Ответы (1)


protobuf-net предназначен для работы с предсказуемыми известными моделями. Это правда, что все можно настроить во время выполнения, но я не думал о том, как поступить с вашим сценарием A1/A2 именно потому, что это не поддерживаемый сценарий (в свою защиту я не вижу, чтобы это хорошо работало с большинством сериализаторов). Если подумать, если у вас где-то есть данные конфигурации/сопоставления, то вы можете просто дважды десериализовать; то есть, пока мы все еще говорим, что AggregateA1._x сопоставляется с 1, а AggregateA2._y сопоставляется с 2, вы можете сделать:

object a1 = model.Deserialize(source, null, typeof(AggregateA1));
source.Position = 0; // rewind
object a2 = model.Deserialize(source, null, typeof(AggregateA2));

Однако более сложные настройки потребуют дополнительных размышлений.

Относительно «произвольных типов»… определить «произвольные» ;p В частности, существует поддержка «суррогатных» типов, которые могут быть полезны для некоторых преобразований, но без очень конкретной «постановки задачи» трудно ответить полностью.

Резюме:

protobuf-net имеет предполагаемое использование, которое включает как сценарии с поддержкой сериализации (атрибутированные и т. д.), так и неосведомленные сценарии (конфигурация времени выполнения и т. д.), но он также работает для ряда более индивидуальных сценариев (позволяя вам перейти к необработанному читателю /writer API, если хотите). Он не и не может гарантировать прямое соответствие любому вообразимому сценарию сериализации, и его поведение будет зависеть от того, насколько вы далеки от этого сценария.

person Marc Gravell    schedule 22.05.2013
comment
Что касается сценария A1/A2: да, вы можете предположить, что x и y из старого класса теперь будут напрямую отображаться в A1.x и A2.y, поэтому двойная десериализация — это то, что вам в любом случае нужно (спасибо, что показали, как это сделать тот). Что я имею в виду под произвольным, так это «любой тип, который вы ему подаете», если вы зарегистрировали его в TypeModel. Например, я иногда использую Lazy‹T›-класс внутри своей модели предметной области. Может ли protobuf-net сериализовать это, просто выполнив «RuntimeTypeModel.Default.Add(typeof(Lazy‹X›), false)»? - person Wim.van.Gool; 22.05.2013
comment
@Wim.van.Gool, возможно, вводит в заблуждение - вам может потребоваться указать, что вы хотите сериализовать и т. д. И он резервирует несколько встроенных типов; но в общем вы можете сделать это, конечно - person Marc Gravell; 22.05.2013