
Великий аргентинский писатель Хорхе Луис Борхес в одном из своих произведений попытался доказать, что вся мировая литература сводится к четырем базовым сюжетам: истории об осаде и защите обречённого города, истории о возвращении, истории о поиске и истории о жертвоприношении или самоубийстве бога. В комментарий к этому заявлению Виктор Пелевин в одном из своих романов написал: "А по-моему, сюжетов всего два. Первый – как человека убивают из-за денег. Второй – как человека приносят в жертву. А на самом деле оба сюжета можно даже объединить в один". Как бы то ни было, оставим спор о сюжетах литературоведам. В этом посте я хочу разобрать схожий тезис - все архитектуры разных видов программного обеспечения можно свести к двум базовым структурам - монолиту и шине.
Операционные системы
В операционных системах есть два типа ядер:
Монолитное ядро (монолит)
Микроядро (шина)
Весь код монолитного ядра выполняется в едином адресном пространстве, а его модули общаются друг с другом через обычные вызовы системных функций. Преимущество монолитного ядра состоит в скорости его работы. Проблема же состоит в том, что при изменении любого модуля, например какого-нибудь драйвера, приходится перекомпилировать всю систему. Кроме того, при фатальной ошибке в одном из модулей падает вся операционная система.
Микроядро предоставляет минимальный набор системных функций, а все модули, в том числе драйверы и файловая система, подключаются к нему и общаются друг с другом через сообщения по шине данных. Это даёт возможность заменять модули на лету - например, подгрузить драйвер без перекомпиляции и даже без перезапуска операционной системы. И ошибка в одном из модулей никак не затронет другие. Более того, можно выкинуть из операционки все ненужные модули, что сильно снизит её размер в байтах. Да и тестировать отдельные модули гораздо проще, чем огромный единый монолит. Недостаток же микроядерных ОС заключается в том, что за удобства приходится платить оверхедом по производительности и более низкой, чем у монолитных систем, скоростью работы.
Примеры ОС с монолитным ядром: Linux, MacOS, Windows. Знаменитый синий экран смерти в Windows возникает как раз из-за монолитности его ядра. Но даже в этих операционных системах используется похожая на микроядерную архитектура подгрузки внешних модулей, так что вернее было бы назвать их гибридными.
Примеры ОС с микроядром: учебный MINIX и операционная система реального времени QNX. Микроядерные системы часто используются в автомобилях, станках, самолетах, космических спутниках и кораблях и вообще везде, где важна стабильность, отказоустойчивость, маленький размер и возможность работать в реальном времени, то есть, иметь строго заданное время исполнения различных функций и фиксированное время отклика.
В 1992 году состоялся знаменитый спор создателя Linux Линуса Торвальдса и создателя MINIX Эндрю Таненбаума о преимуществах и недостатках монолитных ядер и микроядер.
Бекенд
При разработке бекенда тоже используется два подхода к построению его архитектуры:
Монолит
Микросервисы
Монолит представляет собой единое приложение, в котором все модули компилируются вместе и работают в рамках одного сервиса. Модули общаются друг с другом через обычные вызовы функций. Преимущество этого подхода состоит в скорости работы системы, ведь нет необходимости передавать данные по сети. Недостаток же состоит в том, что, когда кодовая база разрастается, и количество разработчиков увеличивается, поддерживать код монолитного бекенда становится всё сложнее и сложнее.
В микросервисном подходе модули приложения разделены по разным сервисам - то есть по сути разным приложениям, которые могут запускаться на разных серверных машинах. Общаются друг с другом микросервисы либо вызовами по сети, либо с помощью шины событий (event bus). Эта архитектура более медленная из-за взаимодействия модулей друг с другом по сети, более сложная в разработке, ведь необходимо деплоить и оркестировать множество модулей. Но в то же время микросервисы гораздо легче скейлить. Кроме того использование event bus позволяет использовать такие подходы как CQRS и event sourcing.
Фронтенд
При разработке фронтенда тоже используется два подхода:
Монолитное приложение
Микрофронтенды
Всё по аналогии с монолитом и микросервисами на бекенде. Но на фронтенде шинная архитектура используется и в других местах.
Во-первых, вся интерактивность в браузере построена на отправке и прослушивании событий. В любом месте кода можно как отправить любой ивент через window.dispatchEvent, так и подписаться на него через window.addEventListener.
Во-вторых, многие системы управления состоянием вроде Redux тоже представляют собой шину событий, которые редуцируют поток событий в текущее состояние подобно тому, как на бекенде при использовании event sourcing поток ивентов превращается в materialized view в базе данных.
Многопоточность
При разработке многопоточных систем тоже используется два подход��:
Ручное управление (философски ближе к монолитному подходу)
Акторы/корутины (шина)
При ручном управлении возможно выжать большую производительность, но разработка и отладка системы с семафорами, локами, волатильными переменными и прочими хитрыми инструментами превращается в ад.
При шинном подходе (акторы в Akka, корутины в Go) потоки обмениваются между собой информацией через сообщения по шине - производительность может быть не такой быстрой, но разработка и отладка в разы легче.
Факторио
На Хабре есть прекрасная серия статей про архитектуру разработки фабрики в игре Factorio:

В этих статьях автор рассказывает про то, как он строит фабрику в факторио: сначала с монолитной архитектурой, потом с микросервисной архитектурой без шины, а после с микросервисной архитектурой с шиной.
Заключение
Подводя итог, можно сказать, что монолитный подход к архитектуре почти всегда даёт преимущества в плане производительности, а модульный подход с шиной сообщений более ресурсозатратный, но более чистый, расширяемый и поддающийся отладке.
P. S.: в моём телеграм‑канале я не пишу про архитектуру ПО, но пишу про философию, математику и буддизм.