(java || kotlin) && devOps
369 subscribers
6 photos
1 video
6 files
306 links
Полезное про Java и Kotlin - фреймворки, паттерны, тесты, тонкости JVM. Немного архитектуры. И DevOps, куда без него
Download Telegram
Всем привет!

Если кто не знает - у PostgreSQL есть такая интересная фича, как механизм NOTIFY/LISTEN https://www.postgresql.org/docs/current/sql-notify.html
А вот пример его использования: https://eax.me/postgresql-notify-listen/
По сути мы получаем очередь средствами СУБД. Чем она лучше той же Kafka - тем, что выполняется в контексте транзакции СУБД, и т.об. событие не будет отправлено потребителям до тех пор, пока транзакция не закоммитится. Это логично, более того, для событий, возникающих внутри БД, может быть критично. Что интересно: если LISTEN также находится внутри транзакции - событие тоже будет доставлено только после ее фиксации. Это не так очевидно, но причина аналогична NOTIFY - если транзакция с LISTEN откатится, а в коде клиентского приложения, подписанного на события, будут выполнены какие-то действия вне контура БД - откатить их не получится.
Самое простое применение NOTIFY/LISTEN - инвалидация кэша при изменении таблицы в БД.

Но такую "очередь" в БД можно использовать для произвольных событий, не связанных с БД. Вот подробный пример как интегрировать Spring Integration и NOTIFY\LISTEN https://www.baeldung.com/spring-receiving-postresql-push-notifications
Зачем? Ну например если Kafka разворачивать не хочется, PostgreSQL уже есть, нужна персистентность, и есть множество подписчиков в разных процессах\на разных серверах.

Какие у данного механизма ограничения:
1) производительность PostgreSQL на порядки меньше Kafka
2) есть ограничения на размер сообщения и общий размер очереди
3) все сообщения хранятся в одной очереди pg_notify на диске, которая может стать узким местом.
4) очередь pg_notify в отличие от данных в таблицах не устойчива к падениям СУБД, при перезапуске она очищается, WAL не используется
5) каждая подписка на события забирает один коннект к СУБД из пула

Использовать ли события PostgreSQL? Пилот или небольшая нагрузка + допустимость потерь сообщений - почему бы нет. Остальные случаи надо смотреть детальнее и конечно же проводить НТ

#postgresql #messaging
Всем привет!

Хорошая статья про String Templates в Java 21 https://blog.jetbrains.com/idea/2023/11/string-templates-in-java-why-should-you-care/
Самое интересное в шаблонах Java вот что.
Лично у меня часто складывается впечатление, что Java копирует языковые фишки, они же синтаксический сахар, у Kotlin, Scala, C# и других языков. Копия иногда такая же по функционалу, иногда хуже. Повторюсь - именно это впечатление, т.к. строго говоря появление фичи А после фичи Б не значит, что она скопирована.

Но в данном случае строковые шаблоны получились хоть и не такими синтаксически простыми, но более крутыми по функционалу.
Выглядят они так:

STR."some string \{varWithMeaningfulName}"

Да, странно что \ вместо $, но самое важное здесь то, что шаблон является методом у некого класса. Это класс StringTemplate.Processor<String, RuntimeException>. В Java core есть ряд его стандартных реализаций, но самое главное - можно сделать свою. В статье выше есть парочка интересных примеров.

Как по мне - крутая фича! Жалко, что дотянули до 21-й Java, и то пока preview)))

#java #kotlin #strings #java_new_version
Всем привет!

Недавно я хвалил Java, пришло время поругать) Для начала вот фактура:
https://habr.com/ru/articles/676852/
Если вкратце: в Java есть стримы и есть проверяемые исключения. Если первые совместить со вторыми - код стримов из компактного и красивого становится ужасным. Да, технически все работает, но теряется смысл стримов - компактный код в функциональном стиле.

С одной стороны можно сказать - да и фиг с ними, с этими проверяемыми исключениями. Да, их можно не использовать в своем коде. Я, например, так и делаю, т.к. недостатки проверяемых исключений перевешивают их преимущества, см. https://t.me/javaKotlinDevOps/205.
Но во-первых они могут приходить к нам из внешних библиотек, а во-вторых основной принцип Java, ее сила - максимальная совместимость. Именно совместимостью оправдывается скорость развития Java. А тут получается про нее забыли. И что важно - streams появились в Java 8. Недавно вышла Java 21. Функционал проверяемых исключений хоть особо не развивается, но и не стал deprecated. Значит условный Try из статьи выше должен быть в стандартной библиотеке Java.

#java #exceptions #checked_exceptions #java_streams
Всем привет!

Недавно набрел на интересную статью - https://enterprisecraftsmanship.com/posts/domain-model-purity-completeness
Настоятельно рекомендую ее прочитать, но как всегда вкратце перескажу)

В крупную клетку любой нормально спроектированный сервис включает в себя контроллер (порт в терминологии гексагональной архитекторы), модель и адаптеры.
Вся логика должна быть в модели. Но что делать, если предварительная проверка данных требует обращения к внешней системе (БД), и при этом построение запроса для проверки - это тоже бизнес-логика.

Варианта предлагается три:
1) внести ее в модель, скрыть обращение к данным за интерфейсами, но в любом случае в итоге наше ядро (модель) лезет в БД, что плохо. В первую очередь плохо концептуально, а если "спустится на землю" - сложнее писать модульные тесты, увеличиваются риски "загрязнения" ядра. Т.е. следующие поколения разработчиков видя, что из модели вызывается СУБД, скажут - а что, так можно было?) И будут тянуть в модель другие внешние зависимости. Теория еще такая есть, разбитых окон. К слову - автор статьи также автор отличной книги о модульном тестировании, я о ней уже писал https://t.me/javaKotlinDevOps/50, возможно поэтому ему данный вариант не нравится
2) оставить часть логики в контроллере. Но тогда получается, что логика размазана по двум слоям
3) заранее загрузить нужные данные в ядро. Допустимо, но только для каких-то маленьких и редко меняющихся справочников, типа регионов. Т.е. только в отдельных случаях.

В итоге у нас компромисс между полнотой модели, строгостью соблюдения архитектурных принципов и скоростью работы.
Что тут интересно - главная идея статьи не о том, как сделать правильно, а про то, что разработка ПО - это искусство компромиссов.
Теорема CAP, упоминаемая в статье, к слову о том же. Единственного правильного для всех случаев жизни решения нет. Увы(

Еще одно дополнение. В теории возможен еще один вариант, расширяющий пространство компромиссов. Он немного "наркоманский" в том плане, что усложняет систему.
Предположим мы сделаем интерфейс валидатора в модели. Правила валидации будет задавать декларативно и хранить в модели, не привязывая их к конкретному хранилищу. Т.е. код контроллера вместо:

validateLogic()
callModel()

превращается в:

val rules = getValidateRulesFromModel()
val request = buildValidateRequest()
validate(request)
callModel()

Сам же и покритикую. Задача вроде решена, но дополнительного кода потребуется много. Второй минус, тоже важный - последовательность вызовов неочевидна. Если новый разработчик придет в команду - очень может быть он скажет "WTF" и захочет переписать. Как решение этой проблемы могу предложить описывать алгоритм в документации к коду или аналитике. Документация не нужна с "говорящим" кодом, но тут как раз исключение. Но сложность понимания кода в любом случае повышается.
Т.об. в пространство компромиссов мы вводим еще один параметр - сложность. Полнота модели, целостность архитектуры, скорость и сложность.

#arch #unittests #dev_compromises
Всем привет!

В продолжение темы про скорость и аккуратность внедрения новых фичей в Java. Хороший пример - pattern matching, который наконец окончательно внедрили в в Java 21. Почему хороший пример - потому что до этого было 4 Preview начиная с 17 Java) Всего 2.5 года получается)
Вот тут детали: https://www.infoq.com/news/2023/07/tranforming-java-pattern

Кажется долго, да.

А если я уточню, что речь идет про pattern matching for switch.

switch (response) {
case null -> { }
case String s when s.equalsIgnoreCase("YES") -> System.out.println("You got it");
case String s when s.equalsIgnoreCase("NO") -> System.out.println("Shame");
case String s -> System.out.println("Sorry?");
}

А до этого был Pattern matching для instance of https://www.baeldung.com/java-16-new-features#pattern-matching-for-instanceof-jep-394 в Java 16?

if (x instance of String) {
x.trim()
}

А кроме того параллельно pattern matching для record в 19 Java https://www.baeldung.com/java-19-record-patterns

Double result = switch (object) {
case Location(var name, GPSPoint(var latitude, var longitude)) -> latitude;
default -> 0.0;
};

P.S. Но история еще не закончена)
См. описание последнего внедренного JEP, тот самый из Java 21, раздел Future work https://openjdk.org/jeps/441 -
работа с примитивами, деконструкция произвольных классов, а не только record, сложные условия в case блоке

P.P.S. Но Kotlin обогнали https://youtrack.jetbrains.com/issue/KT-186 )))
Со Scala вроде паритет, но нужны эксперты

#java
Всем привет!

Есть такая проблема в больших компаниях, а-ка "кровавый enterprise" - сложность внедрения новых технологий.
Я про СУБД, в частности noSQL, брокеры сообщений, стриминговые платформы, системы мониторинга, DevOps pipeline, облачные решения и т.д.
Объясняется это тем, что любую внешнюю систему кто-то должен поддерживать, а сейчас в компании этим никто не занимается.

И это на самом деле сильный аргумент, т.к. в больших компаниях разработка и поддержка разделены. А это значит разработчик может затащить новую систему, разворачивать, настраивать и поддерживать которую будет кто-то еще. Более того, если говорить о крупных компаниях - сопровождение там может делиться на прикладное, канальное и инфраструктурное. За СУБД и очереди отвечает последнее.

Что же тут можно сделать?
Просто запретить все новое - вариант очевидно плохой.
А вот снизить "боль" разработчиков, пытающихся внедрить что-то новое, можно так:

1) вести открытый реестр разрешенных технологий. Открытый - значит информацию о нем не нужно искать через корпоративных архитекторов, секретные ссылки и т.д
2) около каждой системы должно быть описание - чем она сильна, какие альтернативы рассматривались и почему в конченом итоге была выбрана именно она
3) должна быть процедура предложений по внедрению новых решений, доступная любой команде
4) в этой процедуре должны быть описаны необходимые для внедрения условия. Тут видится два варианта:
а) внедрение поддерживает множество команд, с конкретными сроками, тогда компания сама "покупает" поддержку - создает команду сопровождения внутри или берет внешних подрядчиков.
б) команда, предложившая новую технологию, сама начинает отвечать за ее поддержку. Как она будет совмещать это со своей текущей деятельностью, потребуется ли ее перевод в инфраструктурные, возможно ее разделение на 2 части - вопрос договоренностей. Ну и да, инициатива имеет инициатора, к этому нужно быть готовым)
5) должны существовать открытые канала для обсуждения новых технологий. Чаты, рассылки, база знаний

Я не изучал, как этот вопрос решается в Agile, но кажется, что должен решаться именно так. В рамках подхода снизу-вверх.

Ну а плохой вариант - когда список технологий спускается откуда-то сверху, назовем это место башней из слоновой кости) А вопросы почему именно это и можно ли что-то добавить подвисают в воздухе, т.к. не понятно, кому их вообще задавать.

#arch #technology
Всем привет!

В развитие прошлого поста предлагаю поговорить про большие компании и новые языки программирования.
С одной стороны тут нет противоречия разработка-сопровождение.
Если говорить про JVM или .NET языки - т.е. случай, когда есть виртуальная машина между приложением и ОС - то при смене языка ничего в плане сопровождения не меняется. Случаи, когда новый язык сгенерирует какой-то неработающий или неоптимальный байт-код я рассмотрю далее.
А если даже язык не JVM\.NET - мы живем в мире победившего Docker и тут все еще лучше: сопровождению остается провалидировать базовый образ, который к нам пришел от создателей языка, и\или выставить требования, что там должно быть.

Может возникнуть вопрос - а что делать, если готового базового образа нет?
Я бы его перефразировал - а что, если язык настолько редкий или новый, что его создатели забыли\не доросли до Docker.
Это вопрос к тем, кто занимается наймом. HR или Tech Lead\ИТ лидер. От них нужен анализ рынка для того, чтобы ответить на вопрос: что будет, если разработчик, предложивший новый перспективный язык, уйдет из компании? Насколько такие разработчики дороже?
Кроме анализа рынка тут может помочь https://www.tiobe.com/tiobe-index/, в частности подсказать тренд - растет или падает популярность языка.
И третий критерий фильтрации языков - насколько язык сложен для обучения. Тут поможет экспертное мнение Team Lead\Tech Lead.

По первому критерию для сферической компании в вакууме я бы отмел, к примеру, Scala. И как ни обидно для Scala - по-третьему тоже. Все же Scala - это немного brainfuck)
Почему для сферической компании в вакууме - есть компании в РФ, где Scala живет давно, для них очевидно этот аргумент не актуален.
Да и по сложности обучения все не так однозначно - если в компании принцип: "Отбираем лучших", - то Scala как раз может быть позитивным фильтром)
По второму критерию можно отфильтровать Ruby, пик его популярности пройден.

Итого - согласовывать переход на новый язык программирования нужно, но основной вопрос здесь в легкости\стоимости найма разработчиков и простоте обучения для новичков. К сопровождению или корпоративной архитектуре он отношения не имеет.

Да, возвращаясь к вопросу про неработающий байт-код. Если язык на рынке давно, то вероятность такого события пренебрежимо мала. А чтобы ее уменьшить еще сильнее есть древнее правило - не надо обновляться на новые версии в первых рядах, стоит подождать хотя бы месяц-другой. И чтобы добить эту тему - возражение "мы не сможем оказывать вам поддержку (консультации) для языка xyz" - некорректно. Т.к. если язык распространен - поддержку окажет документация, stackoverflow, habr, Yandex\google, или даже ChatGPT

#jvm #language #scala
Всем привет!

Уже прошел примерно год как появился ChatGPT. Его активно пытаются применить везде - для решения бизнес-задач, автоматизации рутины и конечно же написания кода. Какую же рутину можно автоматизировать в разработке? А например такую - https://github.com/Nutlope/aicommits

#git #commit
image_2024-02-05_18-02-46.png
173.1 KB
Всем привет!

Не так давно появилась такая технология как Function as a Service или сокращено FaaS. Ее цель - затащить в облако все кроме написания кода. Сегодня нашел неплохую картинку, иллюстрирующую ее на примере сервисов Google.

Слева - это как раз FaaS. Т.е. мы отдаем на откуп облачного провайдера железо и виртуальные машины (instance), управление контейнерами, runtime (JDK,.NET framework), веб сервер и даже веб контроллер (single-endpointed). И хоть тут это не указано - CI\CD pipeline.

Плюсы:
1) сопровождение и DevOps уходят на аутсорс.
Минусы:
1) vendor lock, хотя стандарт разрабатывается,
2) платим за время работы кода и платим достаточно дорого,
3) не подходит для выполняющихся долго или висящих в памяти постоянно процессов

#cloud #faas
Всем привет!

Периодически я вижу в коде такую ситуацию.
Есть метод с 5 параметрами, надо передать ему еще 5. Если сделать это "в лоб" - и выглядит ужасно, и SonarQube ругается.
Что можно сделать?
Ну, например, завести объект XYZParams. По сути DTO, используемую в двух классах - вызывающем и вызываемом.

Мне это решение изначально не понравилось, но я не сразу смог объяснить чем именно) Самое простое объяснение - выглядит как lifehack. Требует SonarQube меньше параметров - вот тебе один параметр. Но объяснение мне не нравилось, стал копать дальше.

И как часто бывает - набрел на статью метра, почему это плохо.
https://martinfowler.com/bliki/LocalDTO.html
Советую почитать и вообще занести этот сайт себе в закладки.
Но если вкратце:
1) DTO - как Transfer объект был создан для передачи по сети, когда велики накладные расходы. Ну или для удовлетворения требований безопасности
2) новая DTO - это всегда маппинг, как минимум односторонний. Маппинг - потенциальный источник ошибок при изменениях
3) и добавка от меня: если доменную модель со всех сторон обложить DTO и маппингами - а зачем она тогда вообще нужна?)

Решение же проблемы может быть два:
1) завести подходящие доменные объекты и передавать их, а не одиночные параметры
2) реорганизовать логику, чтобы одному методу не требовалось столько данных

#arch #dto
Всем привет!

Вытяну ссылку из комментариев сюда: https://youtu.be/hUzpe73Oa3g?si=c_dY1YU2Cc_F8YiY
Хороший ролик про границы применимости паттерна Value Object.

На всякий случай - что такое Value Object и чем он отличается от DTO - https://matthiasnoback.nl/2022/09/is-it-a-dto-or-a-value-object/

Также стоит отметить, что данный паттерн является одним из основных в DDD - Domain Driven Development.

А по видео у меня такой краткий вывод - а точнее два:
1) у любого паттерна есть своя область применения
2) когда вы придумали некий хитрый лайфхак, перед тем как реализовывать его в коде стоит взять паузу и подумать.
Насколько он понятен для новичка? Не усложнит ли он код? Насколько? Не станет ли поддержка такого кода сложнее? Не добавит ли он в вашу модель "уязвимость", позволяющую использовать классы и методы не так, как задумывалось изначально?
Часто лучше написать больше простого кода, чем меньше, но неочевидного и допускающего неверное использование. И далее либо разбить этот код на микросервисы, либо на модули - например, см. мой пост про Modulith - https://t.me/javaKotlinDevOps/143

#patterns #arch #dev_compromises
Всем привет!

Есть такой интересный вопрос - можно ли поместить СУБД в облако?
Если отвечать на него строго технически - да, можно, для этого в k8s есть специальные типы объектов - StatefulSet https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/ и PersistentVolume
https://kubernetes.io/docs/concepts/storage/persistent-volumes/
которые обеспечивают ряд требуемых для СУБД характеристик:
1) подключенное хранилище не удаляется при каждой остановке пода
2) экземпляр StatefulSet имеет постоянное имя
3) процедура масштабирования StatefulSet в обе стороны последовательна - поды добавляются\удаляются по одному

Более того, в документации кубера есть пример с раскатыванием MySQL https://kubernetes.io/docs/tasks/run-application/run-replicated-stateful-application
Причем с возможностью автоматического масштабирования на чтение. Пример на самом деле меня впечатлил: сотня строк манифестов yaml - и БД уезжает в облако.

Так ли все хорошо?
Конечно же нет)
Для начала я бы задал типичный "менеджерский" вопрос - какую задачу решаем?

Первый момент - горизонтальное масштабирование на чтение можно обеспечить для любой СУБД. Как в облаке, так и без облака. На запись - только для NoSQL хранилищ с поддержкой шардирования.

Второй момент: сопровождение СУБД - это не просто гарантия наличия работоспособного инстанса на ПРОМ. Это еще и периодические бэкапы, откаты на резервную копию, начальное заполнение БД данными, заполнение "горячего" кэша, выбор нового master если старому плохо (leader election), тонкие настройки репликации и многое другое, чего я просто не знаю. А знают - хорошие DBA. Т.е. получается даже если мы поместим обычную СУБД в облако - DBA все равно нужен. Причем DBA, умеющий в облако, а это, кажется, редкая птица)

Т.е. все плохо? И снова нет. Если есть потребность отдать БД "на аутсорс" в облако, то выход - использование хранилищ от облачных провайдеров, спроектированных для работы в облаке. Там все вышеописанные тонкости будут учтены. Примеры:
1) Amazon Aurora https://habr.com/ru/companies/oleg-bunin/articles/471686/
2) YDB https://cloud.yandex.ru/ru/services/ydb
3) Azure Cosmos DB for PostgreSQL https://learn.microsoft.com/en-us/azure/cosmos-db/postgresql/introduction

Ну или хотя бы использование заточенных под работу в облаке СУБД. Вот пара примеров, первыми попавшихся мне на глаза:
1) Tarantool https://habr.com/ru/companies/vk/articles/533308/
2) CockroachDB https://www.cockroachlabs.com/docs/v23.2/deploy-cockroachdb-with-kubernetes
Как можно заметить, в обоих случаях есть специальный оператор k8s для управления кластером.

P.S. Тема сложная, интересная, поэтому думаю это не последняя статья.

#k8s #storage #cloud #rdbms
Всем привет!

Есть интересная тема - инструменты сборки для JVM проектов. А в рамках нее другая горячая тема - управление конфликтами зависимостей. Когда в проект подтягивается, как правило транзитивно, две версии одной и той же зависимости. А должна остаться только одна)
Отличное сравнение 3 систем сборки по управлению конфликтами зависимостей еще 10+ лет назад проведено в этой статье: https://habr.com/ru/companies/jugru/articles/191246/
Вывод из статьи - в Maven все сделано, скажет так, странно)))
Приходится явно указывать нужную версию каждой конфликтной зависимости в проекте.

Первый вопрос, который приходит на ум - зачем в Maven так сделали и когда собираются исправлять.
Ответ тут - https://stackoverflow.com/questions/34201120/maven-set-dependency-mediation-strategy-to-newest-rather-than-nearest
Спойлер - исправлять не собираются, считают, что так сборка будет более предсказуемой и повторяющейся. Т.е. описанный выше подход - запускай приложение, находи конфликты в runtime и указывай явно версию в своем модуле - считается правильным. Но есть лайфхак - см. ответ на stackoverflow.

Ну а чтобы найти версии проблемной зависимости - нужен mvn dependency:tree. О его "секретных" (на самом деле полезных) ключах этой таски Maven можно почитать тут https://www.digitalocean.com/community/tutorials/maven-dependency-tree-resolving-conflicts

Ну и если хочется копнуть глубже, например понять, как разрешается конфликт scope-ов зависимости или узнать про то, как разработчик библиотеки может уменьшить возможность появления конфликта (optional) - см. главный источник истины по Maven - его документацию https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html

#maven #gradle #java #buildtool #dependency_management
Всем привет!

Вдогонку ко вчерашней теме про управление зависимостями.
В Maven есть две фичи, смягчающие боль по управлению зависимостями:
1) bom - Bill Of material - pom-ник, объявляющий версии протестированного и гарантированного рабочего набора обязательных зависимостей, например, библиотеки Spring Security
2) dependencyManagemenet - возможность в одном месте, как правило это корневой pom, объявить все версии используемых в проекте библиотек. Туда же можно подключать уже готовые bom-ы. После этого задача поднятия версии какой-то зависимости по всему многомодульному проекту упрощается

Есть ли что-то подобное в Gradle?
Да, есть.
Вот тут описывается как "сэмулировать" bom в Gradle - https://habr.com/ru/articles/784784/
Ключевые слова platform и constraints https://docs.gradle.org/current/userguide/dependency_constraints.html
Почему я говорю сэмулировать: если посмотреть на структуру bom - это типичный Maven xml файл. Gradle публикует bom в двух форматах - maven для совместимости и свой json, вот пример https://repo1.maven.org/maven2/io/github/mfvanek/pg-index-health-bom/0.10.2/

Но на самом деле Gradle умеет чуть больше - есть такая штука, как catalog - по сути позволяющая структурировать зависимости в древовидную структуру и дать имя-ссылку каждому уровню. См. https://docs.gradle.org/current/userguide/platforms.html Причем объявлять завимости можно как в build.gradle, так и в отдельном toml файле. Каталог можно использовать сам по себе - как набор версий, так и ограничивать с помощью него версии транзитивных зависимостей - через те же constraints и platform.

#gradle #maven #depenedency_management #buildtool
Всем привет!

Наверное вы слышали про такой стандарт API как GraphQL. Если посмотреть на главную страницу их сайта https://graphql.org/, то там бросается в глаза фраза: "Evolve your API without versions".
Если воспринимать ее буквально, то можно подумать: "О, круто, API без версионирования! А что так можно было?)"

Но как это обычно бывает в разработке - не все так просто.

Если подумать - API без версионирования и без сопутствующих ему проблем, в частности несовместимых изменений, может быть только в том случае, если в API вообще нет обязательных полей. Но полезность такого API стремится к нулю. А точнее это уже не API, а что-то похоже на поиск Google или Yandex или общение с ChatGPT.
В GraphQL конечно же обязательные поля есть. Единственный момент - по умолчанию все поля не обязательны.

А если подумать еще - есть еще один способ. Завязаться на какой-то стандарт уровня HTTP, HTML или SWIFT, у которого так много потребителей, что волей неволей придется поддерживать обратную совместимость. Матерится сквозь зубы, но поддерживать) И то, даже в таких областях появляются новые версии API.
Плюс можно добавить уровень API шлюза - наружу выдаем API по стандарт, внутри - свое, которое может меняться свободно.
Но напрямую к GraphQL этот кейс не относится, это можно сделать и с REST, и с JSON-RPC.

Но если вчитаться в документацию GraphQL внимательнее, то видно, что авторы предлагают другое - эволюцию API мелкими шагами без явного задания версий.
https://principledgraphql.com/agility/#5-use-an-agile-approach-to-schema-development
Agile в деле построения API.
По сути это старый добрый принцип:
1) добавляем новый метод в API
2) старый объявляем deprecated
3) уведомляем об этом потребителей, не забывая про дату вывода из эксплуатации
4) когда придет время удаляем старое API
Profit!

В GraphQL выглядит это вот так:

type Account {
surname: String! @deprecated(reason: "Use personSurname")
personSurname: String
}

В чем тут могут быть проблемы:
1) не факт, что потребители вовремя обновятся. Да, deprecated на уровне API лучше, чем рассылка потребителям или страничка в сети, но это не панацея. К слову, OpenAPI тоже так умеет.
2) да, контролировать потребителей на уровне отдельного deprecated поля в GraphQL гораздо проще: возможность на клиенте указывать только нужные поля - это наверное главная фишка GraphQL. Но не все потребители будут так делать. Некоторые - сознательно, нарушая конвенцию использования GraphQL, некоторые - случайно, просто забыв убрать лишние поля
3) если работать до последнего потребителя - API быстро превратится в помойку
4) а главное: если заводить множество мелких изменений в API - каждый спринт по изменению - со временем очень сложно станет этим управлять. Да, есть инструменты, облегчающие управление - https://github.com/kamilkisiela/graphql-inspector Да, нужна четкая политика по работе с изменениями API. Но у меня есть сомнения, будет ли это все работать при непрерывном потоке изменений в большой организации без введения новых версий. Как и Agile в целом такой подход требует ответственности от команды и также плохо масштабируется.

Ну и последнее. Да, ничего для создания версий в GraphQL нет. Но никто не мешает развернуть рядом endpoint и назвать его graphql/v2. Или в схему к полю xyz добавить поле xyzV2 )))

#api #graphql #versioning
Всем привет!

Самая крутая фича GraphQL - это возможность явного указания какие данные нужны клиенту.
Причем фильтровать можно на любом уровне вложенности объектов.
И к тому же фильтровать не только по возвращаемым полям, но и по списку записей - аналог WHERE в SQL.
Пример запроса:

{
authors(surname: "Пушкин") {
name,
surname,
books(year: "1831")) {
title,
genre,
year
}
}
}

На стороне сервера для каждого объекта в запросе - в моем примере Author и Book - можно написать свой fetcher, независимо вытаскивающий данные из хранилища.
А вишенка на торте - с помощью Federation https://netflix.github.io/dgs/advanced/federated-testing/ можно децентрализовать схему, расположив fetchers в разных микросервисах.

Но главный плюс также является и главной потенциальной проблемой.
GraphQL подталкивает к тому, чтобы был один endpoint, через который можно получить все данные.
А если потребители захотят получить все?)

Встают вопросы:
1) разграничения доступа. В целом решается интеграцией со Spring Security https://netflix.github.io/dgs/advanced/security/ Можно расставить аннотации @Secured над каждым объектом. Но я вижу больше рисков по сравнению с специальным REST endpoint, "заточенным" под определенный набор данных.
2) производительности - ведь одним запросом к GraphQL сервису в теории можно вытащить все содержимое БД. А скорее получить проблему N+1 и подвесить БД). Авторы естественно знают об этой проблеме https://netflix.github.io/dgs/data-loaders/ и предлагают решение: batching и кэширование. Но решение не коробочное, требуется проектирование

P.S. Еще одна крутая фича - graphiql - интерактивная веб-консоль для построения запросов, включенная по умолчанию https://github.com/graphql/graphiql C auto-completion.
Ну и конечно возможность подписки на события как часть протокола GraphQL. Реализуется как правило через WebsSocket https://netflix.github.io/dgs/advanced/subscriptions/

#graphql #api
Всем привет!

При чтении одной из статей наткнулся на такую штуку, как Oracle Autonomous Database. Загуглил. Первая ссылка конечно же на сайт Oracle https://www.oracle.com/autonomous-database/
Там вот такое описание:
"Oracle Autonomous Database is a fully automated service that makes it easy for all organizations to develop and deploy application workloads, regardless of complexity, scale, or criticality. The service’s converged engine supports diverse data types, simplifying application development and deployment from modeling and coding to ETL, database optimization, and data analysis. ..."

Все же сразу понятно?))))

А вот что пишет Гугл в быстрых подсказках:
"An autonomous database is a cloud database that uses machine learning to automate database tuning, security, backups, updates, and other routine management tasks traditionally performed by DBAs. Unlike a conventional database, an autonomous database performs all these tasks and more without human intervention."

Яндекс кстати "залажал" - выдал русский перевод с сайта Oracle.

Вопрос - что должно произойти, чтобы enterprise разработчики стали описывать свои технологии нормально?) Широкое внедрение open-source, похоже, не помогло. У многих enterprise уровень воды по прежнему сравним с типичными CEO оптимизированными сайтами, где ответ на простой вопрос скрывается где-то во 3-5)))) главе. Или вообще отсутствует. Так и растет популярность ChatGPT...

#blood_enterpirise #oracle
Всем привет!

Нашел хорошую статью о том, как совместить тестирование Spring контроллеров и один из самых известных фреймворков для тестирования REST - Rest Assured. https://www.baeldung.com/spring-mock-mvc-rest-assured

Кстати, в начале статьи есть ссылка на пример использования чистого Spring MVC Test, если кто его не использовал - можете сравнить синтаксис.

Еще статья хороша тем, что четко разделяет модульные и интеграционные тесты. И я бы разделил точно также) Я иногда задаю вопрос о видах тестов на интервью, ответ мне не всегда нравится. Для ленивых, вкратце - интеграционным тест можно считать, если появляется сеть - открывается порт, вызывается другой процесс, внешнее хранилище, пусть даже и в embedded варианте. Хотя справедливости ради - вопрос холиварный, из-за того, что много пограничных случаев.

#unittests #spring #rest #integration_tests #interview_question
Всем привет!

Наверное все здесь знают, что такое UUID. Universally Unique IDentifier. Можно использовать как искусственный ключ. С высокой точностью обеспечивает уникальность, хотя и не 100%. Казалось бы, о чем здесь можно рассказывать. Ну и UUID и UUID.

А если я скажу, что недавно вышла 7-я (!) версия стандарта?) Для меня это было сюрпризом.
Вот описание первых 5 версий: https://habr.com/ru/companies/vk/articles/522094/
Вот - какие проблемы решает 7-й https://www.pvsm.ru/sistemnoe-programmirovanie/367012
А вот генератор разных версий с кратким описанием каждой: https://idtools.co/uuid/v7

Если вкратце про суть проблемы - UUID часто используют в БД как ключ. Но при таком использовании у него есть один большой минус - значения UUID не возрастают монолитно, как, например, обычный инкремент. Где это может быть полезно - сортировка и партиционирование таблиц.
Что интересно - в первой версии UUID было зашито время, а время - это тоже счетчик, в формате "с начала эпохи" или Unix time. Но в первой версии время хранилось в 2 разных частях UUID, причем еще и в перевернутом виде.
А 6-я версия - легкая модификация 1-й, где первый блок, содержащий время, хранят в нормальном формате и по таким UUID возможна сортировка.

#uuid #rdbms