Т.к. система малосвязанная. В старый код (проекты) лезть уже не нужно. Можно спокойно пилить новые страницы новой командой.
Если потребуется правки, то распределённость логики позволит на функциональных модулях вносит изменения без опасения сломать соседние страницы (части системы).
Доменные сервисы тоже не обладают сложной логикой и легче, чем микросервисы для восприятия.
У нас есть два слоя: функциональные модули и доменные сервисы.
Доменные сервисы — это отдельные сервисы со своими границами ответственности. А вот со словом “микросервис” я бы был аккуратнее, потому что оно сразу накладывает дополнительные ожидания и требования, хотя они и близки к ним.
Функциональные модули — это логические модули, обычно на уровне отдельных csproj. В идеальном мире каждый такой функциональный модуль можно было бы вынести в отдельный solution. Но на практике так не получится - не хватает ресурсов: появляется слишком много репозиториев, пайплайнов, деплоев, инфраструктурной обвязки и операционной сложности.
Поэтому часть функциональных модулей объединяется в группы и разворачивается вместе — фактически как модульный монолит. При этом внутри остаются логические границы между модулями.
Как именно бить функциональные модули на группы — отдельный вопрос. Можно группировать по командам, по доменной зоне, по крупным этапам развития продукта или даже по техническим причинам, например при переходе на новую версию .NET.
Да, полностью человеческий фактор убрать нельзя. Но его можно сильно уменьшить за счёт структуры модулей, явных соглашений и конфигурации.
На бэке можно достаточно явно отследить, если один функциональный модуль начинает ссылаться не туда или обращаться к чужой зоне ответственности напрямую. На фронте тоже можно изначально задать соглашения в конфигурации: например, чтобы часть URL/path указывала на конкретный функциональный модуль. Тогда такие нарушения сложнее сделать случайно, и их проще увидеть на review.
Тот же bff вообще странная тема, сначала спроектируем плохо, а потом сделаем ещё один сервис что бы отдавал данные как надо, даже звучит плохо
Я уже подумываю убрать термин BFF из статьи, потому что на него многие обратили внимание и, похоже, он уводит обсуждение немного в сторону.
В моём случае я вкладывал в него не смысл “сделали плохой API, а потом прикрыли его ещё одним сервисом”, а скорее смысл адаптационного слоя под конкретный клиента (виджета, страницы). Например, такой слой может:
агрегировать данные: сходить в User Service, Orders Service, Payment Service и вернуть frontend уже готовый объект;
преобразовывать формат ответа: внутренние сервисы могут отдавать сложные DTO, а наружу возвращается удобный ViewModel-формат;
скрывать внутреннюю архитектуру: frontend не знает, сколько микросервисов внутри и как они устроены;
оптимизировать API под конкретный клиент — web, mobile или фронтовую часть функционального модуля.
То есть идея не в том, что мы сначала плохо спроектировали систему, а потом пытаемся это исправить. Идея в том, что логика, связанная с конкретным пользовательским сценарием, сосредоточена в одном месте — в рамках функционального модуля.
У нас были случаи, когда web-фронт и мобильное приложение смотрели на один и тот же функциональный модуль. При этом логика прав менялась только на бэке, а web и mobile автоматически подстраивались под неё через возвращаемое состояние/доступные действия.
Про федерации на фронте слышу от знакомых только плохое, там либо 1 фрейморк, либо никак
С Module Federation у нас был опыт, где на одном проекте приходилось совмещать React, Vue и Angular. Разные подрядчики делали компоненты на разных технологиях, и всё это нужно было отображать в рамках одной страницы. Как так получилось — отдельная история и не совсем была связана с нами. Но технически через Module Federation можно подружить разные технологии. При этом я согласен, что это не значит, что так нужно делать без необходимости. Если есть возможность держаться одного фреймворка и единых стандартов, это обычно проще.
Для фронта это $mol framework, который может быть и федерацией и монолитом и микрофронтами.
Про $mol framework честно пока не могу уверенно сказать. Нужно отдельно изучать и пробовать.
Называется HARP ( human API rest protocol ) вот он ещё только на бумаге есть
Я бы не сказал, что то, что мы делали, можно назвать HARP. Но поверхностно некоторые идеи действительно пересекаются: например, стремление описывать API так, чтобы клиент мог получить нужную структуру данных без дополнительного слоя адаптации.
Пользователи у нас хранились во внешней системе — например, Active Directory / OAuth2. Также есть вариант, когда логин/пароль остаются на уровне базы API Gateway, но это касается именно аутентификации.
Список доступных ролей и разрешений пользователя хранится в доменном сервисе прав/разрешений. На практике система прав чуть сложнее, чем просто роли, но суть в том, что источник прав у нас один. Логика расчёта доступов не размазывается между Gateway и разными модулями.
По поводу примера с кнопкой: у нас есть два метода. Первый метод возвращает состояние страницы для фронтенда: какие действия доступны и какие кнопки показывать. Второй метод непосредственно создаёт сущность. Оба метода находятся в одном функциональном модуле и используют одни и те же правила проверки прав.
То есть фронтенд может скрыть кнопку на основании флага, полученного с сервера, но это не является единственным механизмом защиты. Если пользователь выполнит запрос напрямую, сервер всё равно проверит права в методе создания сущности и запретит операцию, если прав недостаточно.
---
В статье описана шина событий на уровне одной вкладки. Более сложные варианты, например BroadcastChannel, не было необходимости применять, хотя в целом мы их иногда рассматривали.
JWT у нас хранится в cookies и обновляется по мере выполнения запросов. Отдельных проблем с синхронизацией токена между вкладками из-за этого подхода не возникало.
Как показала практика, в ретроспективе, получаем стабильную структуру и довольно простое восприятие проекта на всё время.
Монолит через года требует обновление фрейморков, а это огромные затраты. Микросервисы накапливают множество методов. И сложнее следить за теми методами которые могли выйти из эксплуатации, но их продолжают поддерживать.
В общем и целом такой подход позволяет удержать единый уровень "сложности" и подход к проекту.
Более того т.к. страницы и виджеты развязаны и не пересекаются, таки эм образом можно легко модифицированный части проекта без опасения сломать в других местах. А как следствие спрект легче поддаётся модификациям.
За несколько лет экосистема вокруг Module Federation стала более зрелой.
Поправил, для удобства. Но сути текста это не меняет.
Во втором блоке я имел виду, что модули локально можно запускать по разному. Можно пустить всё на самотек, и люди сами будут запускать большую часть фронта руками. Даже те модули, которые сейчас не нужно править, но нужны для финального отображения. Можно добавить файл со скриптом, для автозапуска таких частей, можно сделать отдельный преднастроенный контейнер и запускать его через docker-compose файл. Просто я это проговариваю, т.к. это не размышления из вакуума, а из опыта создания проектов.
Переделать всё может только тимлид и то после согласования с архитектором.
Для отслеживания того, что пишет "рядовой" разработчик и есть пулреквесты. Модули разделены по проектам, то как следствие отслеживать нарушение концепциий можно без особых усилий.
Ну тут нужно согласование и одобрение скорее не менеджера, а архитектора и тимлида. Именно они решают, как проект удобней будет поддерживать.
Например, для чисел вполне очевидно, что 1 || 23 != (1*10+23).
Да. Просто так делать нельзя. Сама идея, что число любое число состоит из конкатенированных цифр. Т.е. 23 (например в десятичной системе) это 2‖3. Т.е. в описанном вами случае будет 1‖2‖3. В противном случае действительно ничего не сходится. Т.к. операция применяется не правильно.
257 системе счисления — будет 256 чисел.
В целом нужно просто понять какие системы нам нужны и уже из это исходить.
В целом там не получается каких-то безумных размерностей в данных
Как я уже писал в статье. Все пояснения решения через конкатенацию установляются на уровне – «смотрите, мы приписали цифру 7 справа и у нас получился ответ». Ну или пишется, что так делать нельзя.
«Почему нельзя» — никто не пишет. То я попытался рассмотреть точку зрения противников такой приписки. Мне показалось, что очевидным ответом будет — такая операция при переводе чисел в другую систему счисления ломает формулу, а такого быть не должно. И тут уже не идет речь о «красоте» формулы, сам факт – результаты подсчетов должны сохраняться. Ломается всё, по простой причине, т.к. изначально понятие «конкатенации» означало приписывание числа справа.
Вот дальше и начались мои размышления, как нам описать эту операцию т.к. что бы это еще работало. Самым простым способом показать, что расчеты не ломаются – это перевести их в другую систему счисления.
Прямой ссылки на «конкурс» у меня нет.
Но в заметках о проблеме числа 10958 часто встречается фраза — Массачусетский технологический институт готов выплатить 5000$!
Читал по большей части. Может это и послужило триггером для написание этой статьи. В приведенной статье, писалось, что «внешняя конкатенация» вообще является ошибочной, что собственно меня смутило/ заинтересовало. Ибо там говорится, что так вообще делать нельзя. Мне же стало интересно, переложить конкатенацию на алгебраически рельсы ибо в википедии о таком подходе вообще речи не идет, только о том, что конкатенация троки объединяет складывает.
П.с.
Просто я сталкивался эвристикой, при которой наиболее простом случае нужно было до множить на 100. Что мне крайне не нравилось из-за костыльности решения, но ничего иного в голову не приходило. И как-то видео с этим числом и мои проблемы с эвристикой на ложилось друг на друга. Сейчас в моей голове сформировался какой-то математический смысл такой эвристики, да и на задачку решение ложиться, а не просто абстрактные рассуждения)
Не совсем так. это скорее способ переключить внимание. От известных A, B, C, D, E, F в иной формат. В целом все числиную систему можно записть либыми символами, например так: 아, 애, 야, 얘, 어, 에, 여, 예, 오, 와, 왜, 외, 요, 우, 워, 웨…
А зачем? Если мы так имеем разбросанные id-шники, то зачем нам при вставке нового пользователя брать минимальный? Более того, взятие минимального id — это в примере, а не в условии. В условии — взять id которого нет в базе.
— Говорю — авторы сами намудрили решением простой задачи, а потом попросили пользователей тыкнуть пальцем в небо и написать решение которое будет похоже на их мудрёное, которое не отражает суть изначально поставленной задачи.
Как раз наоборот.
Т.к. система малосвязанная. В старый код (проекты) лезть уже не нужно. Можно спокойно пилить новые страницы новой командой.
Если потребуется правки, то распределённость логики позволит на функциональных модулях вносит изменения без опасения сломать соседние страницы (части системы).
Доменные сервисы тоже не обладают сложной логикой и легче, чем микросервисы для восприятия.
У нас есть два слоя: функциональные модули и доменные сервисы.
Доменные сервисы — это отдельные сервисы со своими границами ответственности. А вот со словом “микросервис” я бы был аккуратнее, потому что оно сразу накладывает дополнительные ожидания и требования, хотя они и близки к ним.
Функциональные модули — это логические модули, обычно на уровне отдельных csproj. В идеальном мире каждый такой функциональный модуль можно было бы вынести в отдельный solution. Но на практике так не получится - не хватает ресурсов: появляется слишком много репозиториев, пайплайнов, деплоев, инфраструктурной обвязки и операционной сложности.
Поэтому часть функциональных модулей объединяется в группы и разворачивается вместе — фактически как модульный монолит. При этом внутри остаются логические границы между модулями.
Как именно бить функциональные модули на группы — отдельный вопрос. Можно группировать по командам, по доменной зоне, по крупным этапам развития продукта или даже по техническим причинам, например при переходе на новую версию .NET.
API Gateway у нас — отдельный сервис.
Да, полностью человеческий фактор убрать нельзя. Но его можно сильно уменьшить за счёт структуры модулей, явных соглашений и конфигурации.
На бэке можно достаточно явно отследить, если один функциональный модуль начинает ссылаться не туда или обращаться к чужой зоне ответственности напрямую. На фронте тоже можно изначально задать соглашения в конфигурации: например, чтобы часть URL/path указывала на конкретный функциональный модуль. Тогда такие нарушения сложнее сделать случайно, и их проще увидеть на review.
Я уже подумываю убрать термин BFF из статьи, потому что на него многие обратили внимание и, похоже, он уводит обсуждение немного в сторону.
В моём случае я вкладывал в него не смысл “сделали плохой API, а потом прикрыли его ещё одним сервисом”, а скорее смысл адаптационного слоя под конкретный клиента (виджета, страницы). Например, такой слой может:
агрегировать данные: сходить в User Service, Orders Service, Payment Service и вернуть frontend уже готовый объект;
преобразовывать формат ответа: внутренние сервисы могут отдавать сложные DTO, а наружу возвращается удобный ViewModel-формат;
скрывать внутреннюю архитектуру: frontend не знает, сколько микросервисов внутри и как они устроены;
оптимизировать API под конкретный клиент — web, mobile или фронтовую часть функционального модуля.
То есть идея не в том, что мы сначала плохо спроектировали систему, а потом пытаемся это исправить. Идея в том, что логика, связанная с конкретным пользовательским сценарием, сосредоточена в одном месте — в рамках функционального модуля.
У нас были случаи, когда web-фронт и мобильное приложение смотрели на один и тот же функциональный модуль. При этом логика прав менялась только на бэке, а web и mobile автоматически подстраивались под неё через возвращаемое состояние/доступные действия.
С Module Federation у нас был опыт, где на одном проекте приходилось совмещать React, Vue и Angular. Разные подрядчики делали компоненты на разных технологиях, и всё это нужно было отображать в рамках одной страницы. Как так получилось — отдельная история и не совсем была связана с нами. Но технически через Module Federation можно подружить разные технологии. При этом я согласен, что это не значит, что так нужно делать без необходимости. Если есть возможность держаться одного фреймворка и единых стандартов, это обычно проще.
Про $mol framework честно пока не могу уверенно сказать. Нужно отдельно изучать и пробовать.
Я бы не сказал, что то, что мы делали, можно назвать HARP. Но поверхностно некоторые идеи действительно пересекаются: например, стремление описывать API так, чтобы клиент мог получить нужную структуру данных без дополнительного слоя адаптации.
Пользователи у нас хранились во внешней системе — например, Active Directory / OAuth2. Также есть вариант, когда логин/пароль остаются на уровне базы API Gateway, но это касается именно аутентификации.
Список доступных ролей и разрешений пользователя хранится в доменном сервисе прав/разрешений. На практике система прав чуть сложнее, чем просто роли, но суть в том, что источник прав у нас один. Логика расчёта доступов не размазывается между Gateway и разными модулями.
По поводу примера с кнопкой: у нас есть два метода. Первый метод возвращает состояние страницы для фронтенда: какие действия доступны и какие кнопки показывать. Второй метод непосредственно создаёт сущность. Оба метода находятся в одном функциональном модуле и используют одни и те же правила проверки прав.
То есть фронтенд может скрыть кнопку на основании флага, полученного с сервера, но это не является единственным механизмом защиты. Если пользователь выполнит запрос напрямую, сервер всё равно проверит права в методе создания сущности и запретит операцию, если прав недостаточно.
---
В статье описана шина событий на уровне одной вкладки. Более сложные варианты, например BroadcastChannel, не было необходимости применять, хотя в целом мы их иногда рассматривали.
JWT у нас хранится в cookies и обновляется по мере выполнения запросов. Отдельных проблем с синхронизацией токена между вкладками из-за этого подхода не возникало.
Как показала практика, в ретроспективе, получаем стабильную структуру и довольно простое восприятие проекта на всё время.
Монолит через года требует обновление фрейморков, а это огромные затраты. Микросервисы накапливают множество методов. И сложнее следить за теми методами которые могли выйти из эксплуатации, но их продолжают поддерживать.
В общем и целом такой подход позволяет удержать единый уровень "сложности" и подход к проекту.
Более того т.к. страницы и виджеты развязаны и не пересекаются, таки эм образом можно легко модифицированный части проекта без опасения сломать в других местах. А как следствие спрект легче поддаётся модификациям.
Поправил, для удобства. Но сути текста это не меняет.
Во втором блоке я имел виду, что модули локально можно запускать по разному. Можно пустить всё на самотек, и люди сами будут запускать большую часть фронта руками. Даже те модули, которые сейчас не нужно править, но нужны для финального отображения. Можно добавить файл со скриптом, для автозапуска таких частей, можно сделать отдельный преднастроенный контейнер и запускать его через docker-compose файл. Просто я это проговариваю, т.к. это не размышления из вакуума, а из опыта создания проектов.
Ну тут простые ответы :)
Переделать всё может только тимлид и то после согласования с архитектором.
Для отслеживания того, что пишет "рядовой" разработчик и есть пулреквесты. Модули разделены по проектам, то как следствие отслеживать нарушение концепциий можно без особых усилий.
Ну тут нужно согласование и одобрение скорее не менеджера, а архитектора и тимлида. Именно они решают, как проект удобней будет поддерживать.
<удалено>
Да. Просто так делать нельзя. Сама идея, что число любое число состоит из конкатенированных цифр. Т.е. 23 (например в десятичной системе) это 2‖3. Т.е. в описанном вами случае будет 1‖2‖3. В противном случае действительно ничего не сходится. Т.к. операция применяется не правильно.
В целом нужно просто понять какие системы нам нужны и уже из это исходить.
В целом там не получается каких-то безумных размерностей в данных
«Почему нельзя» — никто не пишет. То я попытался рассмотреть точку зрения противников такой приписки. Мне показалось, что очевидным ответом будет — такая операция при переводе чисел в другую систему счисления ломает формулу, а такого быть не должно. И тут уже не идет речь о «красоте» формулы, сам факт – результаты подсчетов должны сохраняться. Ломается всё, по простой причине, т.к. изначально понятие «конкатенации» означало приписывание числа справа.
Вот дальше и начались мои размышления, как нам описать эту операцию т.к. что бы это еще работало. Самым простым способом показать, что расчеты не ломаются – это перевести их в другую систему счисления.
Но получилось сделать прикольную систему перевода между системами счисления.
habr.com/ru/post/566052
Но в заметках о проблеме числа 10958 часто встречается фраза — Массачусетский технологический институт готов выплатить 5000$!
То можете попробовать)
П.с.
Просто я сталкивался эвристикой, при которой наиболее простом случае нужно было до множить на 100. Что мне крайне не нравилось из-за костыльности решения, но ничего иного в голову не приходило. И как-то видео с этим числом и мои проблемы с эвристикой на ложилось друг на друга. Сейчас в моей голове сформировался какой-то математический смысл такой эвристики, да и на задачку решение ложиться, а не просто абстрактные рассуждения)
Да и фиг с ними.)
— Говорю — авторы сами намудрили решением простой задачи, а потом попросили пользователей тыкнуть пальцем в небо и написать решение которое будет похоже на их мудрёное, которое не отражает суть изначально поставленной задачи.