Slide 1

Slide 1 text

Павел Лакосников Senior engineer, юнит Antimonolith Тактика распила монолита

Slide 2

Slide 2 text

!2 Из монолита в микросервисы

Slide 3

Slide 3 text

Монолит 3 года назад !3

Slide 4

Slide 4 text

!4 Как выглядел монолит 3 года назад: 2.6к файлов и 200к строк PHP 1.5к html файлов 1к+ файлов js, lua, python, perl ServiceRegistry + Global как основные способ работы с базой Разветвлённая многоуровневая система наследования

Slide 5

Slide 5 text

!5 Как выглядел монолит 3 года назад: 2.6к файлов и 200к строк PHP 1.5к html файлов 1к+ файлов js, lua, python, perl ServiceRegistry + Global как основные способ работы с базой Разветвлённая многоуровневая система наследования

Slide 6

Slide 6 text

Он работал! !6

Slide 7

Slide 7 text

!7 Это был местами читаемый и неплохой код

Slide 8

Slide 8 text

!8 Он был надёжен

Slide 9

Slide 9 text

!9 Он был надёжен,
 если работал

Slide 10

Slide 10 text

!10 Он работал очень быстро

Slide 11

Slide 11 text

!11 Чем же он нам мешал?

Slide 12

Slide 12 text

!12 Не позволял быть гибкими

Slide 13

Slide 13 text

!13 Не позволял быть гибкими Не позволял командам быстро внедрять изменения, не вызывая «эффект лавины»

Slide 14

Slide 14 text

!14 Не позволял быть гибкими Не позволял командам быстро внедрять изменения, не вызывая «эффект лавины» Не позволял экспериментировать

Slide 15

Slide 15 text

!15 Не позволял быть гибкими Не позволял командам быстро внедрять изменения, не вызывая «эффект лавины» Не позволял экспериментировать Гигантскими тяжелыми моделями

Slide 16

Slide 16 text

!16 Начинаем распиливать…

Slide 17

Slide 17 text

Вендоризация !17

Slide 18

Slide 18 text

!18 Как выглядел код в монолите

Slide 19

Slide 19 text

!19 Пример сессий: Роутеры

Slide 20

Slide 20 text

!20 Пример сессий: Бэкофис

Slide 21

Slide 21 text

!21 Пример сессий: Авторизация

Slide 22

Slide 22 text

!22 Пример сессий: Мессенджер

Slide 23

Slide 23 text

!23 Пример сессий: Наследники

Slide 24

Slide 24 text

!24 Вендоризация 1. Собираем код в отдельный пакет 2. Прогоняем статический анализатор 3. Пишем тесты 4. Подключаем к монолиту как внешнюю библиотеку

Slide 25

Slide 25 text

!25 Зачем? 1. Контроль за изменениями 2. Изоляция всех внешних зависимостей 3. Одинаковое поведение у всех потребителей

Slide 26

Slide 26 text

!26 Как не надо делать

Slide 27

Slide 27 text

!27 Не писать тесты на перенесённый код

Slide 28

Slide 28 text

!28 Просто копировать файлики

Slide 29

Slide 29 text

!29 Переносить все в одну библиотеку

Slide 30

Slide 30 text

Вынос словарей !30

Slide 31

Slide 31 text

!31 Что именно Список городов Список районов Список категорий Список микрокатегорий

Slide 32

Slide 32 text

!32 Зачем? Это корневые зависимости, нужные не только в монолите, но и в большинстве сервисов

Slide 33

Slide 33 text

!33 Зачем? Это корневые зависимости, нужные не только в монолите, но и в большинстве сервисов Фиксация констант, интерфейсов

Slide 34

Slide 34 text

!34 Зачем? Это корневые зависимости, нужные не только в монолите, но и в большинстве сервисов Фиксация констант, интерфейсов Контроль за их изменением

Slide 35

Slide 35 text

!35 Зачем? Это корневые зависимости, нужные не только в монолите, но и в большинстве сервисов Фиксация констант, интерфейсов Контроль за их изменением Доступность для любых сервисов

Slide 36

Slide 36 text

!36 Как именно? Вендоризовали интерфейсы всех словарных сущностей, все константы

Slide 37

Slide 37 text

!37 Как именно? Вендоризовали интерфейсы всех словарных сущностей, все константы Отделили базу данных, хранящих словарные сущности

Slide 38

Slide 38 text

!38 Как именно? Вендоризовали интерфейсы всех словарных сущностей, все константы Отделили базу данных, хранящих словарные сущности Сделали микросервис, способный отдавать словарные данные в куче форматов

Slide 39

Slide 39 text

!39 Как именно? Вендоризовали интерфейсы всех словарных сущностей, все константы Отделили базу данных, хранящих словарные сущности Сделали микросервис, способный отдавать словарные данные в куче форматов Доработали сборку монолита, добавив этап сборки словарей

Slide 40

Slide 40 text

!40

Slide 41

Slide 41 text

Уменьшение глубины наследования !41

Slide 42

Slide 42 text

!42 Глубина наследования классов достигала 8 уровней

Slide 43

Slide 43 text

!43 Controller_Site_Profile_Settings_Ru

Slide 44

Slide 44 text

!44 Расширение ответа по цепочке от наследников к родителю

Slide 45

Slide 45 text

!45 На вход в метод отрисовки объявления $expandedItem = [ 'id' => $itemId, 'category' => [ 'id' => $itemCategory->id, 'name' => $itemCategory->name ] ];

Slide 46

Slide 46 text

!46 На выходе из цепочки 6 килобайт полезных данных 150 различных полей

Slide 47

Slide 47 text

!47 В самом конце … if ($item->isOnModerate()) { return $this->notFound(); } ];

Slide 48

Slide 48 text

!48 Проблемы Трудно рефакторить Легко задеть чужую функциональность Избыточность данных

Slide 49

Slide 49 text

!49 Что вообще происходит?

Slide 50

Slide 50 text

!50 Как именно? Диаграммы вызовов в разрезе запроса

Slide 51

Slide 51 text

!51 Как именно? Диаграммы вызовов в разрезе запроса Вдумчивый и внимательный рефакторинг

Slide 52

Slide 52 text

!52 NO SILVER BULLET

Slide 53

Slide 53 text

Классы вместо массивов !53

Slide 54

Slide 54 text

!54 Что именно? Многие сущности представлялись в коде как большие массивы с данными

Slide 55

Slide 55 text

!55 Что именно? Многие сущности представлялись в коде как большие массивы с данными Эти массивы передаются из метода в метод, по цепочке

Slide 56

Slide 56 text

!56 Что именно? Многие сущности представлялись в коде как большие массивы с данными Эти массивы передаются из метода в метод, по цепочке В процессе дополняются, расширяются и декорируют

Slide 57

Slide 57 text

!57 Зачем? Больше типизации => меньше ошибок

Slide 58

Slide 58 text

!58 Зачем? Больше типизации => меньше ошибок Понимание, какие поля и где используются

Slide 59

Slide 59 text

!59 Зачем? Больше типизации => меньше ошибок Понимание, какие поля и где используются Удаление неиспользуемых или устаревших полей

Slide 60

Slide 60 text

!60 Как именно? Создаем простейшую модель Item {id, title}

Slide 61

Slide 61 text

!61 Как именно? Создаем простейшую модель Item {id, title}

Slide 62

Slide 62 text

!62 Как именно? Постепенно расширяем её, создавая новые типы данных (Price, Contacts) и сокращая объем $data

Slide 63

Slide 63 text

Сделали маленькие модели !63

Slide 64

Slide 64 text

!64 Что именно? На прошлом этапе у нас получились большие модели

Slide 65

Slide 65 text

!65 Объявление в поиске

Slide 66

Slide 66 text

!66 Страница объявления

Slide 67

Slide 67 text

!67 Объявление в чужом профиле

Slide 68

Slide 68 text

!68 Объявление в своём профиле

Slide 69

Slide 69 text

!69 Изначально везде была одна модель

Slide 70

Slide 70 text

!70 Проблемы Огромная холостая нагрузка на сеть

Slide 71

Slide 71 text

!71 Проблемы Огромная холостая нагрузка на сеть Низкая надежность

Slide 72

Slide 72 text

!72 Проблемы Огромная холостая нагрузка на сеть Низкая надежность Сложность тестирования

Slide 73

Slide 73 text

!73 Проблемы Огромная холостая нагрузка на сеть Низкая надежность Сложность тестирования Невозможность создать микросервис, отвечающий только за часть свойств этой модели

Slide 74

Slide 74 text

!74 Как именно? Выделили десятки интерфейсов из большой модели

Slide 75

Slide 75 text

!75 Как именно? Выделили десятки интерфейсов из большой модели Разбили одну большую модель на множество маленьких

Slide 76

Slide 76 text

!76 Как именно? Выделили десятки интерфейсов из большой модели Разбили одну большую модель на множество маленьких Сделали отдельные фабрики для каждой из них

Slide 77

Slide 77 text

Перешли на DataMappper вместо ORM !77

Slide 78

Slide 78 text

!78 Что именно? Вызовы хранимок на получение данных из самых разных места монолита

Slide 79

Slide 79 text

!79 Что именно? Вызовы хранимок на получение данных из самых разных места монолита Сохранение моделей в отличном от создания месте

Slide 80

Slide 80 text

!80 Что именно? Вызовы хранимок на получение данных из самых разных места монолита Сохранение моделей в отличном от создания месте Разные алиасы у полей, и способы получения данных

Slide 81

Slide 81 text

!81 Достаем item[100] из базы

Slide 82

Slide 82 text

!82 Достаем item[100] из базы

Slide 83

Slide 83 text

!83 Достаем item[100] из базы

Slide 84

Slide 84 text

!84 Проблемы Большая глубина наследования на классах занимающихся работой с моделью

Slide 85

Slide 85 text

!85 Проблемы Большая глубина наследования на классах, занимающихся работой с моделью Большое количество входных похожих точек, вызывающих работу с моделью

Slide 86

Slide 86 text

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

Slide 87

Slide 87 text

!87 Как именно? Переход на dataMapper модель

Slide 88

Slide 88 text

!88 Как именно? Переход на dataMapper модель Подстройка публичного интерфейса под нужды конкретных потребителей

Slide 89

Slide 89 text

!89 Как именно? Переход на dataMapper модель Подстройка публичного интерфейса под нужды конкретных потребителей Инкапсулюяция всей логики базы связанной с сущностью в одном месте

Slide 90

Slide 90 text

Отказались от поддержки старых приложений !90

Slide 91

Slide 91 text

!91 Что именно? Большой хвост из старых версий мобильных приложений

Slide 92

Slide 92 text

!92 Что именно? Большой хвост из старых версий мобильных приложений Большое десктоп-апи, которые не чистили раньше

Slide 93

Slide 93 text

!93 Проблемы Старые методы ожидали очень большие модели данных, избыточные для их работы

Slide 94

Slide 94 text

!94 Проблемы Старые методы ожидали очень большие модели данных, избыточные для их работы Часть запросов использовались исключительно ботами

Slide 95

Slide 95 text

!95 Проблемы Старые методы ожидали очень большие модели данных, избыточные для их работы Часть запросов использовались исключительно ботами Часть запросов были «мертвыми»

Slide 96

Slide 96 text

!96 Проблемы Старые методы ожидали очень большие модели данных, избыточные для их работы Часть запросов использовались исключительно ботами Часть запросов были «мертвыми» Осложняли рефакторинг

Slide 97

Slide 97 text

!97 Как именно? Сделали метрики по использованию API

Slide 98

Slide 98 text

!98

Slide 99

Slide 99 text

!99 Как именно? Сделали метрики по использованию API Сверились c ними

Slide 100

Slide 100 text

!100

Slide 101

Slide 101 text

!101 Как именно? Сделали метрики по использованию API Сверились c ними Отказались от поддержки версий приложений с минорным трафиком

Slide 102

Slide 102 text

!102 Начали делать это регулярно

Slide 103

Slide 103 text

Перешли на Redis !103

Slide 104

Slide 104 text

!104 Что именно? В проекте было несколько коммунальных мемкешей Использовались они для кратковременного кеширования

Slide 105

Slide 105 text

!105 Проблемы Перетирали ключи в разных потребителях

Slide 106

Slide 106 text

!106 Проблемы Перетирали ключи в разных потребителях Автоматически конвертит классы через php_serialize (круто, когда класс переименовали а кэш нет)

Slide 107

Slide 107 text

!107 Проблемы Перетирали ключи в разных потребителях Автоматически конвертит классы через php_serialize (круто, когда класс переименовали а кэш нет) Не перистентен

Slide 108

Slide 108 text

!108 Проблемы Перетирали ключи в разных потребителях Автоматически конвертит классы через php_serialize (круто, когда класс переименовали а кэш нет) Не перистентен Общие политики вытеснения для всех

Slide 109

Slide 109 text

!109 Как именно? Плавно заменили несколько мемкешей на большую пачку персистентных Redis

Slide 110

Slide 110 text

!110 Как именно? Плавно заменили несколько мемкешей на большую пачку персистентных Redis Выделили отдельные обертки под отдельные Redis

Slide 111

Slide 111 text

!111 Как именно? Плавно заменили несколько мемкешей на большую пачку персистентных Redis Выделили отдельные обертки под отдельные Redis Разделили потребителей каждый по своему Redis’у

Slide 112

Slide 112 text

!112 Их еще и из монолита легче вытащит

Slide 113

Slide 113 text

Что же стало с монолитом? !113

Slide 114

Slide 114 text

!114 Монолит не распилен

Slide 115

Slide 115 text

!115 Он даже увеличился

Slide 116

Slide 116 text

!116 13к файлов PHP 554к строк PHP

Slide 117

Slide 117 text

!117 ≈ 300микросервисов ≈ 1500 репозиториев

Slide 118

Slide 118 text

!118 Перешли на 
 PHP 7.4

Slide 119

Slide 119 text

!119 Что изменилось

Slide 120

Slide 120 text

!120 implode (string $glue , array $pieces ) Раньше порядок аргументов не имел значения Было implode (string $glue, array $pieces): string implode (array $pieces, string $glue): string Стало implode (string $glue, array $pieces): string

Slide 121

Slide 121 text

!121 Внедрили DI

Slide 122

Slide 122 text

!122 Добавили литеры Php-stan Php-cs Route-checker Сrontab-checker Проверка схемы ответов API Линтер на examples ответов API Валидация контраков

Slide 123

Slide 123 text

!123 Добавили литеры Php-stan Php-cs Route-checker Сrontab-checker Проверка схемы ответов API Линтер на examples ответов API Валидация контраков

Slide 124

Slide 124 text

!124 У нас появился service-price-formatter

Slide 125

Slide 125 text

!125 Сервис форматирующий цену

Slide 126

Slide 126 text

!126 Да, это stateless сервис, просто форматирующий цену

Slide 127

Slide 127 text

!127 Он утилизирует 40 ядер

Slide 128

Slide 128 text

!128 Мы его удалили

Slide 129

Slide 129 text

Декомпозируй правильно! !129

Slide 130

Slide 130 text

Почти конец !130

Slide 131

Slide 131 text

!131 Чтобы распилить монолит…

Slide 132

Slide 132 text

!132 Не делайте price-formatter

Slide 133

Slide 133 text

!133 Избавьтесь от Dependency Hell

Slide 134

Slide 134 text

!134 Увеличьте покрытие тестами и литерами

Slide 135

Slide 135 text

!135 Откажитесь от ORM, 
 ServiceRegistry и Global’ов

Slide 136

Slide 136 text

Павел Лакосников Senior engineer, юнит Antimonolith facebook: https://www.facebook.com/shaman.s.bubnom telegram: shaman_s_bubom