Давайте поговорим о high-load, high-extensible, ООП, DDD & EDA.
Речь пойдет о разнице между high load & high extensibility — это разные цели и задачи в разработке. Для которых нужны принципиально разные подходы. Которые часто вызывают когнитивный диссонанс среди программистов, предвещающий бурную полемику.
На Хабре много статей про high-load, а вот тема high-extensible как-то упускается из внимания.
Если попытаться примерно представить разность в понятиях и механиках, то вот табличка:
Давайте попробуем разобрать это все подробнее.
Хайлоад это хайлоад. Это быстрые ответы на запросы и постоянные соединения. Обычно тут php RoadRunner или Go или Rust, с асинхронностью и event loop. Проблема 10k и т. д.
ООП было придумано как решение задачи расширения или High Extensible. Тут лучше работают модульная разработка (MP, DDD) & обмен сообщениями (EDA, event driven architecture).
Мотивация
Суть проблемы, которая рассматривается в контексте этой статьи, заключается в том как команды работают с большими системами.
Речь идет о системах где разработку ведут 5-10 или больше команд, в каждой по 5-10 разработчиков.
Если просто в лоб решать задачи, то можно налететь на проблемы, которые описываются так:
- команды начинают конфликтовать в GIT, мешать друг другу, ломать систему, увеличивается рост ошибок
- переиспользование кода затрудняется, часть команд начинает дублировать работу друг друга
- затрудняются релизы, увеличивается тайм-ту-маркет
- сложнее прогнозировать сроки
- множатся зависимости по задачам и блокеры, так как надо долго ждать результатов от смежных команд
Все это можно решить, или существенно снизить проблемы, если грамотно применять модули (MP), иногда домены (DDD) & обмен сообщениями (EDA как вариант), делая архитектуру более антихрупкой.
Быть эффективным или быть антихрупким?
Это две крайности одной и той же сущности.
В книге Антихрупкость, автор Нассим Талеб пишет о том что погоня за эффективностью всегда влечет рост хрупкости. Если мы ставим себе цель делать систему антихрупкой, нам придется жертвовать эффективностью. Иного не дано.
Антихрупкость любит случайность и неопределенность, что означает — и это ключевое свойство антихрупкости — любовь к ошибкам, к определенному классу ошибок. Уникальность антихрупкости состоит в том, что она позволяет работать с неизвестностью, делать что-то в условиях, когда отсутствует понимание, что именно делается, — и добиваться успеха.
Вам нужна система которая будет эффективной или антихрупкой?
Ответ на этот вопрос влияет на решения о том какие технологии следует использовать.
ООП было придумано как решение проблем расширяемости больших систем (большой функционал)
Цитата Алана Кея, автора ООП:
«Я придумал термин «объектно-ориентированный», и могу сказать, что я не имел в виду С++». Алан Кэй, конференция OOPSLA, 1997.
Ключ к созданию хороших масштабируемых систем это проработка механизмов общения модулей, а не проработка их внутренних свойств и поведения.
Я не против типов, но мне не знакома ни одна система типов, которая не вызывала бы боли. Так что мне все еще нравится динамическая типизация.
Позднее связывание позволяет с меньшими усилиями встраивать в проект идеи, которые возникли позже в процессе разработки (по сравнению с системами с более ранним связыванием вроде C, C++, Java, и пр.)
Я считал объекты чем-то вроде модулей, биологических клеток, и/или отдельных компьютеров в сети, которые могут общаться только через сообщения.
Я сожалею о том, что давным-давно придумал термин «объекты» для этого явления, так как его использование приводит к тому, что многие люди уделяют основное значение идее, которая не так важна, как основная. Основная идея — это обмен сообщениями
ООП для меня означает лишь обмен сообщениями, локальное сохранение, и защита, и скрытие состояния, и крайне позднее связывание
Итак, если взять эти идеи, то тезисами это можно сделать так:
- Масштабируемые и расширяемые системы состоят из модулей
- Модули общаются друг с другом через сообщения
- Это обеспечивает позднее связывание — крайне позднее связывание — код и логику можно менять в любой момент не меняя уже существующий код в модулях
- Для работы такой системы важна динамическая типизация, а не статическая
В 80е годы ООП разбилось на 2 типа
В книге Мифический человеко-месяц, автор Фредерик Брукс описывает момент когда ООП разбилось на 2 мира. Часть "Объектно-ориентированное программирование: а медна пуля не
подойдет?"
Разработка из больших частей. Если осуществлять сборку из частей, которые достаточно сложны и имеют однородные интерфейсы, можно быстро образовывать довольно богатые структуры.
Согласно одному из взглядов на объектно-ориентированное программирование эта дисциплина осуществляет модульность и чистые интерфейсы. Другая точка зрения подчеркивает инкапсуляцию — невозможность увидеть и еще менее изменить внутреннюю структуру детали. Еще одна точка зрения отмечает наследование и сопутствующую ему иерархическую структуру классов с виртуальными функциями. И
еще один взгляд: важнейшей является сильная абстрактная типизация данных вместе с гарантиями, что конкретный тип данных будет обрабатываться только применимыми к нему операциями.
В настоящее время не нужен весь пакет Smalltalk или C++, чтобы использовать любой из этих дисциплин — многие из них поглотили объектно-ориентированные технологии. Объектно-ориентированный подход привлекателен, как поливитамины: одним махом (т.е. переподготовкой программиста) получаешь все. Очень многообещающая концепция.
Получается что идея ООП зародилась в 60е годы, в 80е годы уже были разные точки зрения на то что есть ООП.
Сегодня под ООП чаще всего имеют ввиду классы. Хотя Алан Кей и Дэвид Уэст говорят что это ошибка. И что классы это всего лишь структуры данных. Это не про ООП. C++ & Java в базе не имеют ничего общего с ООП.
Речь идет про то ООП которое про модули и обмен сообщениями. Сегодня эта идеология чаще всего встречается в контексте модулей и сообщений или событий (EDA). Ты пишешь модули, которые обмениваются сообщениями через чистые интерфейсы (события, хуки, триггеры) с крайне поздним связыванием.
Так кто прав, а кто лев?
Если вам нужен HighLoad, вам надо топить за эффективность, масштабирование скорости исполнения, статическую типизацию, асинхронность, события и eventloop.
Если вам нужен HighExtensible, вам надо топить за антихрупкость, масштабирование скорости изменений, динамическую типизацию, модули и домены (DDD) и события как EDA (обмен сообщениями между модулями).
Причины проблем
Проблема в том что программисты спорят о том что ООП плохо (классы это медленно и хрупко, а функции быстро и круто, ну или как то там наоборот), о том что вот в PHP не хватает асинхронности и event loop.
Например эти темы поднимались на недавнем митапе SkyEng https://www.youtube.com/watch?v=QrlWrFILjMk
Но не понимают что это разговор о разных идеологиях, парадигмах и архитектурах. Это все равно что говорить что у дома нет колес или у автомобиля не хватает 3го этажа с ковром. Вот бы ложка была вилкой или вилка была бы ложкой. Вообще есть конечно вилка-ложки, но часто ли мы ими пользуемся вне горных походов?
Если вы создаете прикладную бизнес систему, где есть куча реальных пользователей, бизнес требования, бизнес аналитики, никто не знает что хочет, а требования меняются каждый день — вы очень сильно попадете в засаду если пойдете по пути HighLoad, будете делать статическую типизацию, асинхронность, без доменов и событий — вам будет больно. Тут нужно двигаться ближе к архитектуре для HighExtensible.
Если вы создаете микросервисы, которым надо отрабатывать 100 000 операций в секунду, то попытка пойти по пути динамической типизации, доменам, без асинхронности — тоже сделает очень больно. Тут стоит копать в сторону архитектуры под HighLoad.
Сделать одновременно HighLoad & HighExtensible — не реально или очень близко к не реальному. Попытки совместить эти две идеологии приводят к диким проблемам, большим затратам и часто к провалу.
Ну а программисты готовы спорить об этом до посинения. Не понимая что говорят про разные контексты, задачи и механизмы проектирования архитектур.
Примеры
Про high-load:
- NodeJS — пошел с вектором в event loop & асинхронные механики. Есть много интересных кейсов с построением highload микросервисов и REST API.
- Туда же можно отнести Go, Rust и разные механики PHP типа RoadRunner и PHP Swoole.
Про high-extensible
- Можем взять Redmine, это типа система управления задачами, на базе RoR, там также есть модули (плагины), а общение модулей и обмен сообщениями строится на хуках;
- Django использует концепцию Сигналов, которая по своей сути также является разновидностью обмена сообщениями между модулями или EDA;
- Ребята из FreeScout решили делать систему хелпдеск на Laravel. Это прикладная система с кучей бизнес логики. Они реализовали свою архитектуру модулей (DDD) и обмена сообщениями (EDA) применив Eventy;
- Eventy в свою очередь взял идею хуков из WordPress. Надо ли представлять этого зверя? Который держит более 30% рынка веб сайтов. Его архитектура также базируется на идеи модулей (плагинов, DDD), которые взаимодействуют друг с другом через сообщения (хуки, EDA).
Очень интересный опыт у ребят из iSpring:
https://www.youtube.com/watch?v=xT25xiKqPcI
Там речь о том чем отличается монолит от копролита. О том что если уметь готовить монолит, с DDD & EDA, то можно получить много преимуществ. А если не уметь — то и микросервисы не факт что помогут.
В общем, ребята ступили на путь освоения DDD & EDA. И это радует.
Заключение
Мир технологий в целом и разработок в частности очень многогранен. Если есть какая то технология — значит на то есть причины. Не знание этих причин еще не значит что технология плохая сама по себе.
Динамическая и статическая типизация — обе правильные, если их применять в соответствующих задачах.
ООП тоже очень разное может быть и есть много разных точек зрения на то что это такое. И все они правильные. Просто там разные контексты.
Вилка или ложка — не плохие и не хорошие. И если человек ест спагетти ложкой, а суп вилкой, а потом пишет о том что ложка это плохо, потому что спагетти вываливаются, и вилка плохо потому что суп хлебать ей не удобно — то разве это проблема в вилке или ложке?
Ну и под конец, хочется больше примеров. Кто какие еще примеры знает грамотного применения модулей и сообщений (DDD & EDA)?
Особенно интересует опыт построения HighExtensible систем в РФ. Потому что на западе его хватает. А тема high-load на Хабре и так перегрета. Хочется больше поговорить о практиках high-extensible.