Например, разбор графов в graphql запросах достаточно тяжёлый, фактически ребята из core team node.js в открытую заявляют, что производительность node.js-решения недостаточна. Так же могут быть тяжёлые crypto-операции (не зря их выносят в нативные модули). Даже банальное форматирование ответа для логгера лучше выносить в отдельный тред, чтобы не просаживать основной на бесполезных для пользователя операциях.
Разбивать тяжёлые операции на чанки хорошее решение, про которое зачастую забывают.
Не хотел затрагивать ещё и тему декораторов. Но да, безусловно, это может стать большой проблемой nest-проектов.
Но что делать, все реализации IoC-контейнеров для ts построены на легаси-декораторах.
Если это вопрос ко мне, то я не сравниваю производительность асинхронного java кода с node.js. Я говорю о том, что производительность node.js достаточная для работы в режиме BFF-прослойки и мы не деградируем систему несмотря на однопоточную природу JavaScript.
Но уточнение резонное, хорошая поддержка асинхронного I/O была сильной стороной node.js только в момент появления на рынке, сейчас все остальные решения так же подтянулись и могут предложить свой вариант async I/O.
Да, если нам критично время ответа. Однако, мне кажется, что такие задачи встречаются не часто, во многих системах работать с данными можно асинхронно. Но это только ощущения, без статистики.
Да, если разделить, то можно жить. Другой вопрос, что система становится очень сложной и нужна соответствующая квалификация, чтобы её поддерживать в живом состоянии.
Давно идёт война между этими фандомами. Моё личное мнение, что на уровень БД выносить бизнес-логику можно только в тех случаях, когда это даст критически важное ускорение для системы. Это очень жёсткий трейдофф, и компании, пережившие переезды между несколькими БД, хорошо это понимают.
Но как фронтендер я не могу тут спорить — опыта не хватит.
Я затронул этот момент в посте. Node.js действительно не предназначена для интенсивных CPU-вычислений и приложения на node.js всегда масштабируют либо встроенным механизмом воркер-тредов (порождая несколько процессов внутри материнского) либо внешними средствами. Но в том и соль BFF, что на нём не должно быть нагрузок CPU, только I/O, а тут Node.js прекрасно справляется с огромным количеством входящих запросов, используя модель с event loop и системным демультиплексером.
Т.е. с большим количеством запросов года справится, если на эти запросы не будет навешено тяжёлой работы, особенно синхронной.
Нет, это не аналог исключений. Разница в том, что исключение выпрыгивает из потока. Наша функция имеет контракт, говорящий о том, что она возвращает/не возвращает набор данных определённого типа. Исключение нарушает этот контракт, всплывая по стеку до первого перехватчика. Указать в контракте в JS/TS факт наличия исключений и их тип мы не можем. Таким образом, потребитель нашего кода не знает не заглядывая в исходники и документацию о том, что может быть порождено исключение и факт его обработки нельзя проверить статически.
Промис не нарушает контракт. Мы говорим, что наша функция возвращает промис и она его возвращает. Конечно, мы всё ещё сталкиваемся с проблемой статического анализа обработки хорошего и плохого пути (потребитель может не обработать catch), но отловить это намного проще. Но async/await ломает всю эту красоту и мы снова вынуждены писать try/catch. Тут уже вступает в игру result-контейнер.
В Nest же, как вижу, предлагается использовать неявный слой IoC, а про дополнительные данные, привязанные к сессии пользователя, не говорится, так что сравнить не с чем.
Да, в Nest и правда очень сильно не хватает возможности привязать данные к сессии пользователя. Это решается созданием сервиса в скоупе реквеста (IoC даёт это из коробки), но это довольно дурнопахнущее решение. Другой вариант — использовать CLS (например cls-hooked, либо недавно приземлённые в ноду AsyncLocalStorage)
Так что логичнее было бы вынести это в отдельную статью, сравнив несколько паттернов организации сущностей и взаимодействия между ними, ибо в итоге раздел «архитектурные слои» получился очень немногословным и смятым
Ваша правда. Для того и пишем, чтобы учиться доносить мысли лучше. Теперь сам вижу, что скомкано вышло :)
Исключения не так страшны (в конце концов, если не упираться в идеологическую чистоту, это дело вкуса), как сложности с их отловом в Js/ts по конкретному типу
Да, если бы мы могли как в Java описывать все возвращаемые исключения по типам — жизнь была бы намного лучше.
Вот что действительно печалит — так это отсутствие нормальной ORM, позволяющей работать с rich models и DDD aggregate roots.
К счастью, BFF перекладывает всю ответственность по работе с БД на плечи бэкенда. Боль очень понимаю, но сам не испытываю.
Проблема необходимости разного набора данных разным приложениям решается версионированием АПИ, тут ничем BFF не поможет
В данном случае речь не о поддержке нескольких версий, а об оптимальном наборе данных для каждой страницы/приложения и уменьшении количества запросов, необходимых для получения этих данных. Для примера, на одной странице нам можем понадобится только имя пользователя, на другой нам нужно получить имя, ссылку на аватар и счётчик комментариев. Используя один универсальный эндпоинт мы будем либо вытягивать недостаточно данных и делать дополнительные запросы, либо тащить избыточный набор и нагружать канал.
API Gateway как маппер,, в какой микросервис пойти в зависимости от урла запроса
В нашем случае всегда есть оркестрация, недостаточно иметь простой маппер. Например, авторизация пользователя — это отдельный микросервис. Да, мы стараемся минимально зашивать бизнес-логику, но коммуникаций между микросервисами бэкенда у нас в избытке.
Конечно, можно написать любую дичь и сказать «поддерживать такой код не хочется», но вот нападки на Express этим не обоснованы
Пока мой опыт показывает, что качество кода с переходом на Nest в командах выросло. Абсолютно согласен, что можно писать хорошо на Express, но, к сожалению, чем более низкоуровневое решение, тем больше свободы у разработчика.
Однако я к этому и хотел подвести, что Nest не даст архитектуры, он всего лишь улучшит отдельные моменты. Например, нам удалось построить очень удобную систему распределённого рендеринга (часть страницы рендерится отдельно в отдельном микросервисе), удобно подключаемую через декораторы к контроллерам, что сильно снизило когнитивную нагрузку на разработчика. Код контроллеров остался максимально чистым.
Это же BFF, простой сервер-рендерер-прокси, а не сложная система с кучей сервисов.
Как только бизнес-логика начинает проникать в BFF — исчезает «простой рендер прокси», к сожалению. А в больших проектах она будет проникать.
Миддлвары — отличный паттерн
Но работающий в Express на неконтролируемом мутировании нетипиризованных Request и Response. От этого бывает очень больно.
Nest переусложнен и жестковато структурирован для BFF
Именно этой жесткости нам и не хватало в Express.
Или BFF был просто вступлением, а суть — в рекламе ролика с гексагональным паттерном для высчитывания остатка денег на счету пользователя?
Ролик — просто ответ на возможный вопрос «а как же писать бизнес-логику, не завязанную на фреймворк»? Просто, чтобы не быть голословным.
Разбивать тяжёлые операции на чанки хорошее решение, про которое зачастую забывают.
Но что делать, все реализации IoC-контейнеров для ts построены на легаси-декораторах.
Но уточнение резонное, хорошая поддержка асинхронного I/O была сильной стороной node.js только в момент появления на рынке, сейчас все остальные решения так же подтянулись и могут предложить свой вариант async I/O.
Но как фронтендер я не могу тут спорить — опыта не хватит.
Т.е. с большим количеством запросов года справится, если на эти запросы не будет навешено тяжёлой работы, особенно синхронной.
Промис не нарушает контракт. Мы говорим, что наша функция возвращает промис и она его возвращает. Конечно, мы всё ещё сталкиваемся с проблемой статического анализа обработки хорошего и плохого пути (потребитель может не обработать catch), но отловить это намного проще. Но async/await ломает всю эту красоту и мы снова вынуждены писать try/catch. Тут уже вступает в игру result-контейнер.
Да, в Nest и правда очень сильно не хватает возможности привязать данные к сессии пользователя. Это решается созданием сервиса в скоупе реквеста (IoC даёт это из коробки), но это довольно дурнопахнущее решение. Другой вариант — использовать CLS (например cls-hooked, либо недавно приземлённые в ноду AsyncLocalStorage)
Ваша правда. Для того и пишем, чтобы учиться доносить мысли лучше. Теперь сам вижу, что скомкано вышло :)
Да, если бы мы могли как в Java описывать все возвращаемые исключения по типам — жизнь была бы намного лучше.
К счастью, BFF перекладывает всю ответственность по работе с БД на плечи бэкенда. Боль очень понимаю, но сам не испытываю.
В данном случае речь не о поддержке нескольких версий, а об оптимальном наборе данных для каждой страницы/приложения и уменьшении количества запросов, необходимых для получения этих данных. Для примера, на одной странице нам можем понадобится только имя пользователя, на другой нам нужно получить имя, ссылку на аватар и счётчик комментариев. Используя один универсальный эндпоинт мы будем либо вытягивать недостаточно данных и делать дополнительные запросы, либо тащить избыточный набор и нагружать канал.
В нашем случае всегда есть оркестрация, недостаточно иметь простой маппер. Например, авторизация пользователя — это отдельный микросервис. Да, мы стараемся минимально зашивать бизнес-логику, но коммуникаций между микросервисами бэкенда у нас в избытке.
Пока мой опыт показывает, что качество кода с переходом на Nest в командах выросло. Абсолютно согласен, что можно писать хорошо на Express, но, к сожалению, чем более низкоуровневое решение, тем больше свободы у разработчика.
Однако я к этому и хотел подвести, что Nest не даст архитектуры, он всего лишь улучшит отдельные моменты. Например, нам удалось построить очень удобную систему распределённого рендеринга (часть страницы рендерится отдельно в отдельном микросервисе), удобно подключаемую через декораторы к контроллерам, что сильно снизило когнитивную нагрузку на разработчика. Код контроллеров остался максимально чистым.
Как только бизнес-логика начинает проникать в BFF — исчезает «простой рендер прокси», к сожалению. А в больших проектах она будет проникать.
Но работающий в Express на неконтролируемом мутировании нетипиризованных Request и Response. От этого бывает очень больно.
Именно этой жесткости нам и не хватало в Express.
Ролик — просто ответ на возможный вопрос «а как же писать бизнес-логику, не завязанную на фреймворк»? Просто, чтобы не быть голословным.