Микросервисный підхід у веб-розробці: micro frontends

До недавнього часу JavaScript використовувався для таких примітивних завдань, як зміни кольору тексту на сторінці. Веб почав стрімко розвиватися, і, як наслідок, складність веб-додатків збільшилася. За останні 10 років у веб перекочувало більшість програм, які ми використовуємо щодня. Зараз вже важко уявити своє життя без Google Drive, Google Docs, YouTube і т. д.

У цій статті поговоримо про микросервисном підході в веб-розробки користувальницьких інтерфейсів.

Типове веб-додаток складається з HTML-верстки, CSS-стилів і JavaScript-коду, який дозволяє досягти максимального рівня інтерактивності і чуйності. Чим вище складність програми, тим складніше користувальницький інтерфейс, а внаслідок цього — і інструменти, які потрібні для його розробки. Саме тому фронтенд-розробка перетворилася з простого набору додатків для користувача інтерфейсу в складну екосистему з великою кількістю інструментів і високим порогом входу.

Проблема і рішення

Користувальницькі інтерфейси в інтернеті продовжують розвиватися. Підтвердження цьому можна побачити і на сайті :

Згідно зі статистикою, кожен місяць обсяг JavaScript-коду в веб-додатках зростає, що на більшості проектів веде до збільшення наступних параметрів:

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

Монолітна архітектура — це архітектурний підхід, в якому вся основна логіка програми зібрана в одному місці. Монолітне додаток складається з одношарового об'єднання різних компонентів в одне ціле.

Розробка нової функціональності в такому додатку, де великий обсяг застарілого коду, з кожним роком стає все дорожче для бізнесу. Зі схожою проблемою вже стикалися бекенд-розробники. Одне з рішень більшості проблем монолітної архітектури отримало назву «микросервисы» (Microservices).

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

Крім цього, можна виділити і інші достоїнства микросервисной архітектури в порівнянні з монолітною:

А чи можна використовувати микросервисный підхід у веб-розробки користувальницьких інтерфейсів? Відповідь: да! Можна і потрібно! Саме такий підхід і називають микрофронтенд (micro frontends).

Микрофронтенд (Micro frontends)

Микрофронтенд (micro frontend) — архітектурний підхід, в якому незалежні додатки зібрані в одне велике додаток. Він дає можливість об'єднати в одному додатку різні віджети або сторінки, написані різними командами з використанням різних фреймворків (див. рис. нижче).

Джерело

Головні переваги микрофронтенд-підходу — у розробці великих ентерпрайз-додатків:

Крім очевидних переваг такого підходу, у нього є і суттєві недоліки:

Використання такого архітектурного підходу на маленьких проектах і в маленьких командах несе більше проблем і додаткової складності в розробці, ніж переваг. Але великі проекти разом з розподіленими командами, навпаки, отримують більший виграш від створення микрофронтенд-додатків. Саме тому сьогодні микрофронтенд-архітектура вже широко використовується багатьма великими компаніями в своїх веб-додатках:

На жаль, поки що не існує конкретної специфікації для побудови микрофронтенд-архітектури. Ось, мабуть, найбільш доступні та прості способи і техніки для побудови микрофронтенд-додатків:

Пройдемося по кожному з перерахованих підходів.

IFrames

IFrame — це давня технологія, яка, незважаючи на всю свою неактуальність, дає можливість побудувати микрофронтенд-архітектуру. Використовуючи IFrame, кожен окремий віджет можна помістити в IFrame, який завантажує потрібну програму. При використанні такого підходу ви, швидше за все, зіткнетеся з наступними проблемами:

Вкрай не рекомендую будувати микрофронтенд-архітектуру, використовуючи IFrame. Сьогодні існують інші способи зробити це простіше і ефективніше.

Бібліотека Tailor.js

Тут ви можете прочитати більше про самій бібліотеці. Компанія Zalando створила цілу екосистему для побудови микрофронтенд-архітектури, і Tailor.js — це частина екосистеми. Особливість Tailor.js — те, що це пакет для Node.js, і орієнтований він на побудову микрофронтенд-архітектури з серверним рендерингом.

Для мене додаткова особливість цього пакета — недолік документації. Живі приклади проектів я зміг знайти тільки в окремих топіках GitHub, де звичайні користувачі запитують рада і прикріплюють посилання на свої репозиторії з кодом. Якщо вам потрібен серверний рендеринг, то ця бібліотека точно стане в нагоді.

Single-spa

Основною і, на мою думку, найкращий підхід у побудові микрофронтенд-архітектури — це фреймворк single-spa. Ось основні причини, по яких я раджу вибрати single-spa:

Single-spa — це фреймворк, який дає можливість об'єднати різні додатки, незалежно від використовуваної бібліотеки або фреймворку, в одне ціле. Під капотом single-spa набір існуючих інструментів разом з власними рішеннями:

Типове додаток з використанням single-spa виглядає так:

А ось так виглядає комунікація між окремими елементами микрофронтенд-архітектури, побудованої з використанням single-spa:

Джерело

Root Application — це корінь додатки. Саме тут відбувається підключення sigle-spa як основного фреймворку, а також конфігурація SystemJS для коректного завантаження зовнішніх додатків.

Кожне дочірнє додаток для коректної інтеграції повинно надавати публічні методи bootstrap, mount, unmount, які використовуються фреймворком single-spa для мануального бутстрэппинга програми. Майже кожен сучасний фреймворк існує готовий врапперов, який спрощує цю задачу і автоматизує якусь частину процесу. На сайті single-spa можна знайти список усіх фреймворків, під які існують готові врапперы.

Побудувати набір додатків з використанням микрофронтенд-підходу і single-spa можна як шляхом створення повністю всієї інфраструктури і додатків з нуля, так і на основі наявної програми. Розглянемо приклади того, як це виглядає, створюючи набір повністю нових програм з використанням React.js і Angular 8.

Конфігурація для білду React.js під single-spa представлена тут .

import React from 'react'
import ReactDOM from 'react-dom'
import singleSpaReact from 'single-spa-react'
import { property } from 'lodash'
import setPublicPath from './set-public-path.js'
const reactLifecycles = singleSpaReact({
React,
ReactDOM,
 loadRootComponent: () => import(/* webpackChunkName: "react-app" */'./App.js').then(property('default')),
domElementGetter,
})
export const bootstrap = [
 () => {
 return setPublicPath()
},
reactLifecycles.bootstrap,
]
export const mount = [
reactLifecycles.mount,
]
export const unmount = [
reactLifecycles.unmount,
]
export const unload = [
reactLifecycles.unload,
]
function domElementGetter() {
 let el = document.getElementById("react-app");
 if (!el) {
 el = document.createElement('div');
 el.id = 'react-app';
document.body.appendChild(el);
}
 return el;
}

Використовуючи існуючий врапперов single-spa для React.js ми створюємо інтерфейс з методами bootstrap, mount, unmount, де відповідно описуємо, як повинно бутстрэппиться наш додаток. Врапперов допомагає инкапсулировать внутрішню імплементацію та створити API для правильного підключення single-spa фреймворка.

Схожим чином це виглядає і для Angular 8.

import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { Router } from '@angular/router';
import { AppModule } from './app/app.module';
import { environment } from './environments/environment';
import singleSpaAngular from 'single-spa-angular';
import { singleSpaPropsSubject } from './single-spa/single-spa-props';
if (environment.production) {
enableProdMode();
}
const lifecycles = singleSpaAngular({
 bootstrapFunction: singleSpaProps => {
singleSpaPropsSubject.next(singleSpaProps);
 return platformBrowserDynamic().bootstrapModule(AppModule);
},
 template: '<app-root />',
Router,
 NgZone: NgZone,
});
export const bootstrap = lifecycles.bootstrap;
export const mount = lifecycles.mount;
export const unmount = lifecycles.unmount;

Так само, як і у випадку з React.js ми надаємо інтерфейс для ручного бутстрэппинга програми на Angular 8.

Крім використання специфічних врапперов, додаток має бути зібрано як amd-модуль. Кожен такий модуль асинхронно підключається в корінь всієї микрофронтенд-архітектури — Root Application. Нижче приклад елементарної імплементації.

<!DOCTYPE html>
<html>
<head>
 <title>Root application</title>
 <meta name="importmap-type" content="systemjs-importmap">
 <script type="systemjs-importmap">
{
 "imports": {
 "angular-app": "http://localhost:4201/main.js",
 "react-app": "http://localhost:4202/main.js",
 "single-spa": "https://unpkg.com/[email protected]/lib/system/single-spa.min.js"
}
}
</script>
 <!-- Loading of required libraries shouyld be added -->
</head>
<body>
 <header class="header-fixed">
<nav>
 <a href="/angular-app">Angular app</a>
 <a href="/react-app">React app</a>
 </nav> 
</header>
<script>
 System.import('single-spa').then(function (singleSpa) {
singleSpa.registerApplication(
'angular-app',
 () => System.import('angular-app'),
 location => location.pathname.startsWith('/angular-app')
singleSpa.registerApplication(
'react-app',
 () => System.import('react-app'),
 location => location.pathname.startsWith('/react-app')
)
singleSpa.start();
})
</script>
</body>
</html>

Дві головні частини, на які варто звернути увагу:

Як можна побачити, Root Application — простий HTML-файл з основними конфігураціями для завантаження інших додатків. В одному микрофронтенд-додатку можна зареєструвати безліч микроприложений. Якщо при реєстрації програми в 3-му параметрі методу registerApplication просто вказати /, такий додаток буде завантажуватися для кожного доступного адреси. Саме такий підхід бажано використовувати для створення навігаційної панелі або частин програми, які є загальними і не повинні завантажуватися повторно при переході між додатками.

Повністю робочий приклад того, як це працює, можна знайти в моєму GitHub-репозиторії .

У цьому репозиторії представлена покрокова імплементація микрофронтенд-архітектури, фінальна робоча версія — в гілці з назвою step-7.

Нам, розробникам, далеко не завжди доводиться створювати додатки з нуля. Single-spa дає можливість повністю використовувати існуючі програми як елементи микрофронтенд-архітектури, будь то Root Application або асинхронно завантажувані микроприложения.

Якщо з існуючої програми потрібно створити микроприложение, сумісний з single-spa і має можливість завантажуватися асинхронно, в першу чергу потрібно шукати відповідний врапперов. Для більшості сучасних фреймворків та бібліотек single-spa надає врапперы, готові до завантаження через npm, а також документацію, що описує, як їх правильно налаштувати. Якщо ж для використовуваного фреймворку не існує готового враппера, то завжди можна написати його самостійно. Налаштувавши врапперов і зібравши amd-модуль, ви отримаєте готове до підключення микроприложение; все, що залишиться, це додати відповідну конфігурацію Root Application. З власного досвіду скажу, що для сучасних фреймворків та бібліотек, таких як Angular 2+, React, Vue, які збираються з допомогою Webpack, конвертація в микроприложение проходить швидко і без додаткових танців з бубнами».

Якщо з існуючої програми потрібно створити Root Application, тоді в першу чергу потрібно index.html підключити бібліотеки для single-spa і додати всі необхідні для завантаження інших микроприложений конфігурації. Після того як ваш index.html став Root Application в single-spa, можна приступити до налаштування іншої частини програми для коректної роботи в single-spa-архітектурі так, як це описано в попередньому абзаці.

Проблеми та недоліки

Велика проблема для обох завдань — це старі (legacy) додатки, написані на старих фреймворках і стягуються з використанням старих інструментів. Наприклад, додаток на AngularJS або Backbone, яка збирається за допомогою gulp або grunt, може «з'їсти» дуже багато вашого часу, перш ніж буде коректно налаштовано. З власного досвіду можу виділити наступні проблемні місця:

На етапі конфігурації проблеми, з якими ви зіткнетеся, будуть найрізноманітніші, тому запасіться терпінням ;)

Крім складної конфігурації, виділимо проблеми, які можуть виникнути при побудові микрофронтенд-архітектури з використанням single-spa:

Висновки

Микрофронтенд-архітектура, як і будь-яка інша архітектура, вирішує одні проблеми, створюючи інші. Микрофронтенд-архітектура ускладнить ваше життя і життя вашої команди, якщо:

Один із частих питань: чи можна використовувати single-spa для міграції з одного фреймворку на інший (наприклад, з AngularJS на Angular 2+)? Можна, але не потрібно: практично завжди є більш легкий і нативний спосіб міграції, який вимагає менше конфігураційної роботи і який буде працювати швидше.

Опубліковано: 05/12/19 @ 11:00
Розділ Сервіси

Рекомендуємо:

Поради сеньйорів: як прокачати знання junior security specialist
Зарплатне опитування
«Я просто роблю те, що мені подобається». Як 18-річний студент навчає дітей програмуванню та видає підручники з Python
Go дайджест #11: мови 10 років, новинки в Go 1.14
PM дайджест #22: engineering mentorship, як проводять code review Google