Я сталкивался с дизайном Event Sourcing и хотел бы использовать его в приложении, где требуется клиент REST (точнее, RESTful). Однако мне не удается соединить их вместе, поскольку REST очень похож на CRUD, а источник событий основан на задачах. Мне было интересно, как вы можете создавать команды на основе запросов к REST-серверу. Рассмотрим этот пример:
С помощью REST вы можете поместить новое состояние в ресурс под названием File. В одном запросе вы можете отправить новое имя файла, вы можете изменить родительскую папку и / или изменить владельца файла и так далее.
Как построить сервер, чтобы я мог использовать источник событий. Я думал об этих возможностях:
Определить на сервере , какие поля были изменены , и создать соответствующие команды (
RenameFileCommand
,MoveFileCommand
,ChangeOwnerCommand
...) и отправка их по отдельности. Однако в этой настройке каждая команда может потерпеть неудачу, оставив других вне транзакции и, таким образом, из «атомарного» изменения ресурса.Отправка только одна команда (
UpdateFileCommand
) и в обработчике команды, точнее , в совокупности, определяют , какие поля были изменены и передавать отдельные события вместо (FileRenamedEvent
,FileMovedEvent
,OwnerChangedEvent
...)Это мне совсем не нравится: в запросе к серверу я бы указывал в заголовках, какую команду использовать, потому что пользовательский интерфейс все еще основан на задачах (но связь осуществляется через REST). Однако это не удастся при любом другом использовании связи REST (например, во внешних приложениях), поскольку они не обязаны изменять только одно поле в одном запросе. Кроме того, я привнес довольно большую связь в интерфейс, REST и ES-интерфейс.
Какой из них вы бы предпочли или есть лучший способ справиться с этим?
Примечание: приложение, написанное на Java и Axon Framework для событийного поиска.
источник
Ответы:
Я думаю, что у вас может быть пользовательский процесс с несоответствием реализации здесь.
Во-первых: хочет ли пользователь честно выполнять несколько изменений в файле одновременно? Переименование (которое может включать или не включать изменение пути?), Изменение владельца и, возможно, изменение содержимого файла (ради аргумента) кажутся отдельными действиями.
Давайте возьмем случай, когда ответ «да» - ваши пользователи действительно хотят внести эти изменения одновременно.
В этом случае, я настоятельно рекомендую против любой реализации , которая посылает несколько событий -
RenameFileCommand
,MoveFileCommand
,ChangeOwnerCommand
- для представления этого одного намерения пользователя.Почему? Потому что события могут потерпеть неудачу. Может быть, это крайне редко, но ваш пользователь отправил операцию, которая выглядела атомарно - если одно из событий ниже по потоку завершается неудачей, то состояние вашего приложения теперь недопустимо.
Вы также предлагаете расовые опасности на ресурсе, который четко разделен между каждым из обработчиков событий. Вам нужно будет написать «ChangeOwnerCommand» таким образом, чтобы имя файла и путь к файлу не имели значения, потому что они могут быть устаревшими к моменту получения команды.
При реализации неуправляемой системы отдыха с перемещением и переименованием файлов я предпочитаю обеспечить согласованность, используя что-то вроде системы eTag - убедитесь, что версия редактируемого ресурса - это версия, которую пользователь извлек в последний раз, и произойдет сбой, если она был изменен с тех пор. Но если вы отправляете несколько команд для этой однопользовательской операции, вам нужно будет увеличивать версию своего ресурса после каждой команды - поэтому у вас нет возможности узнать, что ресурс, который пользователь редактирует, действительно совпадает с версией ресурса, который он последний раз читал. ,
Под этим я подразумеваю, что если кто-то еще в тот же момент выполняет другую операцию над файлом. 6 команд могут складываться в любом порядке. Если бы у нас было только две атомарные команды, более ранняя команда могла бы завершиться успешно, а более поздняя команда могла потерпеть неудачу: «ресурс был изменен с момента его последнего получения». Но нет защиты от этого, когда команды не являются атомарными, поэтому целостность системы нарушается.
Интересно, что в REST наблюдается движение к чему-то похожему на событийную архитектуру, которая называется «Отдых без PUT», рекомендованной в технологическом радаре Thoughtworks в январе 2015 года . Здесь есть гораздо более длинный блог об отдыхе без PUT .
По сути, идея в том, что POST, PUT, DELETE и GET хороши для небольших приложений, но когда вам нужно начать предполагать, как put, post и delete могут интерпретироваться на другом конце, вы вводите связывание. (например, «когда Я УДАЛЯЮ ресурс, связанный с моим банковским счетом, этот счет должен быть закрыт»). И предлагаемое решение состоит в том, чтобы рассматривать REST более подробно, используя источник событий. т.е. позволяет POST пользовательское намерение как отдельный ресурс события.
Другой случай проще. Если ваши пользователи не хотят выполнять все эти операции одновременно, не позволяйте им. POST событие для каждого намерения пользователя. Теперь вы можете использовать etag-контроль версий на своих ресурсах.
Что касается других приложений, которые используют совсем другой API для ваших ресурсов. Это пахнет неприятностями. Можете ли вы построить фасад старого API поверх вашего RESTful API и указать их на фасаде? т.е. предоставить сервис, который выполняет несколько обновлений файла последовательно через сервер REST?
Если вы не создадите интерфейс RESTful поверх старого решения, не построите фасад старого интерфейса поверх решения REST и не попытаетесь поддерживать оба API-интерфейса, указывающие на общий ресурс данных, у вас возникнут серьезные головные боли.
источник
Только что я наткнулся на следующую статью, в которой рекомендуется указывать имена команд в запросе к серверу в заголовке Content-Type (следуя 5 уровням типа носителя).
В этой статье они упоминают, что RPC-стиль плох для REST, и предлагают расширить Content-Type для указания имени команды:
Статья находится здесь: http://www.infoq.com/articles/rest-api-on-cqrs
Вы можете прочитать больше о 5 уровнях медиа типа здесь: http://byterot.blogspot.co.uk/2012/12/5-levels-of-media-type-rest-csds.html
Хотя они подвергают доменные события API-интерфейсу REST, что я считаю плохой практикой, мне нравится решение, поскольку оно не создает новый «протокол» исключительно для CQRS, будь то отправка имен команд в теле или в и остается верным принципам RESTful.
источник