Автоматическое преобразование между неизменяемыми бизнес-объектами и сообщениями MessagePack - PullRequest
3 голосов
/ 31 октября 2011

В Java я хотел бы использовать иерархии неизменяемых POJO для выражения моей доменной модели.

, например

final ServiceId id = new ServiceId(ServiceType.Foo, "my-foo-service")
final ServiceConfig cfg = new ServiceConfig("localhost", 8080, "abc", JvmConfig.DEFAULT)
final ServiceInfo info = new ServiceInfo(id, cfg)

Все эти POJO имеют открытые конечные поля без методов получения или установки. (Если вы любите геттеры, сделайте вид, что поля закрыты с геттерами.)

Я также хотел бы сериализовать эти объекты, используя библиотеку MessagePack , чтобы передавать их по сети, сохранять их на узлах ZooKeeper и т. Д.

Проблема в том, что MessagePack поддерживает только сериализацию открытых, неконечных полей, поэтому я не могу сериализовать бизнес-объекты как есть. Также MessagePack не поддерживает enum, поэтому я должен преобразовать значения перечисления в int или String для сериализации. (Да, если вы добавляете аннотацию к enum s. Смотрите мой комментарий ниже.)

Чтобы справиться с этим, у меня есть рукописная соответствующая иерархия объектов «сообщения» с преобразованиями между каждым бизнес-объектом и соответствующим ему объектом сообщения. Очевидно, что это не идеальный вариант, поскольку он вызывает большое количество дублирующегося кода, а человеческая ошибка может привести к отсутствию полей и т. Д.

Есть ли лучшие решения этой проблемы?

  • Генерация кода во время компиляции?
  • Каким образом можно генерировать соответствующие сериализуемые классы во время выполнения?
  • Отказаться от MessagePack?
  • Отказаться от неизменности и enum s в моих бизнес-объектах?
  • Существует ли какая-нибудь универсальная библиотека-оболочка, которая может обернуть изменяемый объект (объект сообщения) в неизменяемый (бизнес-объект)?

MessagePack также поддерживает сериализацию Java Beans (с использованием аннотации @MessagePackBeans), поэтому, если я могу автоматически конвертировать неизменный объект в / из Java Bean, это может приблизить меня к решению.

Ответы [ 2 ]

1 голос
/ 25 июня 2013

По совпадению, я недавно создал проект, который в значительной степени соответствует тому, что вы описываете.Использование неизменяемых моделей данных дает огромные преимущества, но многие технологии сериализации, по-видимому, подходят к неизменности как к запоздалой мысли.Я хотел что-то, что исправило бы это.

Мой проект, Grains , использует генерацию кода для создания неизменной реализации модели предметной области.Реализация является достаточно общей, чтобы ее можно было адаптировать к различным средам сериализации.До сих пор поддерживаются MessagePack, Jackson, Kryo и стандартная сериализация Java.

Просто напишите набор интерфейсов, которые описывают модель вашего домена.Например:

public interface ServiceId {
    enum ServiceType {Foo, Bar}

    String getName();
    ServiceType getType();
}

public interface ServiceConfig {
    enum JvmConfig {DEFAULT, SPECIAL}

    String getHost();
    int getPort();
    String getUser();
    JvmConfig getType();
}

public interface ServiceInfo {
    ServiceId getId();
    ServiceConfig getConfig();
}

Плагин Grains Maven затем генерирует неизменные реализации этих интерфейсов во время компиляции.(Источник, который он генерирует, предназначен для чтения людьми.) Затем вы создаете экземпляры своих объектов.В этом примере показаны два шаблона построения:

ServiceIdGrain id = ServiceIdFactory.defaultValue()
    .withType(ServiceType.Foo)
    .withName("my-foo-service");

ServiceConfigBuilder cfg = ServiceConfigFactory.newBuilder()
    .setHost("localhost")
    .setPort(8080)
    .setUser("abc")
    .setType(JvmConfig.DEFAULT);

ServiceInfoGrain info = ServiceInfoFactory.defaultValue()
    .withId(id)
    .withConfig(cfg.build());

Не так просто, как ваши public final поля, я знаю, но наследование и композиция невозможны без методов получения и установки.И эти объекты легко читаются и пишутся с помощью MessagePack:

MessagePack msgpack = MessagePackTools.newGrainsMessagePack();

byte[] data = msgpack.write(info);
ServiceInfoGrain unpacked = msgpack.read(data, ServiceInfoGrain.class);

Если фреймворк Grains не работает для вас, вы можете проверить его шаблоны MessagePack .Вы можете написать универсальный TemplateBuilder, который использует отражение, чтобы установить конечные поля вашей рукописной модели предметной области.Хитрость заключается в том, чтобы создать пользовательский TemplateRegistry, который позволяет регистрировать вашего собственного сборщика.

0 голосов
/ 05 июня 2013

Звучит так, как будто вы объединили, а не разделили проблемы чтения и записи вашего приложения.Вы, вероятно, должны рассмотреть CQRS на этом этапе.

По моему опыту, неизменяемые доменные объекты почти всегда присоединяются к истории аудита (требование) или к данным поиска (перечисления).

Ваш домен, вероятно, должен быть, в основном, изменчивым, новам все еще не нужны геттеры и сеттеры.Вместо этого у вас должны быть глаголы на ваших объектах, которые приводят к измененной модели домена и вызывают события, когда в домене происходит что-то интересное (интересное для бизнеса - бизнес == кто-то платит за ваше время).Вероятно, вам нужно передавать события, а не доменные объекты.Возможно, это даже команды (они похожи на события, но источником является агент, внешний по отношению к ограниченному контексту, в котором живет ваш домен - события являются внутренними по отношению к ограниченному контексту модели).

Вы можете иметьсервис для сохранения событий (и другой для сохранения команд), который также является вашим журналом аудита (выполняя ваши истории аудита).

У вас может быть обработчик событий, который помещает ваши события в вашу шину.Эти события должны содержать либо простую информацию, либо идентификаторы сущностей.Службы, которые отвечают на эти события, должны выполнять свои обязанности, используя предоставленную информацию, или они должны запрашивать информацию, в которой они нуждаются, используя данные идентификаторы.

Вы действительно не должны раскрывать внутреннее состояние вашей доменной модели.Делая это, вы нарушаете инкапсуляцию, а это на самом деле нежелательно.На вашем месте я бы взглянул на Axon Framework .Скорее всего, это продвинет вас дальше, чем только MessagePack.

...