From AC
Within BC
You may do some RPC
//SOA Rap
Делить на части в SOA – это как раздевать капусту (Это я продолжаю заметки по ходу курса. Лучший способ утрясти в голове – попробовать рассказать кому-то).
На сервисы поделили – можно делить дальше! :)
Business Components
Сервис, олицетворяя собой business capability, состоит из одного или более Business Component (BC).
Чаще всего всё начинается с одного BC в рамках сервиса и, чаще всего, так и заканчивается.
Однако, иногда возникают ситуации, когда код сервиса начинает “ветвиться”. Udi привёл такой пример: допустим, в наш магазин обратился Стив Балмер, который хочет купить 10 000 единиц товара. Станем ли мы обслуживать его как обычного кастомера? Скажем ли мы ему: “добрый вечер, Стив, спасибо, что зашли, встаньте в конец очереди, пожалуйста, не желаете ли стакан воды”? Попросим ли его в качестве подтверждения дать нам личную кредитную карту на случай? (учитывая, что у Майкрософт есть всего две схемы оплаты и по какой они намерены расплачиваться, они выбирают сами)
Скорее всего нет. А тут ещё Стив Джобс обещал на неделе заглянуть…
В общем, в коде нашего сервиса начинают появляться какие-нибудь условия вроде if (strategicCustomer) {…}.
Раз появилось, два появилось, три появилось – и вот тут уже надо задуматься, а не имеем ли мы дело с другим бизнес-кейсом (в рамках того же capability)?
Рекомендуется в этом случае поговорить с людьми со стороны бизнеса, так как можно получить как ответ “нет, все кастомеры у нас одинаковы, это вот единственное такое исключение”, так и ответ “да, у нас есть определённая категория привелигированных кастомеров, с которыми мы работаем по несколько иной схеме”.
Часто, продолжив разговор, можно обнаружить, что на самом деле бизнес потенциально хотел бы работать с такими кастомерами по ещё более иной схеме, но даже не задумывался над этим, так как давно привыкли думать в рамках того, что позволяет наш софт. И, конечно, на будущее ещё есть планы (Стив Джобс ведь и друзей приведёт).
Если это различие существенно – ура, у нас появился отдельный кейс, выделяемый в отдельный BC.
Создаётся новый BC так: берём тот, что у нас есть, делаем полную копию, убираем из первого всё, что касается кастомера стратегического, из второго – всё то, что касается кастомера обычного.
Отдельно подчёркивается: кроме BC сервис не содержит ничего, кроме, может быть, неймспейса.
Правила простые:
-
Никакого кода помимо кода, содержащегося в BC, в сервисе нет вообще.
-
Два BC друг с другом на уровне кода не связаны никак (никакого наследования одного от другого, никакого общего переиспользуемого кода)
-
Сба BC “выглядят” идентично: они реагируют на одни и те же события, посылают одни и те же сообщения в тех же фазах.
-
Снаружи сервиса BC не видны, сколько их там и что там – не имеет значения, снаружи сервис – это по-прежнему просто сервис.
Таким образом, BC – это этакий мини-сервис, или реализация сервиса в контексте какой-то бизнес-вариации.
Было много вопросов относительно дублирования кода, на которые Udi отвечал примерно так: “если мы обнаружили, что бизнес-аспекты разные, то со временем они будут расходиться дальше, дальше и дальше”, что, в общем-то, имеет смысл.
Как в том примере со стратегическим клиентом: Быть может такому клиенту не имеет большого смысла показывать шоппинг-карту, как обычному клиенту? 10 000 наименований в карте вряд ли кому-то будут интересны. Быть может, схема оплаты для таких клиентов совершенно другая, с ними удобнее работать по предварительному инвойсу, а не по кредитной карте? Быть может схема базы данных для такого случая будет несколько отличаться от “обычной”? А быть может и вообще будет другая база данных?
До тех пор, пока все BC сервиса имеют одинаковый “интерфейс”, то есть, откликаются на одни и те же события (OrderApproved) и посылают одни и те же события (ClientBilled) они “взаимозаменяемы” и снаружи невозможно сказать, какой именно BC обработал событие.
А как они реализованы – их БиСи’шье дело. Ибо инкапсуляция и single responsibility principle.
Autonomous Components
В терминах кода BC представляет собой solution (как я говорил раньше о сервисе, когда сервис у нас был одним BC по сути), состоящий из разных “кусочков”: обработчики событий и сообщений, возможно проект схемы БД, возможно UI какой-то…
Всё это дело можно подразделить на набор неких Autonomous Components (AC).
Правила такие:
-
AC отвечает за определённые типы сообщений (в идеале – за один тип сообщения)
-
AC использует Bus (вход-выход)
-
Допустимы RPC calls в рамках одного BC (внутри AC – who cares, между AC – acceptable, за пределы BC – запрещено)
Важно ещё то, что AC – это единица развёртывания (deployment unit). То есть, AC могут быть установлены на на один или более серверов, в одном или более экземпляре и т.д.
Таким образом, да, BC (а, следовательно, и сервис) у нас может находиться физически на разных машинах и всё такое.
Я вот так себе это в OneNote зарисовал (и да, это у меня почерк такой, особенно когда вертикально на экране пером пишу):

Промежуточный результат
Так, как AC у нас это, получается, такие маленькие проектики, суть маленькие DLLs (единицы развёртывания), отвечающие за малое количество сообщений, то и кода там в них, по идее, до��таточно не много.
То есть, мы получили такую декомпозицию, при которой чётко и понятно, что и в каком кусочке находится, при этом эти кусочки достаточно изолированы друг от друга и относительно просты (хоть и являются частью большой сложной системы).
Рассмотрим простой случай, когда производится работа над “неразделяемыми” данными, то есть, данными, которые не изменяются одновременно сразу несколькими пользователями. Например, user profile, или shopping cart (очень маловероятно, что в Вашу карту кто-то что-то ещё положит), или запрос в службу поддержки… Думаю, понятно, что практически в любой системе подавляющее большинство сценариев будет именно таким: крааайне маловероятно, что кто-то ещё будет менять те же данные (такие кейсы есть, но о них позже)
Поскольку AC относительно мал, отвечает за небольшой кусочек функциональности, конкурентности с данными никакой нет, то мы можем фактически свести его виду:
“Я умею сделать SELECT”. То есть, в достаточно большом количестве случаев нам может не понадобиться никакой дополнительной бизнес-логики в AC.
Показать цену для продукта?
Пожалуйста, SELECT * FROM Pricing WHERE ProductId = @id. Быть может, AND Date=@date.
По идее, поскольку данные у нас партицированы по сервису/BC, и мы уже не имеем имеем дела с некой таблицей Products, содержащей кучу полей, мы можем себе позволить эту звёздочку.
Мы можем себе позволить не иметь никакого ORM для этого AC и даже использовать DataSet. DataSet’ы – хорошие, пока не начинают применяться в немереных масштабах.
Это элементарно простой AC, выдающий цену продукта, профиль пользователя, наименование и описание продукта и т.д.
“Я умею обновить информацию”. Простой Update в базу данных. Мы не ожидаем, что кто-то ещё будет обновлять пользовательский профиль в то же самое время, это краааайне маловероятно, если не невероятно вообще. В “случае чего” же сработает правило “последний победил”, мы же всё равно так делаем! :) Что произойдёт в 99.9% систем, если на изменение профиля пользователя вдруг придут два запроса одновременно? (пользователь два раза нажал кнопку send?) Да ничего. Профиль запишется два раза и всего делов. Мы всё равно так делаем. По умолчанию. Но можно и не придерживаться умолчаний.
То есть, в достаточно большом количестве случаев наши AC будут простыми SELECT/UPDATE, что не может не радовать.
Для других, более сложных случаев с конкурентными данными используется CQRS, о чём, может быть, позже.