Давайте поговорим о high-load, high-extensible, ООП, DDD & EDA.


Речь пойдет о разнице между high load & high extensibility — это разные цели и задачи в разработке. Для которых нужны принципиально разные подходы. Которые часто вызывают когнитивный диссонанс среди программистов, предвещающий бурную полемику.


На Хабре много статей про high-load, а вот тема high-extensible как-то упускается из внимания.


Если попытаться примерно представить разность в понятиях и механиках, то вот табличка:


image


Давайте попробуем разобрать это все подробнее.


Хайлоад это хайлоад. Это быстрые ответы на запросы и постоянные соединения. Обычно тут 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, и пр.)

Я считал объекты чем-то вроде модулей, биологических клеток, и/или отдельных компьютеров в сети, которые могут общаться только через сообщения.

Я сожалею о том, что давным-давно придумал термин «объекты» для этого явления, так как его использование приводит к тому, что многие люди уделяют основное значение идее, которая не так важна, как основная. Основная идея — это обмен сообщениями

ООП для меня означает лишь обмен сообщениями, локальное сохранение, и защита, и скрытие состояния, и крайне позднее связывание

Итак, если взять эти идеи, то тезисами это можно сделать так:


  1. Масштабируемые и расширяемые системы состоят из модулей
  2. Модули общаются друг с другом через сообщения
  3. Это обеспечивает позднее связывание — крайне позднее связывание — код и логику можно менять в любой момент не меняя уже существующий код в модулях
  4. Для работы такой системы важна динамическая типизация, а не статическая

В 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, там также есть модули (плагины), а общение модулей и обмен сообщениями строится на хуках.
  • Ребята из 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.