All streams
Search
Write a publication
Pull to refresh
58
0.1
Алексей @jdev

Эксперт по эффективной разработке, Kotlin техлид

Send message

Ещё, кстати, прикольная идея в этом ключе и без фокуса на разделении ио и логики - Integration Operation Separation Principle и Integration Operation Data API Architecture

Да ну как сказать. React (да и Compose на андроиде) именно так и делает, насколько я знаю, и не чё "пипл юзает".

Да, полагаю, под капотом они хачат реальную модель "на месте", но сами UI компоненты и их состояние (с Redux-ом по крайней мере) с точки зраения разработчика - чистые функции и неизменяемые данные. Опять же насколько знаю, сам ни на React, ни на Compose не писал ничего.

Но вообще я возьму самоотвод - UI в целом и его рендеринг в частности - не моя специализация.

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

Ещё, пожалуй, стоит проговорить, что цепочка - ввод -> бизнес-логика -> вывод - это идеал, которого часто, но не всегда можно достичь.

А вот штука, которой можно достичь практически всегда - это сендвич - io -> logic -> io -> logic -> io.

Но большой вопрос стоит ли оно того.

Плюс по моим канонам, сам io на внутри себя и на своём уровне абстракции может содержать логику. Вот тут у меня описан пример такого случая.

Вот ещё пара ссылок по теме, как упоровшись по ФП выжимать возможный максимум разделения ио и логики:

https://blog.ploeh.dk/2017/02/02/dependency-rejection/

https://blog.ploeh.dk/2019/12/02/refactoring-registration-flow-to-functional-architecture/

Не правильно.

идеи не годятся

Идеи тащтельного проектирования АПИ и разделения ио и логики - универсальные и годятся веде.

весь код придётся выбросить в трубу

Код серверного рендеринга - естественно придётся выкинуть в трубу при разработке десктопного клиента.

Код бизнес-логики от вида клиента никак не зависит и будет работать и дальше.

Если вы захотите запустить весь сервис на дескотопе в одном процессе - выкинуть придётся код контроллеров (связывающий HTTP и бизнес-логику).

Если вы при этом захотите отказаться и от реляционки (хотя бы в виде SQLlite) в пользу файликов - так же придётся написать занового код репозиториев (связывающий бизнес-логику с постоянным хранилищем)

Завязывайте уже какашками метаться и начинайте взрослеть, если хотите, чтобы ваши слова имели хоть какой-то вес.

@nin-jin, комментарий к этому посту

:)

Там на каждый чих происходит пересоздание всего мира модели страницы.

Судя по этому сообщению, вы, путаете клиентский и серверный рендеринг:) Это такая технология древних, когда браузер отправляет HTTP-запросы, получает HTML и, действительно, ререндерит всю страницу. Вообще без JS-а.

Советую ознакомится с HTMX и идеями за этой либой - позволяет делать "good enough" UI за очень дёшево.

Нет уж извольте нормальный вариант предоставить. А я посмотрю как Вы это сделаете со своим подходом.

Технически (в нормальных языках :troll:) это можно сделать так:

val a by lazy { aRepo.findByIdOrNull(id) ?: throw NotFoundException() }
// ...

Но стоит ли оно того в данном конкретном случае - для меня большой вопрос.

PS>

Ну а если у вас Прям Нормальный Язык для ФА (Haskell) - оно всё автоматом будет лениво тянуться

Приведите пожалуйста пример как...

Чуть позже

На самом деле это довольно сложно в имеющихся условиях.

Во-первых, структура графа вызовов - это только один из трёх аспектов архитектуры, по которой я сейчас работаю.

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

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

В-третьих, в вашем примере практически нет бизнес-логики.

Но тем не менее, давайте я попробую.

Что бы я точно сделал:

  1. Собрал вычисление h в отдельный метод

  2. Сделал бы данные неизменяемыми, а сохранение обновлений явным

Возможно ещё обернул бы логические выражения проверок в методы с вменяемыми именами, особенно для b и c:

    public long fu(long id) {
        var a = aRepo.findById(id).orElseThrow();
        if(a.type() == PRIME) {
            throw new FuException();
        }
        var b = bRepo.findById(a.bId()).orElseThrow();
        if(b.isFour()) {
            throw new FuException();
        }
        var c = cService.compute(a, b);
        if(c.isDirect()){
            systemXClient.send(c);
        }
        var h = h(a, b,c);
        if (hClient(h).getStatus() == PROBLEM){
            throw new FuException();
        }
        var counterItem = hRepo.findCounterItem(h.id()).orElseThrow();
        var readyH = h.withStatus(READY);
        var readyCounter = counterItem.withStatus(READY);
        hRepo.save(readyH);
        hRepo.save(readyCounter);
        return counterItem.counterId();
    }

    public H h(A a, B b, C c) {
        var g = (a.price() + b.fine()) / c.cf();
        var f = mClient(a.id);
        return g * f;
    }

Но это всё косметика. Тут надо либо сильно глубже смотреть, либо в этом примере и правда бизнес-логика предельно простая и самоочевидная.

Публичных примеров прям кода прям с бизнес-логикой у меня ещё нет. Но есть:

  1. Пример кода оркестрации и графа вызовов операции для операции со сложной бизнес-логикой

  2. Пример построения сложной модели представления

    1. Тривиальная оркестрация

    2. Развесистая логика на чистых функциях

  3. Пример построения docx-дока

    1. Тривиальная оркестрация

    2. Относительно развесистая логика построения в виде чистой функции

      1. Тут код - прям наскоряк написанный говнокод - это был прототип фичи, которая оказалось не востребованной, поэтому рефакторить я его не стал. Но это чистый говнокод - если ему в fetchImage подсунуть Map::get - он для одних и тех же аргументов всегда будет возвращать один и тот же результат.

то где настоящие боевые примеры?

Здесь, здесь, здесь и здесь

дайте мне удобный инструмент

Вот он

Приведите пожалуйста

Чуть позже

Ответ на ваши опасения относительно Функциональной (а Симан в дополнение к Фрактальной топит и за Функциональную) архитектуру так же есть в посте:

Ответ есть в посте

Есть такая поговорка "Со своим уставом в чужой монастырь не приходят", отсюда полезность любых подходов к кодированию может быть только в разрезе PET-проектов, собственной разработки (в том числе коммерческой) или, если вы себя уже зарекомендовали как определенный специалист и вам платят за реорганизацию проекта.

Да, это правда. В моём случае так и есть - последние 4 года я либо делаю проекты с нуля, либо реорганизую их.

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

В этом случае, имхо, надо держать в голове только два стандарта - тот за который платят прямо сейчас, и тот, за который вы хотите чтобы вам платили:)

И главное - не ясна сама цель. Красота ради красоты?

Цель для меня - психологическое благополучие - у меня низкая стрессоустойчивость, поэтому в условиях горящих сроков и критов на проде мне жить некомфортно. А цель для бизнеса - минимизация суммарной стоимости владения продуктом. Структурный дизайн - это часть моего комплексного подхода к разработке бакендов. И опыт показывает что на горизонте от 6 месяцев работы этот подход позволяет снижать трудозатраты на разработку, в том числе за счёт снижения количества багов.

А вы уверены что найдутся специалисты могущие поддерживать такой стиль кодирования чтобы у вас сложилась команда?

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

Вот придёте вы ко мне в команду - будьте уверены что я вам не разрешу так писать код и не важно насколько это правильно и согласуется ли с вашим внутренним миром.

Значит у вас всё хорошо со скоростью и качеством разработки:)

Так же не ясно как отразится ваш подход на производительности

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

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

Однако он упоминает термин «сбалансированность», который, как кажется, несколько притянут за уши.

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

The Practical Guide to Structured Design, с. 182
The Practical Guide to Structured Design, с. 182

Перевод:
Хилая афферентная или эфферентная часть структурной схемы часто является признаком дисбаланса. Но я должен подчеркнуть, что баланс - это не визуальная характеристика. Система сбалансирована тогда и только тогда, когда она имеет дело с логическими данными, находящимися на ее вершине. Сбалансированные системы могут выглядеть — и часто выглядят — довольно асимметрично. Форма структурной схемы определяется задачей, а не эстетическими соображениями.

Так что мы, в итоге, имеем? – Множество обработчиков событий! Поэтому, при оптимизации алгоритмов, нужно вести речь о взаимоотношениях этих обработчиков между собой. Думаю, здесь и кроется вся сложность алгоритмов. Именно, в наличии избыточных связей между этими обработчиками либо объектами, к которым они имеют доступ. Иначе говоря, состояние одного объекта может изменяться разными способами, что нарушает принцип единственной ответственности.

Тут вам, возможно, будет интересен другой мой доклад - Рациональный подход к декомпозиции систем на модули или микросервисы. Я там как раз рассказываю о своём подходе к декомпозиции системы на минимально сцепленные модули-объекты. Правда я сейчас отказался от него:)

Что касается структурных схем автора, то наиболее важные идеи, на мой взгляд, демонстрируются не специфическим кодом, а, именно, принципами.

Да это так, но тут сильно повлияла специфика Joker - там ПК очень любит "мясцо"

Вот здесь и «зарыта собака». И вы, и я хотели бы больше «пояснений» по этим моментам. Но, единственное, что я понял из схем автора, так это то, что надо избегать, по максимуму, горизонтальных структурных связей за счет объединения соответствующих блоков кода.

Написать гайдлайн по проектированию кода трансформаций я тоже собираюсь. Пока что планирую взять за основу Stratified Design. Он хорошо расписан в книге Grokking Simplicity и есть доклад на эту тему от автора книги (сам не смотрел ещё)

К такому проекту я и на пушечный выстрел не подойду:)

Но если проект на 3M строк и 100 разработчиков попилен на 30 сервисов по 100К кода и покрыт качественными тестами - каждый сервис мигрируется также в пределах рабочего дня. Тот же проект, когда он был уже на 50К строк Котлин кода (и я его уже этом размере попилил на два сервиса) на минорные версии обновлялся за минуты.

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

Я в прошлом году переводил проект меньше раз в 7 (~300 Котлин файлов), но порядка на 2-3 быстрее:

Для перехода я:

  1. очевидным образом, обновил версии

  2. заменой по проекту поправил javax.* -> jakarta.*

  3. Поменял com.github.tomakehurst:wiremock-jre8:2.35.0 -> com.github.tomakehurst:wiremock-jre8-standalone:2.35.0. Без этого были проблемы со спекой сервлетов, что ли

  4. В конфиге Spring Security заменой по файлу поправил antMatchers -> requestMatchers

  5. Руками поудалял ConstructorBinding

  6. Больше всего времени ушло на то, чтобы реанимировать вытягивание доков к эндпоинтам в сваггере из котлин доков. Для этого пришлось руками добавить зависимость runtimeOnly("com.github.therapi:therapi-runtime-javadoc:0.15.0"), а допетрить до того, что этот джарник пропал из зависимостей (без ошибок) пришлось самостоятельно

  7. всё. Все 100+ тестов (преимущественно пользовательских/внешних/функциональных/е2е) прошли, приложение благополучно задеплоилось на тестовый стенд. И потом обошлось без факапов в проде

Всё это я сделал часа за два грустным вечером 5-ого января:)

Возможно секрет в том, что проект у меня был на Spring Data JDBC:)

3 не открывается

Блин, это я уже после правок поворошил блог и опять линку сломал 🤦‍♂️ снова поправил

в серии не хватает примера кода на который был заменен sql. Ну и судить о каких то идеях на основе того кода который был до, ошибка.

О каких именно sql-е и идеях вы говорите?

И что такое ПП ?

Процедурное программирование. Я так называю подход, когда классы сущностей (данных) изменяемые и имеют только геттеры/сеттеры, а вся логика - в сервисах/юзкейсах

Я на Паскале писал ток лабы и курсовик в первом семестре первого курса, у меня с тех пор от собственного кода вьетнамские флешбеки, которые в моей голове намертво приколочены к BTP - короче я. ничего. не. забыл :)

На хабр пришло поколение людей, которые родились позже (де-факто) смерти Borland Turbo Pascal:)

В мокей картине мира Functional Core/Imperative Shell = структурный дизайн + неизменяемая модель данных.

Т.е. преимуществ нет, а большая для вас сложность закралась где-то в моём объяснении и/или вашем восприятии:)

В общем если вы владеете FC/IS - вы умеете и балансировку делать:)

Ещё, кстати, мысль дилетанта - а оно (архитектура) вам вообще надо?:)

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

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

Information

Rating
3,789-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