Алексей @jdev
Автор Эргономичного подхода, Kotlin/Backend техлид
Information
- Rating
- Does not participate
- Location
- Кольцово, Новосибирская обл., Россия
- Date of birth
- Registered
- Activity
Specialization
Chief Technology Officer (CTO), Software Architect
Lead
From 350,000 ₽
Functional programming
Object-oriented design
Design information systems
TDD/BDD
Kotlin
PostgreSQL
Java Spring Framework
Linux
Git
Docker
Последние 4 года 90% моих тестов такие и есть - интеграционные, с БД в тестконтейнере и запросами по ХТТП.
И так как я работаю по ТДД и запускаю тесты по нескольку раз в минуту, мне пришлось научиться делать такие тесты более быстрыми, чем тесты на моках.
Два оснонвых секрета:
1) Не использовать @DynamicPropertySource, потому что это приводит к инвалидации контекста в кэше и запуску контекста для каждого тест-кейса
2) Использовать RAM-диск для постгреса.
Вместо DynamicPropertySource я использую такой трюк:
А чтобы посадить Postgres на рам-диск - такой:
В результате, у меня на i7-8700, 32 RAM, SSD интеграционные тесты выполняются от 14мс при тесте с моками в 163 мс:
А в проекте со скрина, я пошёл ещё радикальнее - отказался от @SpringBootTest и запускаю приложание руками, а в локальной разработке сначала ищу предзапущенную БД:
Это позволяет сэкономить ещё пару секунд на инициализации тестов, что имеет существенное значение, когда ты делаешь зелёным один тест кейс.
А вот ещё вспомнил пост
Я сам ограничился этим :)
Мне кажется тут надо смотреть не на количество клиентов, а на количество компаний/разработчиков.
Но вообще я вполне допускаю, что JPA стандарт только в моём инфопузыре и объективно она не так популярна, как мне кажется.
А и самое главное забыл - SDJ интегрируется с MyBatis из коробки:)
То есть вы можете писать в БД через SDJ, со всеми его плюшками, а читать через MyBatis со всеми плюшками ещё и MyBatis.
Не совсем понял ваш посыл.
Константин, проводил свои исследования в 60-ых - лет за 40-50 до хайпа ФП.
Я бы не стал связывать языки с поддержкой парадигмы и разработку в соответствии с парадигмой - на любом (Тьюринг полном) языке можно писать в соответствии с любой парадигмой, вопрос только в бойлерплейте. При том ФП парадигма + более-менее подходящий более-менее мейнстримный язык - это дешевле, на мой взгляд, чем ФП парадигма + чисто функциональный язык под который сложно найти разработчиков, библиотеки, документацию ко всему этому и решения не самых тривиальных проблем.
Но тут мы уходим в холивар, что такое ФП. В котором даже кложуристы с хаскеллистами не могут решить кто из них Труъ ФП.
Полагаю, основная причина - потому что в ВУЗах учат C/Java/Python etc.
Ненене, я в своём уме, я бы никогда не стал этого делать:) Этот пример не про то, что ФП быстрее, а про то, что ФП "понятнее" для компилятора, из чего я делаю предположение, что оно и для человека понятнее.
Ну тут мне кажется мы снова упираемся в вопрос, что такое ФП.
Например
Для меня - ФП. Нужна ли для докторская для чтения этого кода? Нет
Так Константин ровно то и проделал. Взял кучу программ с известной стоимостью, посмотрел что общего между дешёвыми программи и в чём разница с дорогими. Увидел что разница - в функциональной архитектуре.
Другое дело, что исходных данных нет - это да. Но так я и говорил о Гипотезе:)
Вы меня не верно поняли:) Я делаю вывод на основании эмпирического исследования Константина и собственного опыта. А эксперты и книги - это шло в разделе "Косвенные доказательства".
С реальным миром (с состоянием) невозможно работать в чистом функциональном стиле. Именно эту проблему и решает функциональная архитектура - разделяет императивный код, который работает с реальным миром и чистый код, который работает с красивыми моделями.
Соответственно у вас должен быть какой-то инфраструктурный код, который занимается работой с очередью - с одной стороны помещает данные в очередь, а с другой стороны - достаёт.
А вот что будет перед и после этого кода - зависит от задачи.
Как вариант, у вас на обеих сторонах может быть по координатору
Координатор на входе получает запрос по хттп идёт в БД, достаёт оттуда неизменяемую структуру данных, передаёт её в чистое ядро, получает результат обратно и перекладывает его в инфраструктурный модуль для публикации в очередь.
Координатор на выходе получает запрос от инфраструктурного кода и делает всё тоже самое, вплоть до публикации в следующую очередь.
Я согласен, что SDJ - тоже чёрная магия. Но, для меня по крайней мере, несопоставимо более простая чем JPA. С SDJ, я могу работать практически не заглядывая в доки и гугл (после прочтения этой самой доки и сбора базовых грабель), а с JPA постоянно приходилось гуглить заклинания, которые надо скастовать для получения требуемого результата.
Плюс я сейчас в петпроекте эксперементирую с отказом от генерации репозов в пользу самописанных на базе jdbcAggregateTemplate - это ещё пласт магии уберёт
В SDJ всё точно так же.
Плюс SDJ обещает в будущих версиях "решить проблему N+1 раз и навсегда" - это выглядит любопытно для меня.
Мне MyBatis, jooq (+ Exposed) - тоже симпатичны. Но чего мне в них нехватает - возможности сохранить дерево объектов "автомагически".
Я ни в коем случае не утверждаю, что ФП-программы быстрее императивных программ (хорошо написанных).
Этот пример не про то, что ФП-программы быстрее, а про то, что компилятор лучше "понимает" ФП программы и я это использую как аргумент в пользу того, что ФП-программы "понятнее".
Нет, к сожалению, публичного кода, который я бы сейчас стал приводить в пример у меня нет.
Могу предложить посмотреть на https://git.codemonsters.team/guides/ddd-code-toolkit/-/tree/dev - у Макса подход очень близок к моему, но у него структура функции-координатора на монадах.
Спасибо за пост - сам работаю примерно так же, но за другими топиками не доходили руки расписать этот подход, чтобы новым разработчикам на пальцах не объяснять. Теперь буду ваш пост скидывать:)
Спасибо:)
Обсуждать и тыкать плюсы (и минусы) можно (и нужно) телеграмме:)
Понял, спасибо.
Ну и за пост в целом ещ раз спасибо - я в восторге от него, он хорошо лёг на актуальную для меня сейчас проблему с разрастанием модулей в ООП-стиле. У меня пайплайны лежат в тех модулях, с стоянием которых они наиболее сцепленны и это ведёт к жирным модулям и сильной сцепленности модулей из-за пайплайнов, которым надо состояние нескольких модулей потрогать. И я второй день думаю о том как бы мне идею разделения состояния и пайплайнов в свою практику встроить
Угу, я так же извернулся.
Кажется не правильно поняли:)
Вот эта функция:
Я так понял - это статическое определение именно функции, а не переменной функционального типа. Соответственно подключение к внешнеей системе (БД, например) она берёт из глобальной переменной.
Отсюда я вижу два следствия:
При её использовании, надо догадаться что эту глобальную переменную надо про инициализировать
Нельзя иметь в программе два источника данных, которые будут брать юзеров из разных источников.
Или у вас всё-таки что-то в таком духе:
Ну т.е. как вы инжектите инфраструктуру в такие функции?
Касательно второго, комментария, я утверждаю (самовнушаю ), что следующая запись является эквивалентом предыдущей:
То есть конструктор выполняет функцию частичного применения своих параметров к методам объекта. Забыл уже терминологию
А если в рантайме собираете - чем это отличается от объекта?:) Я опять же отошёл от чистой функциональности и утешаю себя тем, что конструктор(p1, p2) + метод(p3) = функции(p1, p2, p3) :)
Фантастика. В хорошем смысле слова.
У меня есть пара практических вопросов.
Я от ROP-а в чисто функциональном стиле отказался как раз из-за синтаксического мусора, который он генеряет в Kotlin. Вы вроде сказали, что в Python те же проблемы, но не рассказали как это в итоге делаете. Расскажете?
Затем, функции в persistance. Они всё-таки по природе своей stateful (имеют источник подключения к БД условной). Вы их в итоге к глобальному окружению приколачиваете? Или через частичное применение в рантайме собираете? Или ещё как-то? Так же буду благодарен за подробности.
И вообще, если можете скинуть ссылку на код в этом стиле, который ходит в базу, ходит по хттп и чё-нить в очередь публикует (желательно одновременно) - изучу с большим интересом:)
А за State-driven и action-driven - отдельное спасибо. Я пару лет думал на эту тему в фоне и ни как не мог это лаконично сформулировать.
Слово микросервисы в названии - ради кликбейта, у меня МСы имеют штраф в 100 у.е. и я начинаю бить систему на разные процессы только когда по другому никак:)
Сходство и правда есть, но не полное.
Ресурсы - включают агрегаты, но не только.
Эффекты - это отдельные операции по чтению и модификации состояния
Операции - вот это аналог команд.
Именно отказаться цели нет - по возможности их по максимуму надо задействовать, опрашивать и учитывать. Но когда их добыть не получается - да, опираемся на своё понимание.
Если есть возможность их намайнить - не теряем. В декомпозиции учитываем принимая экспертные решения в сложных случаях и агрегации ресурсов, либо вообще отходя от алгоритма. И перепроверяем на этапе оптимизации
К текущему моменту этот подход я апробировал на 6 коммерческих проектах 1-12 человеко-месяцев. И я не думаю, что я буду когда-то "up front" проектировать систему на 10 человеко-лет. А если система не помещается в одну голову - это уже 101 балл в пользу разбиения системы на несколько.
Это не совсем так.
Основное отличие в том, что ДДД и ЭШ требует вовлеченности и "образованности" большого количества людей, а это не всегда возможно. Для этих случаев я и разработал декомпозицию на базе эффектов, которую может выполнить один человек и которая даёт достаточно хорошие результаты.
Это совсем не так:)
Способ реализации не зашит ни в концептуальную модель, ни в подход к декомпозиции и спроектированная таким образом система может быть реализована как угодно - я предпочитаю функциональную архитектуру, но не вижу никаких препятствий к тому, чтобы реализовать её хоть в виде гексагональной, хоть cqrs/вертикальной, хоть той же трёхзвенной модели. Надеюсь вы имели ввиду трёхуровневую архитектуру (более привычнй мне термин), а не трёхзвенную уголовную модель в казахстане ?
Для программирования не специфично, но в программировании есть свои ограничения, которые исходят из требований и планов/перспектив развития, и без приземления на них выбрать решение невозможно.
Ну и мне всё больше кажется, что вы говорите о "проблеме выражения". Плюс вспомнил ООПный способ её решения
За книжку - спасибо, добавил себе в список прочтения.
Может быть после неё я уже наконец всё пойму и соглашусь с тем, что ООП хорошее средство для моделирования реальности :trollface:
Я собственно и пошёл изобретать велосипед, т.к. я не понимаю, как проектировать системы через моделирование реальности. А вот как проектировать системы через операции и ресурсы - я понимаю
Я не могу понять "требования" за этим кодом. Все 4 операции доступны для конечного пользователя? Операции записи и печати как-то связанны между собой? Например печать берёт отчёт, ранее записанный в базу? Откуда берётся отчёт для записи в базу?
А вообще ваша задача очень похожа на проблему выражения у которой нет хорошего решения. А как из возможных решений выбирать, зависит опять же от требований и кофейной гущи - что чаще у вас будет появляться, новые данные или новые операции?