Здравствуйте, Miroff, Вы писали:
M>Желание похвальное, только эта абстракция практически сразу протекает. Ты же сам рассуждаешь о представлениях ресурса, а представления зависят от того, кто на них смотрит.
Я бы разводил такие вещи по разным "ресурсам", так, чтобы границы безопасности совпадали с границами ресурсов. Потому что это и надёжнее, и эффективнее.
Например: есть у нас, допустим, судебное постановление. Имена и реквизиты упомянутых там лиц — дело чувствительное. Анонимной публике их видеть не положено; а вот авторизованной — да, нужно.
Вариантов три:
1. При отдаче текста постановления смотрим в токен пользователя, и либо заменяем все имена плейсхолдерами, либо вставляем как надо.
2. Делаем два ресурса: "анонимизированное постановление", "полноценное постановление". Анонимный доступ ко второму получает 401, аутентифицированный доступ без прав на данное дело получает 403.
3. Делаем два ресурса: постановление, в "тексте" которого есть ссылки на фигурантов, каждый из которых — самостоятельный ресурс. Постановления отдаём всем желающим, детали фигурантов — только авторизованным пользователям. Остальные получают 403.
Как по мне, так первый вариант, очевидно, самый-самый плохой. Он плохо масштабируется, и крайне плохо проверяется на корректность.
M>Кому-то можно показывать одно, кому-то другое, третьему вообще ничего нельзя показывать. В результате, во всех без исключения проектах, которые я видел, кэширование за пределами контроллируемого контура отключено по-умолчанию и включается только для определенным образом проверенных ресурсов.
Всё верно. Сначала люди создают себе проблему путём принятия неверных архитектурных решений, потом всю жизнь от неё страдают. Интернет полон вопросов "как мне запретить кэширование респонсов моего сервиса?". Это не потому, что кэширование — плохо, а потому, что не все умеют проектировать сервисы.
M>Современная ИБ говорит, что если какой-то функционал пользователю недоступен он и не должен знать о существовании этого функционала.
Можно ссылку на источник, который вы цитируете? Я не эксперт по ИБ, с удовольствием ознакомлюсь.
M>Иначе мотивированный пользователь начнет искать способ для эскалации привилегий и, чем черт не шутит, вдруг найдет.
В той литературе по ИБ, которую я читал в детстве, излагаемая вами концепция называлась "security by obscurity" и считалась однозначным злом. По сравнению, естественно, с нормальной безопасностью, которая не построена на невежестве пользователей.
REST зарубает вашу идею на корню, т.к. весь "функционал" в нём известен заранее: GET/PUT/DELETE.
M>К тому же, с точки зрения UX скрытие кнопки эквивалентно вызову с ошибкой потому что а) причин ошибки бесконечно много и в этом случае все их нужно обрабатывать и б) ИБ не велит раскрывать внутреннее устройство приложения через сообщения об ошибках, так что пользователь максимум что увидит это "тебе нельзя".
Я не понимаю, что вы имеете под пунктом а). Да, совершенно верно, причин ошибки много, и их надо обрабатывать. В
любом случае. Как минимум вы должны уметь отличать 4хх от 5хх. Полагаться на то, что ошибок не существует — верный способ заслужить ненависть пользователей. Я знаю, что сейчас модно либо вообще не обрабатывать ошибки (тыкаешь кнопку — и
ничего не происходит), либо обрабатывать их в стиле "произошла неизвестная ошибка". Но нужно отдавать себе отчёт в том, что это — не лучший UX. Его можно чем-то оправдывать, но стремиться надо к хорошему — к UX, который пользователя уважает. В частности, не требует от него навыков трассировки веб-сайтов по F12 для того, чтобы отличить ситуацию "в этом детском саду нет мест" от "сессия прокисла, перелогиньтесь".
M>У тебя в любом случае есть связь между двумя ресурсами. Ее можно показать с одного конца, с другого конца или с обоих концов. Последний вариант самый универсальный. Если связь пересекает границы микросервисов, существуют способы это реализовать не увеличивая лишнюю связность: обогащение на уровне gateway, backend for frontend, обогащения на уровне middleware, наконец, просто засунуть в HATEOAS вместо списка туров, ссылку по которой этот список можно получить. Обычно, за HATEOS как раз и отвечает не сам сервис, а middleware.
Можно. Такая реализация, собственно, разрывает жёсткую зависимость микросервисов друг от друга. Фасад, при условии того, что он сам по себе устроен достаточно примитивно — хорошая штука для построения интегрированных сценариев. Но, опять же, он не является необходимым звеном. А вот устранение кольцевых зависимостей микросервисов — как раз суровая необходимость.
M>А давай посчитаем
Один указатель (кэшировать нужно не весь результат, а только порядок записей), пусть long -- 8 байт * 300k запросов в сутки * 1000 записей (ты же не забываешь про max_search_results)= всего 2.2Gb При этом 300к уникальных запросов это где-то 20M DAU т.е. федеральная система уровня всего СНГ по поиску с запасом влазит на одну машину. У нас, слава богу, не 2005 год и уже завезли кэши в разделяемой памяти, типа hazelcast и ehcache.
Омг, омг. Во-первых, кэшировать нужно, конечно же, весь результат. Потому что иначе совершенно непонятно, как в поиск Ивановых затесался Петров. Или в список билетов до 12000р затесался перелёт за 58000.
Во-вторых, что такое "указатель"? Я так понял, вы положились на то, что все исходные данные загружены в память вашей единственной машины?
M>Во-первых, проверка изменились ли результаты поиска эквивалентна выполнению самого запроса, так что кэширование результатов поиска не имеет смысла и никто им не пользуется.
Вы только что предложили кэшировать результаты запроса, и сами же пишете "никто не пользуется". Веб устроен так, что вы никуда не денетесь от распределённого состояния. Даже когда вы смотрите на страницу RSDN, вы смотрите не на "сервер", а на локальную копию результата запроса, которую вам отображает браузер. Весь вопрос — в том, можно ли склеить "полный" результат из его постраничных представлений.
M>Во-вторых, допустим клиент узнает что результаты изменились, делать-то ему что? Повторять запрос?
Зачем? REST как раз позволяет "обновить кэш" одним запросом безо всяких повторений.
M>Тогда никакой консистентности при переходе между страницами не будет и получится ровно то же самое что и с параметрами offset + limit в запросе. Настроить промежуточный кэширующий сервер, который примет полные результаты поиска, а отдавать будет с учетом range. Ну так то же хранение и получится.
Всё верно. Только это хранение вы реализуете не за счёт своих средств, дорогостоящих и ограниченных, а за счёт средств клиентов, которые масштабируются автоматически и бесплатно.
Наличие "промежуточного" кэшируюшего сервера — не обязательная часть решения. Можно считать, что у вас есть "кэширующий клиент", который способен построить иллюзию согласованной бесшовной картины.
Вы застали до-гуглмэпные времена? Когда основным картографическим сервисом был MapQuest, который как раз реализовывал концепцию "постраничной навигации". Пользователь мог двигать "окно просмотра" по фиксированной сетке, перемещаясь по ней в четырёх направлениях. Гугл сумел придумать и реализовать клиента, который создаёт иллюзию "бесконечного канваса", несмотря на наличие под капотом точно такого же "постраничного" механизма.
Теперь всем очевидно, что именно такой способ навигации по "огромной плоскости" и является нормальной реализацией концепции просмотра большой картинки через маленькое окно. Карты гугла, яндекса, 2гиса, и примерно кого угодно теперь устроены именно так, и никак иначе. Та же концепция — у Miro, Figma, и бесчисленного множества диаграммных сервисов.
И только "табличные данные", которые являются частным случаем ровно той же задачи, до сих пор реализуются методиками девяностых годов прошлого века. Нет никакой причины так делать, кроме "здесь так принято" и "мне лень думать, как сделать нормально".
M>Мы подняли все ресурсы, входящие в сценарий, убедились, что их состояние позволяет реализовать этот сценарий и только после этого начинаем претворять сценарий в жизнь. Понимаешь, когда речь идет про микро сервисы, когда каждый сервис отвечает за свой домен и управляет одним-двумя ресурсами, почти все сценарии захватывают несколько сервисов. При этом подавляющее большинство сценариев не требует строгой консистентности, вполне достаточно eventual consistency. Поэтому нет необходимости любой сценарий превращать в сагу или двухфазный коммит. Более того, сценарии, требующие строгой консистентности встречаются настолько редко, что большинство разработчиков такого не реализовывали ни разу в жизни. В то же время, eventual consistency это все еще consistency, а если у нас сценарии разваливаются через раз, никакой консистентности не будет. Поэтому, дизайнить систему так, чтобы сценарии не разваливались, это хорошая идея. В том числе поддерживать между сервисами контракт, что если мы получили от строннего сервиса ресурс и список связанных ресурсов, то попытка обратиться к этим ресурсам не вызовет ошибки.
Этот "контракт" невозможно надёжно обеспечить в реальной среде. Всё, что можно делать — это
притворяться, что он выполняется. Поэтому существенной разницы между hateoas и "компактной" формой ресурсов с точки зрения достигнутого результата я не вижу.