Cloud of Science. 2015. T. 2. № 4 http:/ / cloudofscience.ru ISSN 2409-031X
Использование технологии программирования command query responsibility segregation (CQRS) для распределенных информационных сервисов
Д. Ю. Пимкин, Е. В. Никульчев
Московский технологический институт 119334, Москва, Ленинский проспект, 38а
e-mail: [email protected]
Аннотация. Распределенные системы в настоящее время становятся самой распространенной формой информационных систем. Большие объемы данных, интеграция приложений, персонификация сервисов предъявляют требования к архитектуре систем: масштабируемость, отказоустойчивость, гарантированное качество доставки данных. Для выполнения этих требований требуется использование специализированных подходов к разработке программного обеспечения. В статье рассмотрен принцип императивного программирования CQRS (Command Query Responsibility Segregation), который является мультипарадигмен-ным и может быть использован в объектно-ориентированном, функциональном, смешанном и процедурном программировании. Проведен подробный анализ использования CQRS в программной разработке на примере Scala-кода; рассмотрена техническая реализация проектного решения, а также проведен анализ возможности использования в облачных сервисах.
Ключевые слова: императивное программирование, сервис-ориентированная архитектура, распределенные информационные системы, облачные сервисы, масштабируемая системная архитектура.
1. Введение
С каждым днем продолжает увеличиваться количество пользователей, а также устройств, гаджетов, подключенных к интернету. Все устройства, включая фитнес-трекеры, камеры телефонов и даже умные кофеварки, генерируют данные, которые требуется где-то хранить. С увеличением количества данных в мире увеличивается и потребность в больших объемах для хранения информации и вычислительных мощностей для их обработки.
Термин Big Data («большие данные») появился сравнительно недавно. Google Trends показывает начало активного роста употребления словосочетания, начиная с 2011 года. Концепция Big Data продолжает расширяться и увеличиваться, в частности, с появлением идеологии интернет-вещей (Internet-of-Things, IoT). Исходя из
современных представлений, можно сформулировать основные требования к системам, оперирующим с большими объемами данными:
1. Горизонтальная масштабируемость. Поскольку рост объемов данных неограничен — любая система, которая подразумевает обработку больших данных, должна быть расширяемой.
2. Отказоустойчивость. Принцип горизонтальной масштабируемости подразумевает, что машин в кластере может быть много. Некоторые из этих машин регулярно будут гарантированно выходить из строя. Методы работы с большими данными должны учитывать возможность таких сбоев и переживать их без каких-либо значимых последствий.
3. Локальность данных. В больших распределенных системах данные распределены по большому количеству машин. Если данные физически находятся на одном сервере, а обрабатываются на другом — расходы на передачу данных могут превысить расходы на саму обработку. Поэтому принцип локальности данных подразумевает возможность обработки на той же машине, на которой они хранятся.
Все современные средства работы с большими данными так или иначе следуют этим трем принципам. Для того чтобы им следовать — необходимо разрабатывать новые методы, способы и парадигмы разработки средств разработки данных. И поскольку количество источников данных стремительно растет, то и новые технологии их обработки становятся все более востребованными.
Исторически сложилось так, что приложения в основном полагаются на реляционные СУБД, которые по своей сути соответствуют принципам ACID (Atomicity-Consistency-Isolation-Durability). Сегодня компании сталкиваются с проблемами балансировки, доступности и согласованности наряду с ACID свойствами, поэтому необходимо искать альтернативные решения. Этот путь ведет бизнес к распределенной инфраструктуре или, так называемым облачным решениям [1], в том числе и основанным на NoSQL-базах данных [2].
В статье расматривается новый способ разработки программных приложений, объединяющий command query responsibility segregation (CQRS) и event sourcing [3]. CQRS является продолжением принципа command query separation (CQS), предложенного Бертраном Мейером в 1988 г. [4]. Мейер утверждает, что CQS — это довольно глубокое понятие, являющееся основой для распределенных вычислений. Например, библиотека MSDN пополнилась CQRS каталогом шаблонов проектирования, разделенных на восемь категорий: доступность, управление данными, проектирование и реализация, обмен сообщениями, управление и мониторинг, производительность и масштабируемость, устойчивость и безопасность. Эти категории CQRS могут помочь в проектировании систем для решения различных вопросов, возникающих в корпоративной среде, например, узкие места в производительно-
сти, масштабируемости; параллелизм, конфликты; сложность проектирования, разработки и обслуживания [5]. Помимо этого, принцип CQRS способствует эволюции системы и предлагает способы объединения конфликтов нана уровне домена.
2. Анализ сервис-ориентированной архитектуры и модели CQRS
Сервис-оринтироанные технологии (SOA) получили широкое распространение в корпоративном секторе, так как они позволяют эффективно управлять и развивать IT-инфраструктуру, интегрировать поглощенные компании в свой ландшафт, либо продавать часть бизнес-структуры, а также управлять жизненным циклом программного обеспечения [6].
Для удовлетворения потребностей крупного бизнеса недостаточно наличие лишь одного программного приложения или комплекса программного обеспечения, используемый программный ландшафт является гетерогенным, например, для финансовой группы недостаточно приобрести даже все ПО компании SAP. Кроме того, у разных поставщиков оборудования и ПО используются различные технологии, форматы данных, контракты и т. д.
В 70-е и 80-е гг. XX века наблюдалось преимущественное использование мей-нфремов, в основе которых лежал проприетарный планировщик задач, который запускал shell-скрипты. Интеграция разного ПО осуществлялась с помощью unix pipeline. Многопоточность в то время не была широко распостранена, и при необходимости использовались fibers, отличающиеся от потоков тем, что используют кооперативную многопоточность, а потоки используют вытесняемую многопоточ-ность. В 90-х гг. появились такие объемы информации, с которыми реляционные базы данных справлялись с трудом, в результате все большее распространение получают нереляционные базы данных, такие как Berkley DB и прочие.
Такое эволюционное развитие привело к тому, что типичный ИТ-ландшафт состоит из отдельных решений — ведение главной книги, риск-менеджмент, интернет-банкинг, процессинг и база отчетности и проч. Очевидно, что все решения имеют пересекающиеся кортежи данных и ввод информации сразу в несколько систем неоправдан с точки зрения использования трудовых ресурсов и требуемого времени: это неудобно пользователям, влечет много ошибок, экономически невыгодно, требует больше времени на поддержку и обслуживание. Такие ландшафты имели соединение точка-точка между различными системами, работающими в режиме реального времени и по определенному регламенту. Вследствие чего регламентные выгрузки данных получили название ETL (Extract Transform Load) [7]. Подключение точка-точка в текущих реалиях называется микро-сервисной архитектурой.
Существует два типа корпораций: компании, зарабатывающие благодаря ИТ-инфраструктуре, и компании, деятельность которых существовала задолго по появления компьютеров и программ. Для последних ИТ-инфрастуктура является лишь статьей затрат, поэтому ландшафт таких компаний развивается стихийно и содержит множество случайных программ. Схема ландшафта потоков данных в таких компаниях, если она есть, — достаточно сложная и запутанная. Даже сейчас есть крупные компании, в которых отсутствует единая и достоверная информация о существующих потоках данных. Такая ситуация возникает в силу ряда причин, например, потеря экспертизы вследствие текучести кадров, поглощения другой компании, человеческого фактора. Для возможности контроля подобной ситуации используется SOA, в основе которой, как правило, лежат паттерны ESB (Enterprise Service Bus), глобальная модель данных, MDM (Master Data Management), BI (Business Intelligence).
Ввиду отсутствия интеграционной доктрины и методологии, каждый ИТ-департамент решал проблему интеграции в силу собственных мировоззрений, либо использовал внешнюю экспертизу, интеграторов или вендоров. Софтверные компании, такие как IBM, Oracle, Microsoft, Software AG, Tibco, BEA Systems, Collaxa, Lombardi, уже на тот момент располагали так называемым связующим ПО (middleware), для обеспечения взаимодействия различных приложений, систем и компонентов [8]. Microsoft смогла первой дать формальное описание ESB и SOA, после чего маркетинговый подход большинства вендоров стал использовать эти понятия в продвижении предлагаемых на рынке программных продуктов.
Интерфейсы компонентов в сервис-ориентированной архитектуре инкапсулируют (скрывают) детали реализации (операционную систему, платформу, язык программирования) от остальных компонентов, таким образом обеспечивая комбинирование и многократное использование компонентов для построения сложных распределенных программных комплексов, обеспечивая независимость от используемых платформ и инструментов разработки, способствуя масштабируемости и управляемости создаваемых систем.
Главное, что отличает SOA — это использование независимых сервисов с четко определенными интерфейсами, которые для выполнения своих задач могут быть вызваны неким стандартным способом, при условии, что сервисы заранее ничего не знают о приложении, которое их вызовет, а приложение не знает, каким образом сервисы выполняют свою задачу. SOA может поддерживать интеграцию и консолидацию операций в составе сложных систем, однако SOA не определяет и не предоставляет методологий или фреймворков для документирования сервисов [11].
Принцип императивного программирования CQRS (Command Query Responsibility Segregation) является мультипарадигменным и может быть использован в
объектно-ориентированном, функциональном, смешанном и процедурном программировании. Данный принцип является основой для использования методологии Service Oriented Architecture (SOA) [9] и Enterprise Service Bus (ESB) [10]. В основе принципа CQRS (рис. 1) используются Query и Command для получения и модифицирования данных соответственно [11]. Основной идеей паттерна Command является создание самодостаточного объекта, представляющего некоторое действие, которое впоследствии можно выполнить по желанию. Другими словами, создаем объект, у которого есть метод (как правило, без входящих параметров), который можно вызвать из любого другого объекта. Паттерн Command — одноразовый, а именно, использовав однажды, его нельзя вызвать после использования. Важной особенностью является то, что создание и выполнение некоторой операции разделено. При создании команда получает необходимый контекст выполнения для осуществления операции, и таким образом, потребителю достаточно лишь вызвать метод на экземпляре объекта команд.
В основе паттерна Query лежит паттерн Command. Он позволяет разделить процессы создания экземпляра объекта и непосредственного получения данных. В функциональном программировании есть аналогичный термин — «замыкание». Типичное использование замыкания — это когда функция высшего порядка полу-
хранилище данных
журнал событий
Рисунок 1. Программная модель CQRS
чает определенный набор входных параметров и результатом ее выполнения является замыкание, которое в дальнейшем можно вызвать. Такая схема упрощает асинхронное взаимодействие потоков и лежит в основе принципа CQRS (рис. 2).
Приведем в качестве примера разработку объекта доступа к данным без использования принципа CQRS. Тогда есть некоторый класс, который имеет два метода — для получения данных и для их модификации. Для получения данных имеется входной кортеж данных, характеризующих требуемую выборку данных, которую непосредственно получаем на выходе. При модификации получаем некоторый кортеж данных, успешность выполнения обработки которого характеризуется отсутствием исключительных ситуаций (исключений), либо идентификатор кода ошибки, в зависимости от подхода к обработке ошибок. В случае реализации класса с использованием CQRS данные методы из входных параметров и внутреннего состояния формировали бы контекст выполнения соответствующих команд и возвращали бы соответствующие команды [12].
_.......- доросы
Потребите«
Рисунок 2. Схема шаблона CQRS
Причины разделения команд и запросов заключаются в следующем:
- сильный дисбаланс между количеством чтений и записей при работе с большим объемом информации;
- единая программная модель, инкапсулирующая в себе и запись и чтение, в случае с большими данными работает недостаточно хорошо;
- шаблон Command может содержать сложную бизнес-логику, максимально приближенную к бизнес-операциям;
- для более быстрых запросов данные записываются в хранилище в де-нормализованном состоянии (например, в документо-ориентированную базу данных, такую как Mongo DB);
- операции становятся более атомарными и простыми;
- данные для операций чтения легко воссоздаются заново из записанной событийной модели [13].
Если говорить о принципе «Команда», то центральной частью этого подхода будет являться некая поведенческая модель, нежели данные, как таковые, что, в свою очередь, подводит нас к более достоверной реализации DDD модели [14]. «Команду» можно идентифицировать, как запрос (request) системы выполнить определенное действие или задание, например, зарегистрировать заказ, изменить запись о местоположении объекта и т. д. [15]. Модель "Команда" является императивной и представляет собой запрос системы с целью изменения состояния объекта, таким образом, она выполняет определенное действие, которое клиент желает совершить [1 6]. Однако, с концептуальной точки зрения, было бы неверно рассматривать такой подход только как редактирование данных, более правильно воспринимать "Команду" как представление конкретного запроса на совершение дей-ствияили как вызов метода сериализации [14].
Следует, также, понимать, что «Команда», как обработчик запросов может отказать в их исполнении например, по причине невалидных данных или некорректного состояния запроса. Что касается формы передачи данных, то «Команду» более правильно рассматривать с точки зрения некоторого сообщения (message), а не как привычный объект данных (DTO — data transfer object).
Необходимо упомянуть о такой сущности в модели CQRS, как Command Handler, объекте, который обрабатывает все команды, приходящие в запросах, решает, какая из них может быть отклонена, и преобразует в одно или более событий для дальнейшего сохранения в хранилище данных.
Query (Запрос) позволяет вычитывать необходимую информацию из хранилища данных, без использования ORM [17].
Что касается консистентности (согласованности и непротиворечивости) данных (Eventual Consistency), то следует отметить, что в случае использования CQRS бизнес-сфера определяет срок жизни и хранения объектов между вызовами. В то же время вместе с проекциями изменений в шину событий передается сигнал об изменении данных, т. е., используя шаблон издатель-подписчик (publisher-subscriber), заинтересованные потребители получают уведомления о произошедших изменениях.
Говоря о технической реализации, можно выделить такие немаловажные моменты, как использование в программе очередей (Queue), в том числе для хранения событий, и отсутствие необходимости двухфазного коммита.
Перечисленные преимущества способствуют повышению интереса к изучению CQRS-шаблона и внедрению его в промышленное программирование. В связи с этим рассмотрим изучаемый подход более подробно путем непосредственного написания программного кода. В качестве языка программирования будем использовать Scala.
3. Применение CQRS в разработке программного обеспечения
Пример, показанный на рис. 3 демонстрирует, как клиент может оформить заказ, содержащий множество подзаказов для одного продукта. Для этого он должен создать заказ, отправляя сообщение NewOrder. Это сообщение трансформируется в команду CreateOrderForUser, которая последовательно обрабатывается актором Or-derCommandHandler (описанным ниже). Если сообщение CreateOrderForUser потеряно в процессе отправки актору Orders, то клиенту необходимо направить повторное сообщение. Процесс повторной отправки является идемпотентным. Пользователь добавляет один или более продуктов в заказ посредством команды Addltem и завершает операцию вызовом команды SubmitOrder.
Для наглядности определим следующие бизнес-правила:
- пользователь может совершать один заказ в единицу времени;
- максимальная сумма заказа не может превышать 100 единиц;
- администратор или пользователи (финансовый департамент, отдел логистики или доставки и т. д.) могут производить мониторинг заказа или потока заказов.
В примере 1 (Orders) имеется единый кластер со списком заказов. Этот актор необходим для мониторинга заказов в системе. В реальном проекте это может быть использовано для повторного создания заказа при старте приложения.
Техническая реализация данного решения использует модель CQRS и кластеризацию (рис. 4). Чтобы произвести действие, необходимо вызвать команду, которая будет провалидирована и обработана. Эти действия, в свою очередь, приведут к некоторым бизнес-событиям, которые сохранятся в журнал операций. Таким образом работает модель «Команда» в паттерне CQRS. Так, в DomainActors описаны три актора для обработки бизнес-логики.
package cqrs.domain.orders
import akka.actor.{ Status, ActorLogging, ActorRef, Props } import akka.persistence.PersistentActor import cqrs.settings.SettingsActor
object Orders {
case class Get(orderId: String)
case class CreateOrderForllsertid: String, username: String) case class InitializedOrderAck[id: String, username: String)
sealed trait Evert
case class OrderCreatedforderld: String, username: String) extends Evert sealed class FunctionalExceptionfmessage: String) extends Exception(message)
case class DuplicateOrderKeyException(orderld: String) extends FunctionalException(s"There already is an order with id: sorderld")
val persistenceld: String = "orders"
def props(orderRegion: ActorRef): Props = Props(new Orders(orderRegion))
def actorNameforderld: String): String = s"order-sorderId"
>
class Orders(orderRegion: ActorRef) extends PersistentActor with SettingsActor with ActorLogging {
import cqrs,domain.orders.Orders._
override val persistenceld: String = Orders.persistenceld
var existingOrders = Set.empty[String]
def updateState(event: Event): Unit = event match { case OrderCreated(orderId, username) =» existingOrders +- orderEd;
>
override def receiveCommand: Receive = { case CreateOrderForUser(orderId, username) if lexistingOrders.contains(orderld)
log.debugC'Creating Order")
orderRegion ! OrderCommandHandler.Commard(orderId, Order.3nitializeOrder(username)) sender!) ! Status.Success!) case CreateOrderForUser(orderId, username) =>
log.debugC'Order already created")
sender!) ! Status.Failure(DuplicateOrderKeyExceptioniorderld)) case initializedOrderAck(orderld, username) =
log.debugC'Registering initialized order")
persist(OrderCreated(orderld, username))(updateState)
}
override def receiveRecover: Receive = { case event: Event =•
log.debugC'Receiving recover message: {>", event) updateState(event)
>
Рисунок 3. Orders
Рисунок 4. DomainActors
Order (рис. 5, 6) — это объект, содержащий описание заказа и всю логику для его проведения. Акторы этого объекта могут быть распределены по всем доступным нодам.
Рисунок 5. Object Order
class Order(maxOrderPrice: Double) extends PersistentActor with SettingsActor with ActorLogging { import cqrs.donain.orders.Order._ val orderld = self. path .пале
override def persistence!«?: String = Order,persistenceld(orderld) war orderPrice: Double = 3
context.set Re ceiveTimeout{sett ings. о rde rRece iveTimeout}
def updateState(event: Event): Unit = event match { case Orderlnitiallzed
context become initialized case ItemAdded (quantityj producth'ane, pricePerlten) -» log.debug(s"UPDATE: Spe rsistenceld"J orderPrice += quantity * pricePerlten case OrderSubmitted -* context become submitted
>
def uninitialized! Receive = { case InitializeOrder(username) -persist (Orderlnitiallzed) { evt -» log.debtrg(5"Order initialized {}", persistenceld) opdateState(evt}
sender ! Orders.InitializedOrderAcktorderldj usernaie)
}
case ReceiveTineout -* context. parent ! Passivatef stopMessage = StopOrder) case StopOrder -* context stop self
case _ •* senderf) ! Status.Failure[UnknownOrderException("unknown order")}
>
def initialized: Receive = ^
case Addltern(quantity, productName, pricePerltem) if orderPrice + quantity * pricePerltem maxOrderPrice -persist(ItemAdded(quantityj producthame, pricePerltem)) { evt log.debug(5"Item Added {} to {}", persistenceId, evt) senderO ! Status.Successi()) opdateState(evt)
}
case Addltem(quantity, productName, pricePerltem) -
log.error("Attenpt to add more itens to the order than allowed") senderO ! Status.Failure(MaxOrderPriceReached(crderPricej maxOrderPrice)) case SubmitOrder if orderPrice > 8
log.info(s"Order subnitted {}"j orderld) persist (OrderSubmitted) { event -» senderO ! Status.Successi()) opdateState(event)
}
case ReceiveTineout -* context.parent ! PassivatefstopMessage = StopOrder) case StopOrder -* context stop self
>
def subnitted; Receive = {
case ReceiveTineout -» context.parent ! PassivatefstopMessage = StopOrder) case StopOrder -* context stop self case rsg -
log.error("Order is conpleted. Will not process: 0"j msg) senderO ! Status.Failure(OrderIsSubnittedException(orderId))
>
override def receiveftecover: Receive = i case event: Event -»
log.debug("Receiving recover message: {}", event) updateStatefevent)
>
override def receiveCormand:; Receive = uninitialized
Рисунок 6. DomainActors
OrderCommandHandler (рис. 7) выполняется на каждой ноде, обрабатывая все команды по каждому заказу. Такая обработка производится путем направления команды к Orders или напрямую к Order.
Чтобы создать представление о доменной модели, которая является примером event source, необходимо собрать все события (events) и обработать их в представление. Это является другой частью шаблона CQRS — Запросом (Query), которая представлена актором PersistentView. Однако, проблема последнего заключается в том, что он может взаимодействовать с одним единственным идентификатором (persistenceId).
5 import akka.actor._
6 import akka.contrib.pattern.ShardRegion
7 import akka.util.Timeout
a import cqrs.settings.SettingsActor 9
10 object OrderCommandHandler {
12 case class Comandiorderld: String, end: Order,Command)
13
14 case class JrknownOrderExceptionlmsg: String] extends Exception(msg)
15
16 val idExtractor: SiardRegion.IdExtractor = {
17 case Command (orderld, orderCommand) => {orderld, ordeirCommand) IB }
19
20 def shardResolver: 5liardRegion .ShardResolver = {
case Commardtorderld, _) => math.abs(orderId.hashCode) % 30 toString
22 }
24 def props(orders: ActorRef, orderRegion: ActorRef): Props = Propsinew OrderCommandHandlerforders, orderRegion)J
26 2B
29 class OrderComnandHandlerCorders: ActorRef, orderRegion: ActorRef) extends Actor with ActorLogging with SettingsActor {
30
31 import cqrs.domain.orders.OrderCom(nandHandler._
32
33 implicit val timeout: Timeout - 5 seconds
34
35 override def receive: Receive = {
36 case create: Orders.CreateOrderForUser ^
log.debugC"Creating a new Order: {>", create.id)
3B orders forward create
39 case end: Command -
40 log.debug("Executing a new Command")
41 orderRegion forward end
Объект OrdersView (рис. 8), который, в свою очередь может создавать наглядный список по всем заказам OrderView для каждого заказа в системе. Далее, OrderView перенаправляет все события (рис. 9) родительскому объекту OrdersView, таким образом, OrdersView получает все события из заказов в системе и может создать любое представление доменной модели.
1 package cqrs.domain.orders
2
3 import scala.concurrent.duration..
4
42 }
43 }
Рисунок 7. OrderCommandHandler
package cqrs. read
xnport akka. actor. { ActorLoggingj Props >
xnport akka. persistence, ^ PersistentView, Persistent 1-
inport ccrs. dona in . orders . ( Order, Orders }■
xnport cqrs. read. OrdersView. Orderltem
object OrdersView {
case class Orderlten[quantity: Int, productName: String, pricePerltem: Double)
case class Order (orderld: String, items: List [Orderltem]submitted: Boolean = false)
case object GetTop2I±ems case object GetPendingOrders
case class GetOrdersForUser(username: St ring >
def props: Props =
Props(new OrdersView)
}
class OrdersView extends PersistentView with ActorLogging { override def persistenceld: String = Orders.persistenceld
override def viewld: String = s"$-persistenceld—view"
type Orderld = String type UserNane = String type ProductNane = String
var orderldToOrder: Map[0rderld, OrdersView„Order] = Map.empty
var ordersPerUser: HapJUserName, List I Orderld] ]| = Hap.erpty.withDefaultValue (List .empty) var pendingOrders: Set [Orderld] = Set. empty
var product Sold: Map [ProductName, Int] = Hap , errrpty. witlrDefaultValue(C}
def buildView: Receive = {
case Orders . OrderC rea ted (orderld, username} -* c reat eO rde rViewActor (o rd e rl d }
vaX newOrder = QrdersView . Order (orderld. List .empty [ Order Item] > orderldToOrder = orderIdToOrc"er.updated(orderIdj newOrder}
ordersPerUser = ordersPerUser.updated(usernare, orderld :: crdersPerllserfusername}) pendingOrders += orderld case 0rderView.Envelope{orderld, 0rder.ItemAdded(quantity, productName, pricePerltem)) -» vaX order = orderEdToOrder{orderld)
val orderWiirhfcewItem = order. copy (items = OrderItem(quantity, prcductWame, pricePerltem) :s order.items} orderldToOrder = orderIdToOrc"er.updated(orderIdj orderWitbNeviItem} product So Id -*■= productName -=»• (productSoId(productName) * quantity)
log. info("===========> name: O, quantity: O CO)"* productfcare, quantity, productSold}
case QrderView. Envelope [orderld. Order. OrderS ubmitted) vaX order = orderEdToOrderiorderld) vaX orderWithKewItem = order. copy (submitted = true} orderldToOrder = orderIdToOrc"er.updated(orderIdj orderWitbNeviItem} perd mgOroers —= orderld
>
override def receive: Receive = buildView orElse { case OrdersView .GetPendingOrders -»
vaX pending: Set [Orders View. Order ] = pendingOrders . rap (orderldToOrder {_) } senderO ! pending case OrdersView .GetOrdersForllser(username)
vaX ordersForliser: List [OrdersView. Order] = ordeirsPerUser(username) .raptorderldToOrderJ senderO ! ordersForliser case OrdersView .Getfop2Itens —
senderO ! productSold-toList.sortBy(_._2},reverse.take(2J
def createOrderViewActor(orderld: String): Unit = context.actorOf(OrderView.props(orderld), s"orderView-iorderId")
Рисунок 8. OrdersView
l
2
3
4
5
6 7
a
9 10 11 12
13
14
15
16 17
ia
19
20 21
package cqrs.read
import akka.actor.Props
import akka.persistence.PersistentView import cqrs.domain.orders.0rder import cq rs.read.0 rde rView.Envelope
object OrderView {
case class Envelope(orderid: String, event: Order.Event)
def props(orderid: String): Props = Props(new OrderViewforderid>)
>
class OrderViewforderld: String) extends PersistentView {
override def persistenceld: String = Order.persistenceldiorderid) override def viewld: String = $persistenceld-view"
override def receive: Receive = {
case msg: Order. Event -> context:. parent forward Envelopeforderld, msg)
>
Рисунок 9. OrderView
Учитывая все вышеизложенное, можно вывести определенные свойства информационных систем, для которых подходит SOA-архитектура и, в частности, модель CQRS. Эти свойства вытекают из самой сущности изучаемой модели и являются критическими в CQRS-системах. Без этих свойств системы подобного вида будут полностью не функциональны [18].
Вообще говоря, тот факт, что SOAP web-сервисы не очень подходят для построения высоконагруженных приложений было понятно еще 2007 году. Не смотря на это, некоторые организации продолжают использовать сервис-ориентированную архитектуру. Большая же часть ИТ-сообщества говорит сейчас совсем о другом: модель акторов [19], CQRS [20] и event sourcing [5], Apache Storm [21] и лямбда-архитектура [22]. Все эти подходы выросли из так называемой управляемой событиями архитектуры — построения вычислительной системы как набора независимых компонент, обменивающихся друг с другом сообщениями через очереди (так называемая слабая связность — loose coupling). В частности, это позволяет распределять такие компоненты по серверам, создавая масштабируемые отказоустойчивые решения. (К слову сказать, разработчики традиционных ESB, BPM, CRM и прочих ERP решений этого до сих пор не делают, поэтому с приобретением очередного «коробочного решения» приходят и проблемы с масштабированием и внесением изменений).
Однако главная цель заключается не только в масштабировании и отказоустойчивости, а также в быстром запуске новых сервисов для клиентов компании. Основная проблема, связанная с ИТ в современных организациях, в невозможности быстрой разработки новых качественных услуг для клиентов и партнеров. Сервис-ориентированная архитектура, по сути своей, это диагноз ИТ-отделам организаций. Попытка найти ответ на вопрос, почему ИТ-решения делаются медленно и дорого, а результат неудобен для клиента. Согласно SOA, причина в этих самых унаследованных (legacy) приложениях, под завязку набитых ненужным функционалом и несущих на себе огромный технический долг. Однако если уметь перехватывать события, происходящие в таких приложениях, то новый функционал можно разрабатывать снаружи, не затрагивая устаревшие системы.
Сервисная шина — главный претендент на создание платформы для разработки таких решений. Несколько примеров событий: регистрация клиента, подключение/отключение услуг, изменение состояния клиента, поступление платежа или списание средств, действия клиента в системе самообслуживания, обращение в контакт-центр, запрос с мобильного телефона. Поверх этих событий можно разработать огромное количество сценариев: уведомление, начисление бонусов, управление параметрами сервиса и т. д., другими словами, если уметь перехватывать события и быстро реализовывать новые сценарии их обработки, то логика предостав-
ления услуг ограничивается только фантазией. Далее можно использовать в сценариях результаты аналитики больших данных или делать такую аналитику в реальном времени, и главное, что для всего этого не нужно дорабатывать унаследованные приложения.
Основная идея здесь в том, что ESB — это способ обеспечить себе свободу принятия решений и повысить скорость разработки новых слабосвязанных компонентов [23].
Концептуальная модель интеграционного SOA решения должна обладать свойствами: отзывчивость, отказоустойчивость, эластичность.
Отзывчивость — это основа юзабилити и полезности по той простой причине, что долгие задержки интерфейса не добавляют желания им пользоваться, а отзывчивость означает, что проблемы могут быть быстро обнаружены и эффективно решены. Отзывчивые системы фокусируются на предоставлении быстрых и постоянных по времени ожидания ответов, использовании подходящих верхних временных границ для обеспечения стабильного качества обслуживания. Данное постоянное и предсказуемое поведение, в свою очередь, упрощает обработку ошибок, укрепляет доверие пользователей и призывает их к дальнейшему взаимодействию. Разработанное приложение реагирует настолько быстро, насколько это возможно.
Отказоустойчивость — приложение остается отзывчивым при возникновении сбоя. Это применимо не только к высокодоступным, критически важным системам — любая отказо-неустойчивая система не будет отзывчивой в случае отказа. Устойчивость достигается за счет репликации, локализации, изоляции и делегации. Отказы не выходят за пределы модуля, и посредством изолирования модулей друг от друга можно быть уверенным в том, что отдельные части приложения могут отказать и восстановиться после сбоя, при этом не приводя к падению всего приложения. Восстановление каждого отказавшего модуля делегируется другому, внешнему модулю, а высокая доступность достигается посредством репликации. Клиенты модуля не имеют головной боли с обработкой отказа модуля.
Эластичность — приложение остается отзывчивым под различной нагрузкой. Реактивные приложения могут реагировать на изменения нагрузки через увеличение или уменьшение ресурсов, предназначенных для ее обработки. Это предполагает архитектуру, не имеющую точек блокировок или центральных узких мест, что выражается в способности к шардингу и репликации модулей и дальнейшим распределением нагрузки между ними. Результатом этого является масштабируемость при помощи недорогого апаратного обеспечения.
Ориентированность на передачу сообщений — реактивные приложения ориентируются на асинхронную передачу сообщений для установки границ между модулями, которые обеспечивают слабую связанность, изоляцию, прозрачность место-
положения и предоставляют средства для делегирования ошибок как сообщений. Введение явной передачи сообщений дает возможности для балансировки нагрузки, эластичности и управления потоками данных посредством формирования и мониторинга очереди сообщений, и снижения пропускной способности, когда необходимо. Прозрачность местоположения позволяет использовать одну и ту же логику обработки ошибок как на кластере, так и на одном узле. Неблокирующие коммуникации позволяют получателям сообщений потреблять ресурсы только тогда, когда они активны, что ведет к меньшим издержкам при работе приложения.
Таким образом, используя CQRS для оптимизации операций чтения, обеспечиваем отзывчивость приложения, а эластичность и отказоустойчивость реализуются с помощью паттерна Event Sourcing, когда отдельные части приложения могут отказать и восстановиться после сбоя, при этом не приводя к падению всего приложения. Восстановление каждого отказавшего модуля делегируется другому, внешнему модулю, а высокая доступность достигается посредством репликации. Клиенты модуля не имеют проблем с обработкой отказа модуля.
4. Пример модели проектного решения
Для организации разработано проектное решение, основные положения которого приведены ниже. Так, существующее интеграционное решение (далее ИР) вида «точка-точка» создает определенные неудобства при добавлении новых сервисов и изменении реализованных. Разрабатываемое ИР будет полностью реализовано в SOA архитектуре с использованием продукта Oracle SOA Suite. Разрабатываемое решение проектируется с учетом возможного изменения интеграционной логики конечных систем без изменения бизнес-логики.
Для обеспечения отказоустойчивости, надежности и масштабируемости решение построено с использованием Event Driven Architecture. Это означает, что все системы, которым требуется сервис от ИР, должны его получать с помощью двух асинхронных сообщений: запроса и ответа. В случае, если система-потребитель не поддерживает такое взаимодействие, то на шине реализован адаптер, осуществляющий эмуляцию асинхронных запросов и ответов.
Подход Event Driven Architecture обеспечивает два важных преимущества:
- вызывающая система не зависит от доступности и времени отклика вызываемых систем, не держит вычислительные ресурсы (потоки), связанные с ожиданием ответа;
- так как в основе EDA лежит обмен сообщениями, это позволяет централизованно, с помощью средств администрирования интеграционного решения, управлять количеством обработчиков входящих сообщений, т. е. управлять нагрузкой на сервера приложений (рис. 10).
Все интегрируемые внешние системы для взаимодействия с ИР должны обеспечить передачу сообщений по заранее оговоренному формату и протоколу. Структура сообщений и описание используемого протокола должны быть описаны в техническом задании. Передаваемые сообщения могут содержать дополнительную техническую информацию: идентификатор сообщения; приоритет сообщения; время обращения к ИР; идентификатор сессии; имя пользователя; хеш пароля пользователя; идентификатор системы инициатора запроса; идентификатор вызываемого сервиса в ИР; корреляционный идентификатор.
Рисунок 10. Event Driven Architecture
Если часть из этих параметров не будет передаваться из ИС, то ИР на уровне адаптеров будет формировать эти значения. В интеграционном решении предполагается наличие обменов между многими системами. Каждая система имеет свой формат представления бизнес-объекта. Таким образом, при передаче бизнес-объекта из одной системы в другую требуется преобразование. Чем больше систем и связей между ними, тем большее число преобразований необходимо реализовы-вать (п х т, где п — число потребителей и т — число провайдеров).
Избежать данного недостатка позволяет концепция канонической модели данных (ASBO-GBO-ASBO). При использовании такой концепции используем общий
бизнес-объект (GBO) при осуществлении взаимодействия внутри интеграционной шины. При таком подходе число преобразований сокращается до n + m.
При добавлении новой системы нам необходимо лишь реализовать два преобразования из ASBO в GBO (для последующей обработки сообщения в шине) и из GBO в ASBO (для отправки сообщения из шины в конечную систему). Данные преобразования производятся в адаптерах к соответствующим конечным системам (рис. 11).
Рисунок 11. Каноническая модель данных (ASBO-GBO-ASBO)
Для взаимодействия с каждой внешней системой разрабатывается отдельный адаптер. Если с внешней системой используется несколько протоколов взаимодействия, то для каждого протокола может быть разработан отдельный адаптер. Внутри адаптера реализуется логика обработки протокола взаимодействия с внешней системой, а также преобразование формата сообщений в канонический формат (GBO), затем адаптер передает сообщение на обработку определенному интеграционному сервису.
Интеграционный сервис — набор программных компонентов, реализующих определенную интеграционную логику. Интеграционные сервисы выполняют различную функциональность (проверка, обогащение, маршрутизация сообщений), а также могут взаимодействовать с адаптерами и другими интеграционными сервисами. Интеграционные сервисы позволяют повторно использовать реализованную функциональность, что уменьшает трудоемкость разработки при подключении новых внешних систем и изменении существующей функциональности сервисов.
Для подключения новой внешней системы к существующему интеграционному сервису необходимо разработать адаптер, реализующий взаимодействие по используемому протоколу и преобразующий формат данных в канонический, при этом данный адаптер не будет влиять на правильность работы других компонентов ИР (сервисов). При необходимости изменить существующий сервис может потребоваться внесение изменений, как в интеграционный сервис, так и в адаптеры, которые задействованы в изменяемом бизнес-процессе.
При внесении изменений в существующие сервисы нужно руководствоваться правилами совместимости с ранее реализованным функционалом. Если же совместимость реализовать невозможно, то нужно увеличить версию измененных компонентов. При этом если не все потребители сервиса могут перейти на новую модель данных, то появляется новая версия сервиса для тех потребителей, которые перешли на новую модель. А старые сервисы, работающие со старой моделью, остаются доступными для текущих потребителей. Общая схема по внедрению новой функциональности изображена на рис. 12.
При проектировании и разработке интеграционного решения используются следующие технологии: SOA, SCA, Java EE, XML, Web services, JMS, JDBC, Oracle AQ. ИР будет разрабатываться на базе интеграционной платформы OracleSOASuite. Для служебных баз данных интеграционной платформы использeуется СУБД Oracle 11g. Основное средство разработки программных модулей, это JDeveloperStudio 11.1.1.6.0 .
Рисунок 12. Схема по внедрению новой функциональности
5. Анализ возможности использования сервис-ориентированных решений в облачных сервисах
Попытка объединить все доменные области в одной большой модели значительно повышает сложность разрабатываемой системы, поскольку все объекты и взаимодействия между ними начинают зависеть друг от друга, причем, зависеть как
от компонентов и действий всей системы в целом, так и от каждой ее составляющей. Результат — беспорядочные запросы, хаотичность всей системы в целом. Помимо этого, масштабируемость системы определяется возможностями базы данных, однако классические реляционные базы плохо масштабируемы, и, даже для незначительного увеличения производительности, скорее всего, потребуется основательное обновление всей системы. Таким образом, это подводит к пониманию eventual consistency, когда при проектировании новой информационной системы или модернизации уже существующей необходимо закладываться не на локальную архитектуру, а на распределенную [17]. То есть если мы имеем сколь угодно много небольших доменных моделей и несколько реплик только для чтения, это не просто делает систему независимой от сторонних модулей и процессов, но и гарантирует получение оперативной информации при достаточно высокой производительности.
Здесь необходимо упомянуть про атомарность процессов — сервисы должны быть автономны и, как следствие, идемпотентны. Если это условие не соблюдается, то происходит потеря масштабируемости приложения.
Концепция применения облачных технологий заключается в предоставлении неограниченно масштабируемых сервисов. Конечно, такие сервисы имеют технические лимиты, в лучшем случае, стоимость использования станет главным ограничением для достижения такого лимита. В то же время потребитель сервисов не может воспользоваться масштабируемой инфраструктурой в полном объеме, если его собственная архитектура не масштабируется. Для достижения максимальной эффективности в облачном кластере необходимо, чтобы масштабируемость потребителя сервиса и его провайдера были эластичны. Провайдер разрабатывает свои сервисы и дает рекомендации по их использованию своим потребителями, так как не может вмешиваться в логику работы своих потребителей и посредников. С точки зрения потребителя, необходимо избегать монолитных решений, узких мест или мест, которые не могут воспользоваться преимуществами от предоставления сервиса по запросу в своих архитектурах, а при наличии таковых, по возможности, делать рефакторинг для получения максимальной отдачи от ^пользования облачных технологий.
Увеличение ресурсов дает пропорциональный прирост производительности. Эластичность масштабируемых сервисов возможна в гетерогенной среде (различная пропускная способность участков сети, различная конфигурация серверов).
Маcштабируемые приложения эффективны относительно операционных издержек, снижают риски и имеют минимальные капитальные затраты (из-за уменьшения стоимости использования единиц владения с увеличением таковых).
Масштабируемые решения являются отказоустойчивыми и надежными. Такие характеристики должны быть основой архитектуры потребителей сервисов и их
поставщиков для достижения паритета масштабируемости, что и является основным фактором увеличения производительности.
Традиционный подход к использованию собственной инфраструктуры не оставляет права на ошибку при прогнозировании необходимого для данного решения количества вычислительных ресурсов. В средних и крупных компаниях оборудование закупается заранее и предполагает определенный период использования. Иными словами, нет возможности докупить вычислительные мощности в краткосрочной перспективе, избавиться от лишних и т. д. В случае заниженной оценки необходимых вычислительных ресурсов возможен дефицит производительности из-за увеличения потока данных. В противном случае, инвестиции, вложенные в оборудование, не будут приносить дивидендов и даже становиться источником дополнительных затрат (стоимость энергозатрат), в то время как сервисы будут иметь значительный излишек вычислительных ресурсов.
Эластичность облачных сервисов позволяет инфраструктуре близко соответствовать объему возложенных на нее задач и реагировать на его изменения. Это увеличивает загрузку производственных мощностей поставщика услуг и позволяет всем его потребителям иметь общий пул резервов, что в конечном счете снижает стоимость владения ресурсами. Таким образом, эластичность — одно из фундаментальных свойств облачных сервисов. Основными метриками эластичности облаков является способность увеличивать и уменьшать вовлеченные вычислительные ресурсы для обеспечения нефункциональных требований к производительности с минимальными рывками [24].
Важно отметить, что способность к эластичной масштабируемости лежит в основе преимуществ использования облачных технологий. Архитекторы облачных приложений должны применять эту концепцию при проектировании своих приложений для эффективного использования арендуемых вычислительных ресурсов.
Классические корпоративные приложения проектировались из расчета жестко заданных вычислительных мощностей, а не гибкой и предопределенной инфраструктуры. Корпорации не имеют ежедневного плана по развертыванию нового оборудования, и, как результат, многие архитекторы не закладывали в своих проектах динамическое выделение ресурсов или их уменьшение. Время развертывания и стоимость приобретения ресурсов достаточно дороги для компаний, а общей практикой было прогнозирование с достаточным запасом на весь предполагаемый жизненный цикл решения. Более того, архитекторы никогда не занимались оптимизацией совместного использования ресурсов. Это было приемлемой практикой, так как оборудования хватало с запасом при должном прогнозировании. Понятие «эластичность масштабируемости» было недооценено, поскольку получение новых вычислительных ресурсов за минуты было возможно. Облачные технологии изменили
такой подход, отпала необходимость заказывать новое оборудование на месяцы вперед или ждать очереди на неиспользуемые аппаратные ресурсы. Облачная инфраструктура может предоставить необходимые мощности за минуты после получения соответствующего запроса или делать это автоматически, снижая риски потребителя. В то же время можно отказаться от лишних на данный момент ресурсов для снижения операционных затрат [25].
Эластичность масштабируемости — это требование к архитектуре (свойство системы). Например, такое свойство помогает определиться, какой компонент системы или ее слой может стать эластичным, что необходимо предпринять, чтобы данный компонент стал масштабируемым, какое влияние от реализации эластичности будет оказано на всю архитектуру системы в целом. Для достижения горизонтальной масштабируемости требуются определенные подходы. Эластичная масштабируемая архитектра в облаке требует особого мышления от архитектора системы. Другими словами, при миграции в облачную инфраструктуру необходимо спроецировать спецификацию архитектуры на возможности облачной инфраструктуры (нужно обратить внимание, что далеко не всегда облачная инфраструктура может предоставить специфичное оборудование и, следовательно, может существенно отличаться от традиционной модели развертывания). Негативным примером решения могут быть служить: добавить некоторое количество RAM для данного сервера, если единственный экземпляр базы данных требует более производительный накопитель. Архитектор облачных решений должен понимать, что облачная инфаструктура предоставляет абстрактные ресурсы, и такие ресурсы могут стать мощным подспорьем при облачной модели развертывания контура кластера. Например, если облачный провайдер не может предоставить виртуальный сервер с большим объемом RAM, можно попробовать использовать распределенный кэш (Hazelcast, Infinispan, Coherence, Ignite, memcached, jgroups), либо воспользоваться партиционированием данных на нескольких серверах. Если база данных имеет повышенные требования к накопителю, который не может быть гарантирован провайдером облачного сервиса, можно воспользоваться несколькими рекомендациями. Для профиля нагрузки, смещенного в сторону интенсивного чтения, можно увеличить количество хранилищ, предназначенных для чтения. В качестве альтернативы можно воспользоваться шардированием данных или использовать другую базу данных, которая обладает лучшей масштабируемостью.
Практическое правило: нужно оставаться пессимистом во время принятия архитектурных решений в облаке — нужно предполагать, что сбой может произойти в любом компоненте контура. Или, другими словами, всегда нужно проектировать, реализовывать и развертывать систему для автоматического возобоновления после сбоя. Необходимо ответить на вопрос, как поведет себя система, если катастрофа
случится с инфраструктурой приложения. Необходимо закладываться на большее число запросов к системе, необнаруженные ошибки программирования внутри своего приложения и в инфраструктуре провайдера облачного сервиса. Чтобы создать надежное масштабируемое приложение в облаке, необходимо продумать возможные сбои и адекватную реакцию на них со стороны приложений до того, как это произойдет.
Облачные технологии дополняют SOA-архитектуру слабосвязанными компонентами, которые лучше масштабируются. Ключевым фактором является отсутствие жестких связей одних компонентов с другими, т. е. если один компонент отключен, не отвечает или перегружен, то другие компоненты продолжают работать и сбоя не происходит. В конечном счете слабосвязанность изолирует различные слои и компоненты приложения таким образом, что архитектура приложения взаимодействует асинхронно с другими составляющими по принципу черного ящика. Например, сервера приложений должны быть отделены от веб-серверов и серверов баз данных. Сервера приложений ничего не знают о внутреннем устройстве вебсерверов и наоборот. В случае потоковой обработки данных необходимо создать асинхронный компонент, который не зависит от всех остальных.
Горизонтально масштабируемые слабосвязные компоненты, взаимодействующие асинхроннно — это основа облачных решений. В случае невозможности использования некоторых компонент в облаке возможна гибридная модель развертывания. В такой модели есть компоненты как в составе собственной инфраструктуры потребителя, так и облачные компоненты, которые располагаются в пространстве облачной инфраструктуры. Для защиты от привязки к поставщику возможно использование нескольких облачных инфраструктур разных провайдеров. В таких вариантах развертывания сетевой трафик может стать значительной частью операционных затрат [26].
6. Заключение
Проведен теоретический анализ сервис-ориентированной архитектуры и шаблона CQRS, его суть, особенности, преимущества и недостатки; проведен подробный анализ использования изучаемого шаблона в программной разработке на примере Scala-кода, где были рассмотрены основные составляющие CQRS подхода, такие, как Query (запрос) и Command (команда). Рассмотрена техническая реализация предлагаемого проектного решения, приведена логическая схема внутренней организации интеграционного решения, описаны интеграционные шаблоны, перечислены средства технической реализации предлагаемого интеграционного решения и проведен анализ возможности использования сервис-ориентированных решений в облачных сервисах.
Литература
[1] Nikulchev E., Pluzhnik E., Biryukov D., Lukyanchikov O., Payain S. Experimental study of the cloud architecture selection for effective big data processing // International Journal of Advanced Computer Science and Applications. 2015. Vol. 6, No. 6. P. 22-26.
[2] Sadalage P. J., Fowler M. NoSQL Distilled. — New Jersey : Pearson Education - 2013
[3] Abdullin R. DDDD, CQRS and Other Enterprise Development Buzz-words [Электронный документ] Software Design, 2010. Режим доступа: https://abdullin.com/post/dddd-cqrs-and-other-enterprise-development-buzz-words/
[4] Meyer B. Eiffel: A language and environment for software engineering // Journal of Systems and Software. 1988. Vol. 8. No. 3. P. 199-246.
[5] Betts, D., Dominguez, J., Melnik, G., Simonazzi, F., Subramanian, M.Exploring CQRS and Event Sourcing. A journey into high scalability, availability, and maintainability with Windows Azure. — Microsoft, 2012.
[6] Mitra T. Architecture in practice. Part 2: An introduction to SOA solution scenarios. — IBM, 2007 (http://www.ibm.com/developerworks/webservices/library/ar-arprac2/index.html)
[7] Ye C., Jacobsen H. A. Whitening SOA testing via event exposure // IEEE Transactions on Software Engineering. 2013.Vol. 39. No. 10. P. 1444-1465.
[8] Sommerville I. Software engineering. International Computer Science Series. 7th ed. — Addi-son Wesley, Pearson Education, 2004
[9] Erl T. Service-oriented architecture (SOA): concepts, technology, and design. — Prentice Hall Ptr, 2005.
[10] Bien A. Real World Java EE Patterns: Rethinking Best Practices. — Press.adam-bien.com., 2012.
[11] NiltoftP., Pochill P. Evaluating Command Query Responsibility Segregation. — Lund : Lund University, 2013.
[12] Young G. CQRS. [Электронный ресурс]. http://goodenoughsoftware.net/2012/03/02/cqrs/
[13]Michelson B.M. Event-Driven Architecture Overview. — Patricia Seybold Group, 2011.
[14] Evans E.J. Domain-Driven Design: Tackling Complexity in the Heart of Software. — Amsterdam : Addison-Wesley Longman, 2004.
[15] Freeman E. Head First Design Patterns. — O'Reilly, 2004.
[16] FowlerM. Patterns of Enterprise Application Architecture. — Addison-Wesley, 2003.
[17] Nikulchev E., Pluzhnik E., Biryukov D., Lukyanchikov O. Designing applications in a hybrid cloud // Contemporary Engineering Sciences. 2015. Vol. 8, No. 21. P. 963-970.
[18] Akidau T., Bradshaw R., Chambers C., at al. The dataflow model: a practical approach to balancing correctness, latency, and cost in massive-scale, unbounded, out-of-order data processing // Proceedings of the VLDB Endowment. 2015. Vol. 8. No. 12. P. 1792-1803.
[19] Roestenburg R., Bakker R., Williams R. Akka in action. — Manning Publications Co., 2015.
[20] Vernon V. Implementing domain-driven design. — Addison-Wesley, 2013.
[21] Extract, Transform, and Load Big Data with Apache Hadoop. — Intel, 2013 (https://software.intel.com/sites/default/files/article/402274/etl-big-data-with-hadoop.pdf)
[22] BernhardtM. Reactive Web Applications. — Manning Publications, 2015.
[23] Acharya A., Bishop S., Hopkins A. at al. Patterns: Implementing an SOA using an enterprise service bus. — IBM, International Technical Support Organization, 2004.
[24] Pluzhnik E., Lukyanchikov O., Nikulchev E., Biryukov D. Developing middleware for hybrid cloud computing architectures // 2015 International Conference «Stability and Control Processes» in Memory of V.I. Zubov (SCP). — IEEE, 2015. P. 586-588.
[25] Pluzhnik E., Nikulchev E., Payain S. Optimal control of applications for hybrid cloud services // 2014 IEEE World Congress on Services (SERVICES 2014). — IEEE, 2014. P. 458-461.
[26] Никульчев Е.В., Плужник Е.В., Лукьянчиков О.И. Проектирование распределенных информационных систем обработки больших объемов данных в гибридной облачной инфраструктуре // Вестник РГРТУ. 2014. № 50-1. С. 135-138.
Авторы:
Дмитрий Юрьевич Пимкин — магистрант, Московский технологический институт
Евгений Витальевич Никульчев — доктор технических наук, профессор, проректор по научной работе, Московский технологический институт
Using Commands Query Responsibility Segregation (CQRS) for Developing Distributed Information Services
Dmitry Pimkin, Evgeny Nikulchev
Moscow Technological Institute 38A, Leninskiy pr., Moscow, Russia, 119334
e-mail: [email protected]
Abstract. Distributed computing systems are now becoming very common. Big data, application integration, personalized services have requirements to architecture systems: scalability, fault tolerance, quality of data delivery. To meet these requirements require the use of specialized approaches to software development. The article deals with the principle of imperative programming CQRS (Command Query Responsibility Segregation), which is multiparadigm and can be used in object-oriented, functional, mixed and procedural programming. The detailed analysis CQRS use in software development in the Scala-code example; examined the technical realization of the project solutions, as well as an analysis of the possibility of using cloud services. Key words: imperative programming, service-oriented architecture, distributed information systems, cloud services, scalable system architecture.
References
[1] Nikulchev E., Pluzhnik E., Biryukov D., Lukyanchikov O., Payain S. (2015) International Journal of Advanced Computer Science and Applications, 6(6):22-26.
[2] Sadalage P. J., Fowler M. (2013) NoSQL Distilled. Pearson Education.
[3] Abdullin R. DDDD, CQRS and Other Enterprise Development Buzz-words. Software Design, 2010. https://abdullin.com/post/dddd-cqrs-and-other-enterprise-development-buzz-words/
[4] Meyer B. (1988) // Journal of Systems and Software, 8(3):199-246.
[5] Betts, D., Dominguez, J., Melnik, G., Simonazzi, F., Subramanian, M.Exploring CQRS and Event Sourcing. A journey into high scalability, availability, and maintainability with Windows Azure. — Microsoft, 2012.
[6] Mitra T. (2007) Architecture in practice. Part 2: An introduction to SOA solution scenarios. IBM. (http://www.ibm.com/developerworks/webservices/library/ar-arprac2/index.html)
[7] Ye C., Jacobsen H. A. (2013) IEEE Transactions on Software Engineering, 39(10):1444-1465.
[8] Sommerville I. (2004) Software engineering. International Computer Science Series. 7th ed. Addison Wesley, Pearson Education.
[9] Erl T. (2005) Service-oriented architecture (SOA): concepts, technology, and design .Prentice Hall Ptr.
[10] Bien A. (2012) Real World Java EE Patterns: Rethinking Best Practices. Press.adam-bien.com., 2012.
[11] Niltoft P., Pochill P. (2013) Evaluating Command Query Responsibility Segregation. Lund University.
[12] Young G. (2012) CQRS. http://goodenoughsoftware.net/2012/03/02/cqrs/
[13]Michelson B.M. (2011) Event-Driven Architecture Overview. Patricia Seybold Group.
[14] Evans E.J. (2004) Domain-Driven Design: Tackling Complexity in the Heart of Software. Addison-Wesley Longman.
[15] Freeman E. (2004) Head First Design Patterns. O'Reilly.
[16] Fowler M. (2003) Patterns of Enterprise Application Architecture. Addison-Wesley.
[17] Nikulchev E., Pluzhnik E., Biryukov D., Lukyanchikov O. (2015) Contemporary Engineering Sciences, 8(21): 963-970.
[18] Akidau T., Bradshaw R., Chambers C., at al. (2015) Proceedings of the VLDB Endowment, 8(12):1792-1803.
[19] Roestenburg R., Bakker R., Williams R. (2015) Akka in action. Manning Publications Co.
[20] Vernon V. (2013) Implementing domain-driven design. Addison-Wesley.
[21] Extract, Transform, and Load Big Data with Apache Hadoop. Intel, 2013 https://software.intel.com/sites/default/files/article/402274/etl-big-data-with-hadoop.pdf
[22] Bernhardt M. (2015) Reactive Web Applications. Manning Publications.
[23] Acharya A., Bishop S., Hopkins A. at al. (2004) Patterns: Implementing an SOA using an enterprise service bus. IBM, International Technical Support Organization.
[24] Pluzhnik E., Lukyanchikov O., Nikulchev E., Biryukov D. (2015) Developing middleware for hybrid cloud computing architectures. In 2015 International Conference «Stability and Control Processes» in Memory of V.I. Zubov (SCP), pp. 586-588.
[25] Pluzhnik E., Nikulchev E., Payain S. (2014) Optimal control of applications for hybrid cloud services. In 2014 IEEE World Congress on Services (SERVICES 2014). pp. 458-461.
[26] Nikulchev E.V., Pluzhnik E.V., Lukyanchikov O.I. (2014) Vestnik RGRTU, 50-1:135-138. [In Rus]