В одном из наших последних проектов нас попросили создать фреймворк для микросервисов. Мы хотели сделать что-то декларативное и простое в использовании. Мы также хотели скрыть сложность, вызванную несколькими общими средствами (ведение журнала, трассировки Zipkin, метрики InfluxDB и т. Д.). Для этого мы взяли несколько подсказок из инструмента, которым пользуемся каждый день: React.

Как работает React: версия TL; DR

В React мы используем JSX, чтобы объявить, каким должен быть наш UI. В следующем примере мы определяем компонент HelloWorld с помощью свойства name. Он, в свою очередь, использует компоненты p и span, которые предоставляются React.

JSX - это просто синтаксический сахар и должен использоваться вместе с транспилером, таким как Babel. Если вы запустите пример через Babel (с включенной поддержкой preset-react), вы получите результат, очень похожий на этот:

Мы видим, что компоненты - это просто функции. Мы используем их для создания элементов. Элемент - это просто метаинформация об иерархии компонентов и их свойствах (свойствах). В этом легко убедиться, если вы просто выведете JSX:

Итак, элемент - это просто структура данных. Он сообщает нам, к какому типу компонента принадлежит элемент, каковы его свойства и отношения между родительскими и дочерними компонентами (с помощью специального свойства children). Вы можете прочитать этот пост в блоге React, чтобы разобраться в этом подробнее. Там это гораздо лучше объяснено :)

Магия React происходит в ReactDOM.render. С помощью этой информации React создает виртуальную модель DOM и определяет наиболее эффективный способ визуализации представления, обрабатывает состояние пользовательского интерфейса, проксифицирует события DOM и т. Д. Он просто абстрагирует детали управления DOM, в то время как разработчик сосредотачивается на описании того, как UI должен быть. Аккуратный!

Наши цели

В этой статье мы постараемся решить лишь минимальную часть реальной проблемы. Мы хотим:

  • Создавайте микросервисы, использующие промежуточное ПО, и делайте это декларативно.
  • Скрыть тот факт, что мы внутри компании используем Koa.js в качестве базового фреймворка. Также сделайте возможным переход на другой фреймворк в будущем.
  • Добавьте одно общее средство к каждому микросервису: журнал запросов без дополнительного кода.
  • Никаких проверок работоспособности, никаких оптимизаций. Это просто игрушка 😉

Мы собираемся провести рефакторинг этого микросервиса Koa.js:

Записано так:

Как видите, здесь нет ссылок на Koa.js, общее ведение журнала будет добавлено службами, а код просто объявляет, как микросервис должен шаг за шагом реагировать.

Заимствование из React

Первым шагом должно стать создание наших компонентов. Компонент - это просто функция, которая с заданными реквизитами возвращает структуру данных (элемент). Мы будем использовать фабрику компонентов baseComponent, чтобы определить все наши компоненты.

Помимо хранения некоторой метаинформации, вновь созданный компонент будет иметь два метода: mix и handler.

mix позволяет нам добавлять новые методы в компонент и иметь гибкий API. Это немного страшно, но если вы не торопитесь, вы увидите, что мы просто добавляем и привязываем функции к нашему недавно созданному объекту. Эти функции могут возвращать либо значение, либо объект, что позволяет объединить в цепочку больше методов (следовательно, гибкий API).

handler - это свойство, которое дает нам реальный код, реализующий поведение компонента (это наш «рендер», похожий на реакцию). Он использует функцию построения. Мы поговорим об этих build функциях позже.

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

  • service может use несколько промежуточных программ.
  • middleware является оболочкой для асинхронной функции Koa.js. middleware, в свою очередь, используется как базовый компонент для определения конкретных промежуточных программ. В нашем случае: status, который устанавливает код состояния HTTP, и body, который устанавливает содержимое тела ответа.

Функция сборки

Второй и последний шаг - определить функцию build, которая принимает элемент и создает настоящий микросервис Koa.js:

  • Мы регистрируем разные конструкторы, по одному для каждого типа компонента / элемента в системе.
  • Функция build находит построителя для данного элемента, вызывает его и передает себя, чтобы позволить строителям рекурсивно создавать его дочерние элементы.
  • Конструктор service создает приложение Koa.js, добавляет промежуточное ПО для общего журналирования и, наконец, строит и добавляет промежуточное ПО, добавленное в service с помощью use.
  • Строитель middleware просто возвращает обернутую функцию.

Подводя итоги

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

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

PS, еще кое-что перед отъездом

Просто ради развлечения.

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

Ага. Возможно. Babel позволяет нам настроить способ преобразования JSX в вызовы функций, используя параметры pragma в presect-react.

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

Так весело! 😂