Я планирую написать эту статью, чтобы четко рассказать, как понять и реализовать шаблон Порты и адаптеры (гексагональная архитектура) в JavaScript.

Я решил построить эту публикацию в другом формате. Разработан репозиторий с реальной реализацией блога с использованием гексагональной архитектуры.

Цель этого репозитория - представить конкретные примеры концепций, которые я объясню.

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



Если какая-либо концепция будет продолжена в репозитории, ссылка будет в конце темы.

Резюме

  • Введение
  • Идея и мотивация
    Почему шестиугольник?
  • Настраиваемая зависимость
    Концептуальный уровень поверх настраиваемой зависимости
    Инвертирование и внедрение зависимости с помощью TypeScript
    Теперь в JavaScript
  • Гексагональная архитектура
    Порт
    Адаптер
    Вариант использования
  • Заключение
  • Ссылки

Идея и мотивация

Архитектурный шаблон Порты и адаптеры , опубликованный в 2005 году Алистером Кокберном, основан на идее отделения приложения от его технологий, таких как фреймворки, в-третьих -party реализации или библиотеки и т. д.

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

Почему шестиугольник?

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

По словам Алистера Кокберна, по этим причинам квадрат не подходил бы. Геометрические фигуры, такие как пятиугольник или семиугольник, очень сложно нарисовать - пусть это будет шестиугольник.

Наконец, демистификация аргументов в том, что число 6 или стороны шестиугольника имели бы какое-то значение для концепции.

Настраиваемая зависимость

Прежде чем мы перейдем к концепции, а затем к ее реализации, мы должны иметь четкое представление о шаблоне настраиваемой зависимости.

Настраиваемая зависимость понимается путем объединения двух очень известных отраслевых шаблонов: Внедрение зависимостей и инверсия зависимостей. В статье Шаблоны портов и адаптеров (гексагональная архитектура) Хуана Мануэля Гарридо Паза есть специальная и хорошо написанная глава на эту тему: https: // jmgarridopaz .github.io / content / hexagonalarchitecture.html # tc3 .

Короче говоря, мы рассматриваем настраиваемую зависимость как зависимость объекта (или функции) от интерфейса (или протокола). Я добавляю слова «функция» и «протокол». »В предыдущем предложении, так как мы более или менее будем следовать этой идее в примере реализации данной публикации.

Устанавливая зависимость для структуры протокола, мы инвертируем зависимость этого объекта для другого конкретного объекта. Затем мы можем ввести некоторую конкретную структуру, которая уважает такой интерфейс, предлагаемый протоколом.

Концептуальный уровень поверх настраиваемой зависимости

В исходной публикации Алистера Кокберна в разделе «Связанные шаблоны» последняя (по крайней мере на момент написания) тема «Инверсия зависимостей (внедрение зависимостей) и весна».

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

Очевидно, что порты и адаптеры состоят из других агрегированных концепций, но я рискну сказать, что одной из его прочных основ будет конфигурируемая зависимость. Имея такую ​​ясность, мы приводим пример применимости как в TypeScript, так и в JavaScript.

Инвертирование и внедрение зависимости с помощью TypeScript

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

Этот вариант использования направлен на создание сообщения в блоге. Я назову это «Создать сообщение».

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

Это свойство передается через конструктор, поэтому, если я запустил метод «execute» варианта использования, не назначая ему ничего, ничего работать не будет. Следовательно, при создании экземпляра класса-примера нам нужно будет назначить конструктору объект, который соответствует интерфейсу «База данных». Таким образом, нам придется внедрить зависимость.

Теперь в JavaScript

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

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

Стратегия здесь состоит в том, чтобы использовать функцию более высокого уровня в качестве «конструктора» классов. На момент написания этой публикации ES2021 уже довольно давно имеет функцию собственных классов. Однако я решил использовать этот пример, чтобы приблизиться к написанию кода большинством консервативных разработчиков JavaScript.

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

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

Это не самый безопасный способ моделирования интерфейса, но это то, что у нас есть сейчас в JavaScript.

Учитывая предупреждения, я выполняю те же два шага примера на TypeScript, отличаясь только отсутствием ссылки «this».

Шестиугольная архитектура

Порт

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

В примере темы «Понимание раз и навсегда» разграничением объекта «База данных» в коде будет Порт .

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

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

Подробное чтение с примером реализации JavaScript см. В разделе Порты - гексагональная архитектура на основе дистилляции →.

Адаптер

В дополнение к концепции Порт на внешней стороне приложения мы используем шаблон проектирования Адаптер, изначально опубликованный в книге Шаблоны проектирования: элементы многоразового объектно-ориентированного программного обеспечения, чтобы перевести язык внешних технологий на язык, поддерживаемый протоколом порта.

Таким образом, мы получаем мастерство управления разрабатываемым кодом.

Если код выполняется в тестовом сценарии, мы создаем вариант использования с «имитацией» версии реализации.

Если вариант использования выполняется в производственной среде, мы внедряем официальную версию, которая нам действительно нужна.

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

Подробное описание с примером реализации JavaScript см. В разделе Адаптеры - гексагональная архитектура на основе дистилляции →.

Сценарий использования

В исходной публикации Алистера Кокберна ссылка на внутреннюю часть кода не рассматривалась как вариант использования.

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

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

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

Это мнение подтверждается концепцией диаграммы вариантов использования, позже предложенной Иваром Якобсоном на печально известном языке нотации UML.

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

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

Если мы присмотримся, участники на диаграмме вариантов использования UML будут отличным показателем адаптеров в гексагональной архитектуре .

Подробные сведения с примером реализации JavaScript см. В разделе Примеры использования - гексагональная архитектура на основе дистилляции →.

Заключение

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

Большой совет: Не используйте этот архитектурный шаблон без создания модульных тестов.

Противодействие приведенному выше приговору было бы почти как смертный приговор.

Одним из мотивов Алистера Кокберна была бы разработка реализаций «макетов» внешних технологий для создания настоящих модульных тестов. . Тесты, проверяющие единую концептуальную функциональность.

Лучшие практики в области разработки программного обеспечения и архитектуры основаны на других передовых методах.

использованная литература

  • Шестиугольная архитектура - https://alistair.cockburn.us/hexagonal-architecture/
  • P Каталога ЕЭЗ - Репозиторий https://martinfowler.com/eaaCatalog/repository.html
  • Шаблоны проектирования: элементы объектно-ориентированного программного обеспечения многократного использования - Эрих Гамма, Ричард Хелм, Ральф Джонсон доктор и Джон Влиссидес.
  • Единый процесс разработки программного обеспечения - Ивар Якобсон, Грэди Буч и Джеймс Рамбо.
  • Чистая архитектура: руководство по структуре и дизайну программного обеспечения - Роберт К. Мартин
  • Программист-прагматик - Дэвид Томас и Эндрю Хант.
  • Объектно-ориентированная разработка программного обеспечения: подход, основанный на сценариях использования - Ивар Якобсон
  • Написание эффективных сценариев использования - Алистер Кокберн
  • SUT, xUnit Patterns - http://xunitpatterns.com/SUT.html
  • Шаблон Порты и адаптеры (шестиугольная архитектура), Хуан Мануэль Гарридо Паз - https://jmgarridopaz.github.io/content/hexagonalarchitecture.html

Больше контента на plainenglish.io