Архитектуры на акторах: системы с моделью

Підписуйтеся на Telegram-канал «DOU #tech», щоб не пропустити нові технічні статті.

Архитектуры на акторах:
Вступление (Actors / Objects; Complexity; When to Distribute; Kinds of Actors)
Монолиты (Control/Data Flow; Reactor / Proactor / Half-Sync/Half-Async; Request State)
Простые системы (Notation for Diagrams; Sharding; Layers; Services / Pipeline)
Системы с моделью (Hexagonal / MVC; Blackboard / Message Bus; Microkernel / Plug-ins)
Фрагментированные системы (Mesh; SOA; Hierarchy; Design Space)

                                                Though my language is dead
                                             Still the shapes fill my head

В предыдущей части мы рассмотрели базовые варианты разделения программы (монолита) асинхронными интерфейсами:

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

  • Условие 1: данные можно разделить на много сетов, и большинство сценариев не трогают более одного сета данных.
  • Условие 2: нет общего (изменяемого в рантайме) состояния, влияющего на сценарии.

2. Разделение на слои — отделяет высокоуровневую логику от низкоуровневой и позволяет каждому уровню работать со своей скоростью и в своем мире.

  • Условие 1: мало взаимодействий между уровнями по сравнению с количеством логики внутри уровней.
  • Условие 2: нет кусков общего состояния, напрямую влияющих на несколько уровней одновременно.

3. Разделение на сервисы — даем независимость субдоменам.

  • Условие 1: большинство сценариев не вовлекают несколько субдоменов одновременно (но могут перепрыгивать между субдоменами по цепочке, цена перепрыгивания — увеличение времени отклика).
  • Условие 2: у субдоменов нет общего состояния.

Условия можно нарушать (слово «большинство» в описаниях), но ценой сложной злой тормозной нестабильной фигни (3-phase commit, orchestration, choreography, materialized views), которая тупо проигрывает тупому синхронному вызову метода класса в монолите.

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

Архитектуры с моделью:

Очень распространены (вездесущи) системы с монолитным (синхронным) горизонтальным слоем, покрывающим весь домен. Назовем его модель. Такой слой может находиться на разных уровнях абстракции и варьироваться по толщине (количеству кода) от основного компонента (гексагоналка) до тонкой прослойки (шина событий). Цель — интегрировать информационные потоки (control / data) и, возможно, данные других компонентов, разделенных по субдоменам. Иными словами — общий уровень собирает все в единую систему, и предоставляет возможность и правила общения для других участников.

В зависимости от того, модель более абстрактна, чем сервисы (находится выше них на диаграмме) или ближе к железу (под ними), будем различать П и Ц архитектуры, а также — их комбинацию Ж (с моделью посредине).

Гексагоналка (П)

Сверху — монолитный слой с высокоуровневой логикой (доменная модель), под ним — сервисы, работающие с периферией (адаптеры).

Плюсы:

  • Основная логика монолитна, легко писать и отлаживать 95% кода.
  • Поддерживаются сценарии любой сложности, если в них не очень много обращений к периферии (чтобы реже обращаться, можно самое полезное кешировать в верхнем слое).
  • Возможность шардинга бизнес-логики (модели), если состояние приходит вместе с запросом (stateless).
  • Высокоуровневая логика (доменная модель) и низкоуровневое обслуживание периферии (драйвера или переходники) могут работать в разных режимах (длительные расчеты против реал-тайм событий).
  • Высокая независимость в разработке и деплое бизнес-логики и каждого из адаптеров периферии.
  • Независимость режима работы и параллелизм между адаптерами периферии.
  • Быстрая обработка сообщений от периферии, если нет необходимости обращаться к основной бизнес-логике (канал адаптер-адаптер).
  • Легкость замены (или полиморфизма) для любого из адаптируемых компонентов периферии: мы уже отделены от них интерфейсами, приспособленными под нужды модели (бизнес-логики). То есть — защита от vendor lock-in.
  • Возможность разработки бизнес-логики при недоступной периферии (адаптеры подменяются заглушками).
  • Легкость тестирования бизнес-логики (поверх заглушек).
  • Воспроизводимость событий при однопоточной бизнес-логике (запись событий производят на входе или выходе очереди сообщений от адаптеров к доменной модели).

Минусы:

  • Бизнес-логика может сильно разрастаться — мы не очень много от нее отрезали. Хотя, интерфейсы к адаптерам, вероятно, будут иметь высокий уровень абстракции (доменные объекты, а не поля в базе данных), что должно слегка уменьшить количество кода в верхнем слое. То есть, переход к гексагоналке не поможет, если монолит умирает от доменной сложности.
  • Сценарии, затрагивающие несколько компонентов периферии, будут заметно медленнее и сложнее, чем у монолита.
  • Мы не можем напрямую использовать периферию через готовые библиотеки — относительно медленный старт проекта, пока напишутся все интерфейсы и адаптеры (или заглушки для начала работ над моделью, пока адаптерами занимаются другие команды).

Итого: очень много жирных плюсов для проектов среднего размера. Для мелких — плохо из-за медленного старта (не окупаются лишние усилия на интерфейсы и адаптеры). Для крупных — мы не разделили слишком сложную бизнес-логику, и все равно попадаем в Monolithic Hell [MP] нашим горизонтальным слоем (доменной моделью).

Финт ушами в том, что мы взяли легкость написания и железонезависимость бизнес-логики (которая у нас на диаграммах сверху) от Layers [POSA1], и при этом выделили каждой железке / (внешней зависимости) свой сервис (адаптер), разбив нижний слой на независимые субдоменные компоненты. Получилось удобнее, чем доменные сервисы, и гибче, чем слои. Цена по сравнению с этими двумя геометриями? Да, вроде, никакой. Небольшое проседание по скорости для высокоуровневых сценариев в рамках одного субдомена. Итого: пример, как разумная комбинация простых паттернов захватывает рынок — потому что имеет плюсы обоих родителей и не имеет многих их минусов. И да, здесь у нас аутсайдер — паттерн, не вошедший в известные книжки, опубликованный где-то в сетевых блогах, но при этом — полностью захвативший мир:

Классика: Ports and Adapters, (Re)Actor-fest.

Бэкенд: Hexagonal Architecture, Onion Architecture, Clean Architecture.

Почему то, что мы обсуждали — гексагоналка? Классические рисунки (и название) гексагоналки идут от диаграммы в радиальных координатах (радиус — уменьшение абстрактности логики, угол — субдомен). У нас — ортогональные координаты. Преобразование координат будет выглядеть так:

Для control flow систем уровень с доменной моделью включает в себя информацию о последнем известном состоянии системы. Например, если мы делаем управление для робота, то у нас будет синхронно доступно (закешировано) положение всех манипуляторов, скорость перемещения, и карта окружающего пространства. Это позволяет максимально легко и быстро (в оперативке, не обращаясь к периферии) рассчитывать последующие действия и реакцию на события. Для data flow систем весь объем данных (состояние системы) обычно слишком большой — он будет храниться в базе данных под одним из адаптеров, и подниматься в модель при надобности. В модели содержится либо только информация для обработки текущего запроса, либо еще и кеш состояний из недавних запросов, если это запрограммировано и помогает разгрузить базу.

Вызовы из модели к адаптерам могут быть синхронными (блокирующими — RPC) или асинхронными (req/cfm, notifications), либо синхронные для одних адаптеров (база данных) и асинхронные для других (сеть) — делают как удобнее для данной программы (часто определяется доменом).

При использовании control flow + data flow (стандартно в телекоме) модель служит для настройки потоков данных, а сами данные передаются между адаптерами напрямую, возможно — вообще без копирования.

Варианты:

Model-View-Controller [POSA1]

Можно считать (плиз, не материться!) одним из вариантов гексагоналки, по крайней мере — в оригинальном описании паттерна, где VC служили адаптером к оконной системе. Получается control flow пайплайн-гексагоналка (однонаправленная передача управления).

Цели MVC (перечисляю по [POSA1]) — разделение логики и интерфейса чтобы:

  • одну модель можно было представить разными видами (у гексагоналки — запустить с разной конфигурацией периферии);
  • реал-тайм обновления интерфейса (у гексагоналки — независимость режима (скорости) работы адаптеров друг от друга и от модели);
  • независимая разработка интерфейса так как он меняется чаще, чем модель (у гексагоналки — независимая разработка адаптеров и модели);
  • легкость переноса программы на другую оконную систему (у гексагоналки — защита от vendor lock-in).

То есть, диаграммы двух старых известных паттернов одинаково выглядят в нашей системе координат, и тут оказывается, что у них идентичные цели и свойства, только описано в разных выражениях.

Еще можно сравнить MVC с нашей диаграммой для пайплайна. Пайплайн тоже содержит два низкоуровневых компонента (source и sink), но верхний уровень с логикой у него разделен на кучу отдельных фильтров. Значит: нет вот этой вот страшной ментальной пропасти между гексагоналкой и пайплайном. Если вдруг мы писали пайплайн, а потом начались такие хотелки заказчика, что фильтры пайплайна перестают быть независимыми друг от друга — да плюньте вы на эту идеальную архитектуру, слепите все фильтры в синхронный моноблок — и получите вместо пайплайна гексагоналку. Раз уж оказалось, что написанный пайплайн плохо подходит под домен или нужно додавить производительность.

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

Half-Sync/Half-Async [POSA2]

Мы уже дважды видели это название: сначала — как внутреннее устройство актора-монолита, затем — глянули более детально как слоистую систему, вспомнив, что Half-Sync/Half-Async родился как описание операционных систем. При этом, на самом деле, нижний слой у ОС — не монолитный, а состоит из нескольких независимых компонентов (подсистем, драйверов). И эти драйвера как раз:

  • абстрагируют пользовательские программы от железа;
  • незаметно для приложений верхнего слоя управляют железом;
  • могут обновляться независимо друг от друга и от пользовательских приложений.

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

Half-Async/Half-Async [POSA2]

То же самое, но с асинхронным общением между верхним и нижним уровнями — когда критична скорость принятия решений и возможность передумать в любой момент в зависимости от ситуации. Если у Half-Sync/Half-Async в верхнем слое живут реакторы [POSA1] с блокирующими вызовами к периферии, то у Half-Async/Half-Async в верхнем слое — один проактор [POSA1], асинхронно отсылающий сообщения и мгновенно реагирующий на входящие события.

Посмотрим, что будет, если инвертировать диаграмму:

Blackboard / Middleware (Ц)

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

Плюсы:

  • Хорошо распутывает сложность бизнес-логики, если сделать много субдоменных сервисов.
  • Отвязывает имплементацию сервисов по свойствам (время отклика, язык).
  • Независимый шардинг сервисов.
  • Независимый деплой сервисов.
  • Возможность для сервисов работать с общими данными без сильного усложнения кода.

Минусы:

  • Общая модель данных уменьшает свободу сервисов к независимым изменениям.
  • Скорее всего, данные тяжело будет масштабировать и шардить.
  • Требования разных сервисов к хранению и передаче данных могут конфликтовать.
  • Нижний слой (и его железо) является единой точкой отказа для системы.

Итого: быстрый старт, когда нужно сделать несколько независимых по свойствам сервисов.

Классика: Blackboard [POSA1], Middleware.

Бэкенд: Shared Database (Smart UI of [DDD]), (Message) Broker [POSA1, EIP], Message Bus [EIP].

Здесь видим два варианта, оба широко используются. В обоих, насколько я знаю, обычно используются синхронные вызовы (direct method call / RPC) к модели.

Blackboard/ Shared Database (хранение данных)

Независимые сервисы пользуются общей моделью данных (базой данных в случае бэкенда), которая сама разруливает синхронизацию изменений. Simple & Stupid. При этом оно работает, пока сервисы простые, а база — быстрая и надежная.

Особое внимание можно уделить CQRS без разделения базы. Такая архитектура используется потому, что:

  • У команд и кверей сильно разные свойства (время отклика, язык программирования, требования к доменной модели). Поэтому удобно их закодить в независимых высокоуровневых модулях.
  • Разделение базы на OLTP + OLAP и поддержка их консистентности — нетривиальны [DDIA] и требуют кучу Ops усилий.

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

Middleware / Message Bus (передача данных)

В нижнем слое находится модель компонентов, знающая о всех сервисах (Broker) и позволяющая сервисам обращаться друг к другу (Message Bus). Также шина данных может гарантировать доставку (хранить неполученные сообщения) и логировать сообщения (что даст возможность воспроизведения глюков — если сервисы детерминистичны — и регрессионного тестирования).

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

Shared Model (Ж)

Ц поверх П, у них общая модель. В зависимости от типа модели:

Microkernel [POSA1] (broker + message bus as model)

Еще один вариант операционных систем (QNX, будущая Fuchsia), считается более надежным, так как ядро живучее. Под моделью компонентов (микроядром) живут драйвера (сервисы, обслуживающие железо), над ним — программы (сервисы с пользовательской нагрузкой / бизнес-логикой). Ядро, как модель компонентов, одновременно предоставляет коммуникацию между сервисами (Message Bus) и присматривает за их жизнеспособностью (Broker). Поэтому оно «микро» — чтобы негде было заглючить или упасть. В бекендах роль микроядра выполняет DevOps инфра (кафко-k8s) и облачный провайдер. В Akka или Elixir микроядром является сама платформа, распределяющая акторы по серверам и передающая между ними сообщения.

Применение — выживание софта любой ценой. Возможно благодаря тому, что микроядро, как брокер, ни от кого не зависит, но обо всех все знает.

DSL (Interpreter [GoF]) / Metadata (Reflection [POSA1]) / Plug-ins [SAP] (domain model)

Смесь гексагоналки и blackboard. Доменная модель (поддержка бизнес-логики) из гексагоналки является базой для высокоуровневых сценариев вышестоящей blackboard. Как примеры можно привести скриптование поведения в играх, или даже SQL-запросы (DSL), работающие поверх движка базы данных (domain model), сидящего поверх адаптеров ОС и файловой системы.

Сверху — метасервисы или сценарии, манипулирующие моделью, наблюдающие события и настраивающие поведение модели в соответствии с бизнес целями и фидбеком от модели. Под ними — монолитная модель со своими правилами и логикой домена. Ниже — служебные сервисы, предоставляющие ресурсы.

Обычно на диаграмме будут разные субдомены выше и ниже модели (прошлая статья начиналась с абстрактной картинки для игрушки. На ней под моделью идет разделение по железу, над моделью — по модулям метаданных). Плюсы и минусы — сумма от blackboard и гексагоналки.

Применение — создание удобного языка (интерфейса) для написания супервысокоуровневой логики ценой общего усложения системы.

(Re)Actor-with-Extractors (R/W or Black and White) (domain model)

Вырожденный случай рефлексии, а еще — забавный изврат для безлочного использования кучи ядер процессора. Есть пул потоков, обслуживающий все объекты в модели. Жизнь программы делится на череду фаз, как игра в мафию:

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

Скедюлер следит за сменой дня и ночи: он дает возможность запуститься каждому объекту каждый день и каждую ночь. Смена времени суток (read / write режим) наступает только тогда, когда все объекты завершили свои действия для текущего режима (чтение взаимодействий и планирование днем / изменение состояния ночью).

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

Итого:

Мы рассмотрели архитектуры с монолитным горизонтальным слоем: практически вездесущие гексагоналку и message bus, и чуть более экзотическую blackboard. Как обычно — попробовали их смешать. И, как обычно, для каждой из рассмотренных геометрий исторически сложилось много названий и подтипов.

Что еще можно нарисовать на плоскости (или собрать из прямоугольников)? Дальше порисуем фрагментированные (не имеющие больших кусков) системы.

Литература

[DDD] Domain-Driven Design: Tackling Complexity in the Heart of Software. Eric Evans. Addison-Wesley (2003).

[DDIA] Designing Data-Intensive Applications: The Big Ideas Behind Reliable, Scalable, and Maintainable Systems. Martin Kleppmann. O’Reilly Media, Inc. (2017).

[EIP] Enterprise Integration Patterns. Gregor Hohpe and Bobby Woolf. Addison-Wesley (2003).

[GoF] Design Patterns: Elements of Reusable Object-Oriented Software. Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides. Addison-Wesley (1994).

[MP] Microservices Patterns: With Examples in Java. Chris Richardson. Manning Publications (2018).

[POSA1] Pattern-Oriented Software Architecture Volume 1: A System of Patterns. Frank Buschmann, Regine Meunier, Hans Rohnert, Peter Sommerlad and Michael Stal. John Wiley & Sons, Inc. (1996).

[POSA2] Pattern-Oriented Software Architecture Volume 2: Patterns for Concurrent and Networked Objects. Douglas C. Schmidt, Michael Stal, Hans Rohnert, Frank Buschmann. John Wiley & Sons, Inc. (2000).

[SAP] Software Architecture Patterns. Mark Richards. O’Reilly Media, Inc. (2015).

Архитектуры на акторах:
Вступление (Actors / Objects; Complexity; When to Distribute; Kinds of Actors)
Монолиты (Control/Data Flow; Reactor / Proactor / Half-Sync/Half-Async; Request State)
Простые системы (Notation for Diagrams; Sharding; Layers; Services / Pipeline)
Системы с моделью (Hexagonal / MVC; Blackboard / Message Bus; Microkernel / Plug-ins)
Фрагментированные системы (Mesh; SOA; Hierarchy; Design Space)

👍ПодобаєтьсяСподобалось6
До обраногоВ обраному6
LinkedIn
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter

Цікава стаття, але мені б хотілось її українською. Дякую!

Редакція пропонувала перекласти та, на мою думку, нема сенсу половину циклу публікувати однією мовою, половину — іншою, бо статті в циклі сильно пов’язані.

Відповідно, мабуть, треба просити редакцію перекласти весь цикл, але вони навряд чи погодяться, якщо не буде масових запитів.

Я, з свого боку, думаю перекладати англійською. Картинки з англійськими написами мають підійти і для українського перекладу.

Зрозуміло. Тепер побачив всі інші статті.

Дякую за коментар і побажання щодо української версії! Ми розглянемо можливість підготувати українську версію усієї серії про Архітектури

американською краще от сам же ж доу назівається американською і ще ні хто не прийшов питати «чому не українською» ))

Та знаю, треба. Ніяк не зберуся.

Денис, спасибо большое за статью!
Можете помочь понять основную разницу между гексагональной архитектурой (ДДД предполагаю) и чистой архитектурой (под ней понимаю разделение логики между хендлером, сервисом и репозиторием и общением между ними через интерфейсы)? В идеале ещё отличия от шины.

Гексагоналка идет отсюда alistair.cockburn.us/hexagonal-architecture это школа дизайн паттернов в стиле книжек GoF, POSA и конференции PLoP. Описана, вроде, в 2005 году как Ports and Adapters и не имеет отношения к DDD — основной смысл гексагоналки — абстрагировать бизнес-логику от нюансов имплементации библиотек, баз данных и сетевых протоколов — то есть, избежать Vendor Lock-In. Второй плюс — это то, что реальные компоненты можно подменять заглушками для тестирования, или даже для параллельной разработки (пока компонент, на который мы должны опираться, не готов). Достигается инверсией зависимости (кажется, так это называют в SOLID), когда между библиотекой/базой/протоколом и нашей логикой вставляется адаптер (из книжки GoF), который переводит стандарт данный чужой библиотеки в нами написанный интерфейс, родной для нашей бизнес-логики. В результате, наша бизнес-логика опирается на свои собственные интерфейсы, и ничего не знает о том, какими библиотеками или базами данных эти интерфейсы имплементятся под капотом.

В DDD (2003 год) такая граница называется Anti-Corruption Layer но, видимо, на ней не было поставлено достаточно ударения, по сравнению с остальной начинкой книжки — и название прошло относительно незамеченным. Тем более, что Hardware Abstraction Layer и OS Abstraction Layer к тому моменту широко использовались десятилетиями.

Итого: хоть DDD описал подход на 2 года раньше, но сейчас везде (микро)сервисы рисуют шестиугольничками. Думаю, потому, что DDD концентрирует внимание на слоях внутри бизнес-логики (шестиугольника), а Ports and Adapters с шестиугольной формой оказалось мемом, который легко рисовать, и который вообще не влазит в детали того, что там внутри — главное — это отделение внутренностей сервиса от внешнего мира.

Чистая архитектура, кратко описанная тут herbertograca.com/...​-the-shoulders-of-giants это 2012 год. По сути, дядя Мартин спер, обработал напильником и обозвал по-своему Onion Architecture herbertograca.com/...​09/21/onion-architecture из статьи 2008 года, в которой сперли идеи DDD и завернули их в форму луковицы, похожую на гексагоналку.

Вот совмещение всех этих архитектур — я так хорошо не напишу, потому что не вникал в детали herbertograca.com/...​ow-i-put-it-all-together

Денис спасибо огромное! Вы очень понятно объясняете и подробно такие сложные вещи, из Вас отличный ментор :)

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

Я свой проект принимал за чистую архитектуру (хендлеры сервисы репозитории общение через интерфейсы, моки с заглушкамия), а получается это как раз гексагоналка как понимаю. Надо править описание проекта в гитхабе :)

Что у меня в резюме получилось (поправьте пожалуйста если не прав), вышла гексагоналка в 2003, потом в 5 году ДДД которую сервисы микросервисы подхватили. А чистая архитектура это вовсе не новое ничего и не альтернативное.

Не совсем.
Гексагоналка в DDD называется Anti-Corruption Layer. Вообще, многие идеи имеют много названий. И часто одну идею придумывают разные люди независимо, и описывают разными словами с разных сторон. Как в сказке про мудрецов и слона. Паттерны и появились как описания вот таких идей, повторяющихся (переизобретенных) в разных проектах. Только вот когда паттернов стало много, их самих начали переизобретать)

DDD — это большой фреймворк для работы с очень сложными проектами. Он включает в себя десятки идей, одной из которых является Anti-Corruption Layer, очень похожий на гексагоналку. И книжка с DDD вышла за 2 года до статьи с гексагоналкой.

Чистая архитектура базируется на DDD, так как у нее рассматривается слоистая начинка сервиса, как и в DDD. У них может различаться порядок слоев и зависимости между слоями. В отличии от гексагоналки, у которой есть только монолитный сервис (и не интересно, что там внутри) и библиотеки, которыми сервис пользуется. Поэтому и гексагоналка шире применима — можно написать невнятный код, и сказать, что это — гексагон, если у него нет внешних зависимостей. А вот написать правильный DDD или «чистый» код — это надо читать книжки, вникать. И может оказаться, что книжки по DDD, Onion и Clean в каких-то деталях противоречат друг другу. Кто знает, права ли хоть одна из них, или каждая применима в небольшом количестве случаев? В результате, гексагоналка чаще применима, как более простое понятие.

Сервисы и микросервисы также не имеют прямого отношения к DDD. Это тоже — разные школы. То есть, внутри микросервиса может быть DDD, а может — не быть. DDD может быть построен из микросервисов, а может — по SOA, а может — как монолит.

Сложно :) Ещё не мой уровень видно, не понимаю, не работал с этим практически. Концепция Хендлер Сервис Репозиторий ещё понятна, а вот все эти вещи пока нет. Нужно практически работать с этим, тогда все встанет на свои места. Спасибо большое 🙏

Концепция Хендлер Сервис Репозиторий

где она описана? Clean Architecture многословна, а в обзорной статье этого нет. Я не понимаю, что вы имеете в виду.

Практически никто не работал со всеми вариантами архитектур. Может, 10% или 20%. Остальное — из литературы, при этом — лучше читать первоисточники или большие книжки, чем обзоры — из обзоров непонятно, почему проблему решают именно так. Или проблемы вообще не видно — технологии меняются, и то, что в 80х год пилили и придумывали паттерны, сейчас веб фреймворк делает в 20 строчек из коробки. Но те же паттерны их 80х могут помочь понять, как делать похожую проблему, для которой нет фреймворка. Или, наоборот, понять, какие будут проблемы. За все приходится платить. Ну и да, даже не зная старых подходов, если подумать, есть шанс их переизобрести. Откуда и взялись паттерны — несколько раз переизобретенные решения.

Могу показать один вариант простого проекта на Golang, думаю по коду на практике проще понять что там используется.

Нет, извините.
Мне еще следующую часть статьи доделывать.

Буду прошлые стать изучать (и следующие).
Суть в том что хендлер ( контроллер ) занимается отдачей JSON например, в сервисе уровнем ниже бизнес логика, в репозитории еще ниже все операции с хранилищем.

Гексагоналка.
Отдача и формат отдачи (JSON) могут легко меняться при переходе на новые платформы, поэтому бизнес-логика не должна от них зависеть.
Так же и база данных может меняться со временем жизни проекта.
То есть, «хендлер» и «репозиторий» здесь — адаптеры из гексагоналки. Бизнес-логика должна ничего не знать о них, а использовать удобные для логики интерфейсы, которые хендлер и репозиторий имплементят. Тогда логика будет жить вне зависимости от изменений хендлера и репозитория.

Вы помогли намного лучше понять мне эти вещи. Премного благодарен!

Заходите в чатик — там много опытных порассказывают всякого вредного t.me/swarchua и поотвечают на вопросы

не нашёл внятного объяснения различий между чистой архитектурой, гексагоналкой и ДДД

ДДД — это не архитектура, в ДДД можно применять разные архитетуры в том числе и гексагоналку

Шина сообщений занимается передачей данных между компонентами, знает только о том, какие компоненты есть в системе, и кто на какие темы подписан. То есть, у шины есть модель связности, или набор компонентов — но нет доменной модели (правил бизнес-логики). Рантайм (интегрированная шина, брокер, микроядро) может поднимать сервисы в соответствии с конфигом и нагрузкой, давать возможность сервисам обмениваться пакетами данных, но он не знает, что какой сервис делает, и не понимает данные, которые передает. Также, шина обычно близко к железу, и оперирует байтами и сокетами.

В гексагоналке или чистой архитектуре центральную часть занимает доменная модель — сценарии использования программы и классы, описывающие бизнес-сущности. То есть, если шина данных оперирует байтами, то гексагоналка оперирует юзерами, банковскими счетами, и маршрутами доставки товаров. Намного выше уровень абстракции. И если сценарий изменяет адресат доставки, в коде нигде нет проверок того, сколько байтов занимает класс адресата.

По сути:
В гексагоне внутри находится все полезное, что система делает для бизнеса — то, ради чего программистам платили деньги. У меня этот слой вверху как более абстрактный (приближенный к человеческим понятиям).
В шине внутри биты и сокеты — она ничего не делает для бизнеса. Это — служебная библиотека, обеспечивающая связь других компонентов. Поэтому я ее рисую с противоположной стороны — снизу. Система координат была в начале предыдущей статьи dou.ua/forums/topic/36465

Благодарю! Теперь буду изучать Ваши остальные статьи, оказывается эта не первая :)

Шина сообщений занимается передачей данных

Не совсем раскрыл основную идею шины данных.
Без шины данных — все интерфейсы будут беспорядочно мапится друг на друга.
Если у тебя 100 сервисов, то у всех этих сервисов нужно прописать адресса друг друга. Если какой-то сервис молча сменил место жительства, нужно отискать все зависимые сервисы от него и перепрописать в конфигах новый адресс сервиса. Шина данных вносит упорядоченность в этот хаос. Теперь каждому новому сервису, вместо конфига с 99 урлами к другим сервисам, нужно прописать только один урл — выход на шину данных.
И на шине данных уже разберутся куда доставить твой пакет.
И отсюда сразу вытекает множество интересных приемов по обработке сообщений. Например навешивание нескольких одинаковых сервисов на шину, для разгребания пакетов параллельно. Навешивание сервисов старых и новых версий одновременно, для обеспечения обратной совместимости и плавного инкремента системы. Горизонтальное масштабирование. Профилирование пакетов. Гарантированая доставка. Асинхронная обработка пакетов. Широкий бродкаст сразу на пачку сервисов. Приоритеты обработки пакетов и прочье прочье, что невозможно в классической архитектуре endpoint-to-endpoint, где сервисы напрямую выходят на какой нибудь REST api.

А шина данных это обязательно распределённая система, сервисы микро и иже с ними, или может быть условный монолит?

А шина данных это обязательно распределённая система, сервисы микро и иже с ними, или может быть условный монолит?

Если формулировка именно «шина данных» то да, это распределенная система.
Но сам концепция шины часто встречается в монолитах. Например весь оконный интерфейс в Windows построен на сообщениях и обработки сообщений контролами в оконной функции, что дает все теже приимущества описанные выше. Также шина часто встречается в железе, например на материнской плате или в самом процессоре компоненты тоже коммуницируют через шину.

Шина может передавать сообщения и на одной железке — например, в Эрланге.

Монолит-монолит — вряд ли, потому что:
1) Шина нужна для передачи сообщений.
2) Сообщения обычно передаются между асинхронными компонентами.
3) Асинхронные компоненты обычно не лезут друг другу в память (работают как акторы).
4) Раз они не лезут в память, а общаются только сообщениями, то им относительно пофиг, лежать в одном процессе, на одной железке, или в сети (есть разница в сериализации и в системных ресурсах).
5) Если оно уже может бежать на нескольких железках — то оно точно не классический монолит, а ближе к какому-то варианту сервисов.

Хотя есть понятие «распределенный монолит» — это когда вот эти общающиеся сервисы сильно зависят друг от друга. Обычно такое считают неправильной архитектурой, потому что толку мало, проблем — много (тормоза, нестабильность).

Эликсир на сколько читал в этом плане похож на Эрланг, его виртуальную машину использует. Из коробки заточен под многоядерерность и распределенные системы.

Монолит-монолит — вряд ли, потому что:
1) Шина нужна для передачи сообщений.
2) Сообщения обычно передаются между асинхронными компонентами.
3) Асинхронные компоненты обычно не лезут друг другу в память (работают как акторы).
4) Раз они не лезут в память, а общаются только сообщениями, то им относительно пофиг, лежать в одном процессе, на одной железке, или в сети (есть разница в сериализации и в системных ресурсах).

щас как раз пытаюсь убедить «заказчика» что «его монолит» не сколько устарел в эпоху когда оно запускается на 64 ядрах на 4 numa sockets и «многопоточность» это собственно и есть ... вот это всё )) даже если оно всё в пределах одного адресного пространства одного процесса просто ядер давно уже на столько много что вопросы «а чего это вы задействуете целый 1 (одно) ядро» и «как нам задействовать наш новый крутой проц на 100500 ядер целиком» уже приходят уже от клиентов «заказчика»

ЗЫ: не уверен что это сработает по скольку вообще-то это таки «взять всё и пере писать» но честно методы «работы многопоточности» чисто пугающие архаичностью ну и загадочностью ко всему ))

Еще одна разница между гексагоналкой и шиной в том, что гексагон обычно сидит на одной железке, часто — в одном процессе, тогда как шина (Message Bus, Middleware) — это виртуальная абстракция, размазанная по сети. То есть, в шине участвуют куча инстансов библиотеки из разных процессов на разных машинах, но все вместе они создают иллюзию связности, когда на одной машине ты в эту шину запихал сообщение, а на другой машине — оно из этой же шины вывалилось.

тогда как шина (Message Bus, Middleware) — это виртуальная абстракция, размазанная по сети.

ну да всё только начинается когда сама шина уже «размазанная по не скольким instances»

Ну шину обычно из коробки берут. Как и базу данных. Поэтому их рисуют черным квадратом)

Написать и одно, и другое — та еще история news.ycombinator.com/item?id=18442941

news.ycombinator.com/item?id=18442941

Проект не удалять не рефакторить ! Будет капчей для gpt-3 !

Підписатись на коментарі