Information
- Rating
- 525-th
- Location
- Кольцово, Новосибирская обл., Россия
- Date of birth
- Registered
- Activity
Specialization
Chief Technology Officer (CTO), Software Architect
Lead
From 500,000 ₽
Functional programming
Object-oriented design
Design information systems
TDD/BDD
Kotlin
PostgreSQL
Java Spring Framework
Linux
Git
Docker
Технически (в нормальных языках :troll:) это можно сделать так:
Но стоит ли оно того в данном конкретном случае - для меня большой вопрос.
PS>
Ну а если у вас Прям Нормальный Язык для ФА (Haskell) - оно всё автоматом будет лениво тянуться
На самом деле это довольно сложно в имеющихся условиях.
Во-первых, структура графа вызовов - это только один из трёх аспектов архитектуры, по которой я сейчас работаю.
И, возможно, если бы у меня был бы доступ до всей вашей кодовой базы и цель перепроектировать - получилось бы что-то совсем другое. А может и нет.
Во-вторых, вы меня даже вменяемого нейминга лишили и мне приходится работать только с синтаксисом. А дизайн - про семантику, а не синтаксис.
В-третьих, в вашем примере практически нет бизнес-логики.
Но тем не менее, давайте я попробую.
Что бы я точно сделал:
Собрал вычисление h в отдельный метод
Сделал бы данные неизменяемыми, а сохранение обновлений явным
Возможно ещё обернул бы логические выражения проверок в методы с вменяемыми именами, особенно для b и c:
Но это всё косметика. Тут надо либо сильно глубже смотреть, либо в этом примере и правда бизнес-логика предельно простая и самоочевидная.
Публичных примеров прям кода прям с бизнес-логикой у меня ещё нет. Но есть:
Пример кода оркестрации и графа вызовов операции для операции со сложной бизнес-логикой
Пример построения сложной модели представления
Тривиальная оркестрация
Развесистая логика на чистых функциях
Пример построения docx-дока
Тривиальная оркестрация
Относительно развесистая логика построения в виде чистой функции
Тут код - прям наскоряк написанный говнокод - это был прототип фичи, которая оказалось не востребованной, поэтому рефакторить я его не стал. Но это чистый говнокод - если ему в fetchImage подсунуть Map::get - он для одних и тех же аргументов всегда будет возвращать один и тот же результат.
Здесь, здесь, здесь и здесь
Вот он
Чуть позже
Ответ на ваши опасения относительно Функциональной (а Симан в дополнение к Фрактальной топит и за Функциональную) архитектуру так же есть в посте:
Ответ есть в посте
Да, это правда. В моём случае так и есть - последние 4 года я либо делаю проекты с нуля, либо реорганизую их.
В этом случае, имхо, надо держать в голове только два стандарта - тот за который платят прямо сейчас, и тот, за который вы хотите чтобы вам платили:)
Цель для меня - психологическое благополучие - у меня низкая стрессоустойчивость, поэтому в условиях горящих сроков и критов на проде мне жить некомфортно. А цель для бизнеса - минимизация суммарной стоимости владения продуктом. Структурный дизайн - это часть моего комплексного подхода к разработке бакендов. И опыт показывает что на горизонте от 6 месяцев работы этот подход позволяет снижать трудозатраты на разработку, в том числе за счёт снижения количества багов.
Нет, это действительно проблема и я её решаю описывая свой подход и рассказывая про него. Но опять же опыт показывает, что даже джуны за 2-3 месяца могут понять как писать такой код и потом их надо только на ревью минимально контролировать.
Значит у вас всё хорошо со скоростью и качеством разработки:)
Если вы любите хайлоад, то лучше меня знаете, что все оптимизации делаются на основе конкретных цифр, конкретного кода. Поэтому в общем случае и правда не ясно.
Конкретно в моей практике нехайлоада (там где вопрос производительности вообще возникает) производительность только улучшается, так как в 100 случаях из 100 она упирается в неэффективную работу с БД.
В оригинальных книгах речь не о сбалансированной форме дерева, а о балансе уровня абстракции основных ветвей.
Тут вам, возможно, будет интересен другой мой доклад - Рациональный подход к декомпозиции систем на модули или микросервисы. Я там как раз рассказываю о своём подходе к декомпозиции системы на минимально сцепленные модули-объекты. Правда я сейчас отказался от него:)
Да это так, но тут сильно повлияла специфика Joker - там ПК очень любит "мясцо"
Написать гайдлайн по проектированию кода трансформаций я тоже собираюсь. Пока что планирую взять за основу Stratified Design. Он хорошо расписан в книге Grokking Simplicity и есть доклад на эту тему от автора книги (сам не смотрел ещё)
К такому проекту я и на пушечный выстрел не подойду:)
Но если проект на 3M строк и 100 разработчиков попилен на 30 сервисов по 100К кода и покрыт качественными тестами - каждый сервис мигрируется также в пределах рабочего дня. Тот же проект, когда он был уже на 50К строк Котлин кода (и я его уже этом размере попилил на два сервиса) на минорные версии обновлялся за минуты.
Я в прошлом году переводил проект меньше раз в 7 (~300 Котлин файлов), но порядка на 2-3 быстрее:
Для перехода я:
очевидным образом, обновил версии
заменой по проекту поправил javax.* -> jakarta.*
Поменял com.github.tomakehurst:wiremock-jre8:2.35.0 -> com.github.tomakehurst:wiremock-jre8-standalone:2.35.0. Без этого были проблемы со спекой сервлетов, что ли
В конфиге Spring Security заменой по файлу поправил antMatchers -> requestMatchers
Руками поудалял ConstructorBinding
Больше всего времени ушло на то, чтобы реанимировать вытягивание доков к эндпоинтам в сваггере из котлин доков. Для этого пришлось руками добавить зависимость runtimeOnly("com.github.therapi:therapi-runtime-javadoc:0.15.0"), а допетрить до того, что этот джарник пропал из зависимостей (без ошибок) пришлось самостоятельно
всё. Все 100+ тестов (преимущественно пользовательских/внешних/функциональных/е2е) прошли, приложение благополучно задеплоилось на тестовый стенд. И потом обошлось без факапов в проде
Всё это я сделал часа за два грустным вечером 5-ого января:)
Возможно секрет в том, что проект у меня был на Spring Data JDBC:)
Блин, это я уже после правок поворошил блог и опять линку сломал 🤦♂️ снова поправил
О каких именно sql-е и идеях вы говорите?
Процедурное программирование. Я так называю подход, когда классы сущностей (данных) изменяемые и имеют только геттеры/сеттеры, а вся логика - в сервисах/юзкейсах
Я на Паскале писал ток лабы и курсовик в первом семестре первого курса, у меня с тех пор от собственного кода вьетнамские флешбеки, которые в моей голове намертво приколочены к BTP - короче я. ничего. не. забыл :)
На хабр пришло поколение людей, которые родились позже (де-факто) смерти Borland Turbo Pascal:)
В мокей картине мира Functional Core/Imperative Shell = структурный дизайн + неизменяемая модель данных.
Т.е. преимуществ нет, а большая для вас сложность закралась где-то в моём объяснении и/или вашем восприятии:)
В общем если вы владеете FC/IS - вы умеете и балансировку делать:)
Ещё, кстати, мысль дилетанта - а оно (архитектура) вам вообще надо?:)
Бакендеры заморачиваются, потому что бакэнды живут годами и десятилетиями. И покопавшись в говне мамонта невольно начинаешь задумываться о том, чтобы в следующий раз позаботиться о себе и сделать понятную и поддерживаемую кодовую базу.
А игры, кажется - максимально быстро запилил, зарелизал и ещё до релиза забыл про первую игру и начал пилить вторую.
Спасибо, поправил, на всякий случай продублирую:
Как я превратил легаси-проект в конфетку за полгода. Том 1
Как я превратил легаси-проект в конфетку за полгода. Том 2
Микроретро Проекта Э
Я правильно понимаю, что второй кейс с функцией назначения водителя вы тоже занесли в "примитивные программы, типа CRUD"?
В таком случае да, СД покрывает только их.
Но тогда боже упаси меня писать то, что вы называете сложными программами:)
Не
заниматься ерундойделать оверинжиниринг, как пацаны из третьего кейса:Не выдумывать, что у вас будет ввод и из консоли, и из смс и вы всё это будете разруливать в одной кодовой базе. Сделайте бэк с игрой, смс-шлюз который дёргает бэк и настольное приложение которое дёргает бэк
Переводы команд на разные языки вынесите в ресурсы. Соответственно, когда приходит команда - идёте в файлик с ресурсами и получаете каноничное имя
Возьмите РСУБД (на такую игру - точно хватит) и храните данные только там. Следите за тем, чтобы это решение не текло через АПИ кода, хранящего состояние игры
Безусловно, бывают случаи, когда есть вариативность в технологиях - тогда вводите интерфейс между кодом управления и кодом реализации io и всё.
Но вообще, см. коммент выше - в докладе и статье я держал в уме разработку бэков информационных систем. Разработка игр, как и фронта, как и АСУТП, как и встроенного софта - имеют свою специфику и не факт, что идея сбалансированной формы там применима.
Я к геймдеву вообще никакого отношения не имею, поэтому возможно фигню сморожу, но у меня такое мнение - львиная доля информации по архитектуре (включая мой доклад/статью), посвящена архитектуре (бакэндов) информационных систем. А "естество" бакенда сильно отличается от "естества" игры и поэтому эта информация плохо ложится на реалии разработки игр.
Опять же, для бэков у меня есть решение. Актуальную версию пока не расписал, но она базируется на алгоритме декомпозиции на базе эффектов (это тоже статья по мотивам доклада и этот доклад уже есть в публичном доступе на Ютубе).
Да, согласен, но когда реально делали проект я этот момент упустил (видимо и правда был с похмелья:) ), а сейчас хотелось оставить код максимально приближенным к реальному.
А за BoxesAggregate - спасибо. Вы, кажется, наконец-то мне объяснили идею "по агрегату на запрос", которую я полгода правильно понять не мог и из-за этого отвергал. Сейчас она заиграла для меня новыми красками, возможно утащу к себе.
Последние 4 года 90% моих тестов такие и есть - интеграционные, с БД в тестконтейнере и запросами по ХТТП.
И так как я работаю по ТДД и запускаю тесты по нескольку раз в минуту, мне пришлось научиться делать такие тесты более быстрыми, чем тесты на моках.
Два оснонвых секрета:
1) Не использовать @DynamicPropertySource, потому что это приводит к инвалидации контекста в кэше и запуску контекста для каждого тест-кейса
2) Использовать RAM-диск для постгреса.
Вместо DynamicPropertySource я использую такой трюк:
А чтобы посадить Postgres на рам-диск - такой:
В результате, у меня на i7-8700, 32 RAM, SSD интеграционные тесты выполняются от 14мс при тесте с моками в 163 мс:
А в проекте со скрина, я пошёл ещё радикальнее - отказался от @SpringBootTest и запускаю приложание руками, а в локальной разработке сначала ищу предзапущенную БД:
Это позволяет сэкономить ещё пару секунд на инициализации тестов, что имеет существенное значение, когда ты делаешь зелёным один тест кейс.