Эргономичный код
832 subscribers
96 photos
3 videos
24 files
442 links
Канал о разработке поддерживаемых бакэндов - про классическую школу TDD, прагматичное функциональное программирование и архитектуру и немного DDD.

Группа: https://t.me/+hrqD87p0Oa

Канал в Max: https://max.ru/id544512614458_biz

https://azhidkov.pro
Download Telegram
Привет!

Начинаю потихоньку возвращаться с каникул и решил написать линко-мысли-в-слухо-пост.

Подвернулся недавно вот этот пост.

Если вкратце, Лукас Эдер (автор jooq-а), предлагает тянуть json сразу из базы, не заморачиваясь на маппинг в энтити, а потом в ДТО.

И этот пост прям хорошо лёг на то, что я в последние пару месяцев качнулся в сторону "облегчения" разработки. Я тут недавно в одном из своих "хорошо" спроектированных проектов добавлял одно поле и мне для этого потребовалось потрогать штук 5-6 классов, в паре-тройке пакетов. И мне это не понравилось. А с таким подходом мне надо было бы поправить ровно два места - в схеме БД и в запросе. Это та самая вторая часть SRP - одна "вещь" должна делаться в одном месте.

До этого подхода тоже, конечно, можно докопаться с разной степенью успешности:

1. Он подходит только если json сливается напрямую в ответ, без какой либо обработки в приложении.
2. Он не подходит если итоговый ответ надо собрать из нескольких источников.
3. Слой работы с БД по факту сцепляется с веб-слоем. Если в ответе надо будет поменять имя поля, то это надо будет делать в репозе.
4. Логика помещается в БД. Тут я уже не согласен. Во-первых, это вообще сейчас модно (Datomic, VoltDB, например, реализуют транзакции только через хранимки). Во-вторых, логика всё равно остаётся в "приложении" - в том же репозитории, по соседству с вызывающим кодом. Написана просто на встроенном DSL-е.
5. Зависит от технологии работы с БД и самой БД, но скорее всего прямо json из базы вернётся каким-то специфичным для базы типом. И это уже будет нарушением принципа сокрытия информации, что совсем плохо. Это можно обойти, вернув из базы или репоза строку и получив stringly typed код.

Тем не менее, мне кажется что для простого веб АПИ к БД, такой подход должен хорошо работать. Я собираюсь попробовать:)

Так же этот подход хорошо согласуется с ещё одной идеей по облегчению разработки - замена "тупых" сервисов на функциональные типы. Мне всегда резал глаз код вроде этого:
class UsersConf {
@Bean fun usersRepo() = UsersRepo()
@Bean fun usersService() = UsersService(usersRepo())
@Bean fun usersController() = UsersController(usersService())
}
class UsersController(val usersService: UsersService) {
fun findById(id: Long) = userService.findById(id)
}
class UsersService(val usersRepo: UsersRepo) {
fun findById(id: Long) = userRepo.findById(id)
}


Сейчас я хочу попробовать вместо этого писать так:
class UsersConf {
@Bean fun usersRepo() = UsersRepo()
@Bean fun usersController() = UsersController(usersRepo()::findById)
}
class UsersController(val findUserById: (Long) -> User) {
fun findById(id: Long) = findUserById(id)
}


Кода меньше, а в плане связности так даже лучше - контроллер вообще зависит только от платформы.

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

P.S.

Как вытянуть json в Postgres

#posts@ergonomic_code #ergo_persistance@ergonomic_code #ergo_approach@ergonomic_code #databases@ergonomic_code
Привет!

Утренние мысли.

В рамках концепции
> В общем буду теперь двигать эргономичный подход в сторону баланса между удобством навигации/рефакторинга/типобезопасностью и количеством кода, который надо навигировать/рефакторить/использовать.

решил попробовать чутка сдать позиции, и пустить spring data jdbc в слой домена, и спринг в целом в слой приложения/юз кейсов/интеракторов. На этом фоне, внезапно подумал, что надо поглядедь на код анкл Боба - может всё-таки без этого можно обойтись и всё придумано до нас.

А там - ни одной информационной системы. Есть Fitnesse, которая судя по всему с БД вообще не работает. Есть пачка всяких игр и симуляторов, всякая мелочёвка и всё.

Тогда я решил залезть в репоз 8th light (которы вроде бы связанной с анкл Бобом). Там тоже всякая мелочёвка.

Дальше я пошёл просто шукать по гитхабу Clean Architecture. Тут уже появились демо проектики с одной сущностью. Но всё ещё ни одного реального репоза с проектом хотя бы на 5-10 сущностей.

Писать посты и рисовать красивые картинки легко - сам так делаю:) Но “Talk is cheap. Show me the code.” (c) Torvalds.
Знаете хоть один живой пример проекта с чистой архитектурой?

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

Пойду дальше искать своего Грааля:)

#ergo_approach@ergonomic_code #ergo_persistance@ergonomic_code #clean_architecture@ergonomic_code #posts@ergonomic_code
Привет!

Пока новый пост варится - радую вас линками:)

Я нашёл базу данных мечты - EdgeDb.

Что в ней крутого:
1) Она на базе Постгреса. Т.е. ядро надёжное и если с верхним слоем будут проблемы - можно будет цепануться к Постгресу напрямую
2) У неё графово-реляионна модель данных
2.1) Она поддерживает вложенные инсёрты
2.2) Она поддерживает наследование и полиморфные связи
2.3) Она поддерживает выборку иерархичных данных
2.4) Запросы на EdgeQL композируемые (Composable)
3) Судя по гиту, пилят её уже 13 лет и сейчас пилят активно как никогда - на момент написания предыдущий мёрж в мастер был 43 минуты назад, а за последние 2 дня вмёржили 20 ПРов
4) Free как и свобода и как пиво
5) Поддерживает GraphQL из коробки
6) У неё релиз версии 1.0 в феврале

Одна проблема - нет клиентов под JVM 😂

#posts@ergonomic_code #ergo_persistance@ergonomic_code #tools@ergonomic_code #databases@ergonomic_code
Привет!

В догонку к понедельничному посту - ещё одна СУБД с похожей архитектурой.
Mnesia - Erlang-овская СУБД, которая объединяет код и данные на одной машине и требует, чтобы функции транзакций были чистыми.

Для Джавы тоже есть встроенная СУБД - MicroStream. Но у них нет транзакций из коробки, хотя они позволяют атомарно сохранять несколько объектов

Ну и кубит тоже в ту же сторону:) Там прямо сейчас есть традиционное АПИ с begin-commit-rollback, но я его выпилю, как руки дойдут.

#ergo_persistance@ergonomic_code
Привет!

У меня есть мечта. Я хочу, чтобы данные и код моих программ располагались на одной машине, лучше - в одном процессе. Когда я закончу Эргономичный подход, я вернусь к созданию технологии, которая позволит это сделать. Если кто-то другой не сделает её раньше:)

#tools@ergonomic_code #ergo_persistance@ergonomic_code #ergo_approach@ergonomic_code
🔥2
Привет!

Я молния! опечатка по фрейду

Молния! Я разочаровался в Spring Data JDBC. Вкратце, по трём причинам: отсутствие наследования, отсутствие спецификаций и отсутствие возможности автоматически подтянуть ридонли агрегат по ссылке. Потом, может, подробнее распишу.

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

#tools@ergonomic_code @spring_data_jdbc@ergonomic_code #ergo_persistance@ergonomic_code
Привет!

У меня продолжается маниакальная стадия "мне нужен идеальный ОРМ" и я накопал ещё пару любопытных линок.

Либа персистентных (в смысле переживающих процесс) коллекций для Kotlin/Java. Она, кажется начинает подгнивать, но может пригодиться в некритичных задачах

Вы говорите в ОРМах много магии? А как насчёт МетаОРМа?

Наконец, на обе штуки я уже давал ссылки, но и себе и вам напомню, что существуют примеры того, что я считаю идеальной технологией персистанса: MicroStream и Erlang Mnesia

#posts@ergonomic_code #tools@ergonomic_code #ergo_persistance@ergonomic_code
👍3🥰1
Привет!

"Хочешь насмешить бога - расскажи ему о своих планах" (с) Народ.

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

А ещё то, что на этой же недели у меня заболеет ребёнок 🤯

Поэтому план уже точно поедет как минимум на неделю.

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

#immutable_domain_model@ergonomic_code #ergo_approach@ergonomic_code #ergo_persistance@ergonomic_code
👍5🔥3🤯2🐳1
Привет!

Я не умер. И канал не умер. И я не забил ни на канал, ни на ЭП:)

Последнее время я активно допиливал Trainer Advisor. И теперь TA содержит всю базовую функциональность.

В цифрах это:
1) 14 таблиц
2) 12 страниц UI
3) 57 эндпоинтов
4) 4378 строк продового Kotlin-кода
5) 136 интеграционных + 2 мок-теста + 2 юнит теста + 1 архитектурный тест = 141 тестов, проходящих за 17 секунд
6) 1e2e Селениум тест

Это всё написано более-менее по текущим канонам ЭП и публично доступно для изучения и ознакомления.

И это самый большой известный мне проект, демонстрирующий какой-либо подход к разработке.
А если к этому добавить ещё и тот факт, что этот проект задеплоен и один живой пользователь внёс в него 40 единиц реальных доменных данных - то уже можно начать бросаться словами в духе "Демонстрация, не имеющая аналогов в мире".:)

—

Прямо сейчас в коде ТА хорошо проиллюстрированы две части ЭП:
1) Неизменяемая модель данных
2) Подход к тестированию

Так же с помощью 370 строк кода мне удалось решить две самые большие боли Spring Data JDBC:
1) Динамические критерии выборки
2) Подгрузку ссылочных данных для вывода на фронт (без дублирования структуры доменных объектов)

И сейчас решение этих задач у меня выглядит так:

// Выборка по динамическим параметрам:
findAll(pageRequest) {
Client::therapistId isEqual therapistId
Client::firstName isILikeIfNotNull clientSearchDto.firstName
Client::lastName isILikeIfNotNull clientSearchDto.lastName
Client::phoneNumber isILikeIfNotNull clientSearchDto.phoneNumber
}
// Выборка с подгрузкой ссылок
object Appointment.Fetch {
val summaryRefs = listOf(Appointment::clientRef, Appointment::typeRef)
val editableRefs = summaryRefs + Appointment::therapeuticTaskRef
}
val appointment = appointmentsRepo.findById(appointmentId, Appointment.Fetch.editableRefs)


Естественно, их можно комбинировать, но пока в проекте такой потребности нет.

Но эта работа так же помогла мне проявить пару противоречий и нерешённых проблем в ЭП:
1) С ФА есть проблема в том, что в большинстве моих проектов бизнес-логики (чистого ядра) набирается от силы процентов 10, а всё остальное - линейный круд. И кажется что это противоречит тому что ФА является одним из столпов ЭП
2) Реальный мир не всегда хорошо ложится на плоскую картину use case/workflow даже 3ей версии эргономичной структуры программ.

Что с этим делать - пока не знаю

—

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

#trainer_advisor@ergonomic_code #ergo_approach@ergonomic_code #ergo_persistance@ergonomic_code
🔥13👍4❤2🤯1
Карта канала (Так же в Max)

Добро пожаловать на канал "Эргономичный код" — канал о разработке поддерживаемых кодовых баз в общем и моём подходе к этой задаче — Эргономичном подходе.

Что такое Эргономичный подход?
По большому счёту это небольшой набор принципов, взятых в основном из классической школы TDD, функциональной архитектуры и DDD, и большой набор рецептов — моделей, методик и шаблонов —, которые позволяют команде быстро создавать кодовые базы, соответствующие этим принципам и, как следствие, лёгкие в поддержке.
Подробности на сайте Эргономичного подхода

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

"Слова дёшевы, покажи мне код!"
Trainer Advisor — некоммерческий, но реальный (~20K строк Котлин кода, 21 таблица, 2 настоящих пользователя, горки костылей, "компромиссных решений" и исторических наслоений) проект с открытым исходным кодом, разрабатываемый по Эргономичному подходу. В этот проект можно поконтрибьютить и на своём опыте прочувствовать работу с эргономичной кодовой базой.

Project Mariotte — минимальный демонстрационный пример кодовой базы, написанной по Эргономичному подходу, на примере операции бронирования номера в отеле

Есть что посмотреть или послушать?
Да, все мои публичные выступления собраны на одной странице

А почитать, кроме канала?
Да, в блоге

В блоге и канале есть подборки:
- Кейсы (в блоге, в канале #case@ergonomic_code)
- Примеры кода (в блоге)
- Эргономичный подход (в блоге, в канале #ergo_approach@ergonomic_code)
- Эргономичное тестирование (в блоге, в канале #ergo_testing@ergonomic_code)
- Функциональная архитектура (в блоге, в канале #functional_architecture@ergonomic_code)
- Эргономиный персистанс (в блоге, в канале #ergo_persistance@ergonomic_code)
- Что ещё почитать (в блоге, в канале #books@ergonomic_code, #posts@ergonomic_code, #papers@ergonomic_code)
- Что ещё посмотреть (в блоге, в канале #talks@ergonomic_code)

Какие ваши доказательства?
Исследование "Характеристики поддерживаемых кодовых баз backend-приложений"

А у меня вопрос!
Приходите в группу - там целому сообществу (более 100 крутых инженеров) можно задать любой вопрос по тематике канала - Эргономичный подход, классическая школа ТДД, ФА, ФП, в целом дизайн модели и системный дизайн

Как мне научить ИИ агента писать код по Эргономичному подходу?
Попробуйте раннюю версию моего фреймворка для ИИ-агентов.
🔥6❤4👍3
Привет!

Периодически гипотезы, лежащие в основе ЭП, рушатся о жестокую реальность промышленной разработки, что приводит к кризисам ЭП, которых уже было три штуки: раз по мотивам проекта Barcoder (решение), два (решение) и три (решение - нафик ООД, ДДД и private внутри одного репоза😀) - по мотивам #project_e.

И вот по мотивам #project_r за последние 5 дней у меня случился и благополучно разрешился очередной кризис ЭП:)

В связи с чем у меня пачка новостей.

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

Отказ от ФА
Номинально самая большая новость - я решил отказаться от ФА (которая когда-то была одним из трёх столпов ЭП).
Но по сути ничего не меняется - ЭП всё так же предполагает использование неизменяемых структур данных и максимально возможное разделение логики и ио.
Это просто признание реальности, что в ЭП, который начинался как сильно функциональный, я методично отказываюсь от отличительных черт ФА/ФП:
1. уже очень давно я отказался от монад и ROP-а в пользу защитных условий
2. затем я затащил операции в императивную оболочку
3. во многом по мотивам Проекта Р я вернул в милость выброс исключений вместо возвращения Result
4. по мотивам Проекта Р, я утащил сбалансированную форму системы с архитектурного уровня системы, на локальный уровень отдельных методов

Отказ от SDJ как дефолтной технологии работы с БД
А вот по сути самое больше изменение - я решил отказаться от SDJ в качестве дефолта для работы с БД.
Это всё ёще допустимый в рамках ЭП т.к. его всё ещё легче "продать" заказчикам и коллегам, но по умолчанию я теперь предлагаю использовать какой-то легковесный DSL - jooq или Exposed, ещё не решил что именно.

Это опять же обусловлено опытом Проекта Р - там у меня львиная доля запросов чтения рукописные, потому что SDJ не в состоянии эффективно доставать данные этого проекта, а после того, как мне пришлось ещё и два кастомных метода сохранения написать - стало понятно, что я борюсь с технологией. Этому решению так же поспособствовали пост из канала одного из участников нашей группы.
И решение перейти на UUID-ы, которое так же было обусловлено опытом разработки Проекта Р, где надо сетапать огромные графы объектов в тестах и с генераций ИДов на уровне БД это очень сложно.

Новая версия описания Эргономичной архитектуры
На мой взгляд самая крутая новость - я выложил новый подход к описанию ЭА.
По сути там ничего не изменилось, изменилась только подача.
Но эта новая подача мне настолько нравится, что я решил показать самый первый черновик.
Основная фишка - я отказался от попытки быть как все и уместить архитектуру на одну картинку и ввёл идею проекций. Их три:

1. проекция структуры данных (модель данных) - тут про агрегаты и связи между ними
2. проекция состояния (объектная модель) - тут про объекты в рантайме (порты, операции, ресурсы) и связи между ними
3. проекция структуры поведения (процедурная модель) - тут про методы и связи между ними

Плюс я туда сразу накидал кучу примеров (пока на словах😕), а так же процессы построения всех этих структур и итоговой архитектуры.

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

И возвращаясь к книге - эта статья по сути - четверть книги. Заполнить там пробелы, добавить примеры и теоретическая часть по ЭА готова. Вспоминая про бейзкамповский Hill Chart, у меня сейчас такое ощущение, что я достиг холма реализации ЭА и дальше только дело техники. Лишь бы очередной проект не зафейлил очередную гипотезу 😄

#the_book@ergonomic_code #functional_architecture@ergonomic_code #ergo_arch@ergonomic_code #ergo_persistance@ergonomic_code #spring_data_jdbc@ergonomic_code
Please open Telegram to view this post
VIEW IN TELEGRAM
1🔥6👍4❤2
Привет!

Ток что впервые за 4 года пришлось запилить руками dirty checking в SDJ...

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

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

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

5-ый кризис ЭП пролетел буквально за часик:)

Хотел написать я.
А потом упёрся в то, что есть ещё одна сущность, которую я сделал частью нескольких агрегатов одного типа...

А если учесть, что эта сущность в перспективе будет контрибьютить в инварианты этого типа агрегатов - Проект Р, кажись, не у ЭП, а у ДДД трубу зашатал...

#project_r@ergonomic_code #ddd@ergonomic_code #ergo_persistance@ergonomic_code #spring_data_jdbc@ergonomic_code
😱4👍1
Привет!

Я всё ещё "гамак-дривеню" проекцию модели данных Эргономичной архитектуры и чтобы накинуть больше входных данных на обработку во сне своему большому мозолистому мозгу я начал читать Data Modeling Essentials. И там наткнулся на новую для себя концепцию в моделировании данных - предаваемость (transferability) отношения.

Отношение является передаваемым, если допустимо менять сущность на одной из сторон.
Например, отношение "Заказ такси" -> "Водитель" является передаваемым. К сожалению.
А вот "Заказ такси" -> "Заказчик" - нет.

И там прям в книге явно написано, что непередаваемость является обазятельным свойтвом связи между сущностью и её слабой/зависимой сущностью.
И в качестве примера такой связи приводят хрестоматийный пример "Заказ" -> "Позиция заказа".

Ну и т.к., имхо, ДДД Агрегаты и сущности+слабые сущности - это одни и те же яйца только в профиль, то можно брать на вооружение эту эвристику при проектировании агрегатов - включать в агрегат только то, что невозможно передать другому агрегату. Не то чтобы супер прорыв, но эврстик много не бывает, на мой взгляд.
NB: и на всякий случай - это необходимое, но недостаточное условие - ни Заказ, ни Заказчик не являются частью агрегата друг друга.

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

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

#ergo_arch@ergonomic_code #ergo_data_model@ergonomic_code #ergo_persistance@ergonomic_code #books@ergonomic_code #ddd@ergonomic_code
❤5👍5🔥4
Привет!

Я недавно сделал Web Push уведомления в Trainer Advisor и попутно снова узнал и придумал пачку прикольных штук, о которых накатал очередной "микропост" на 30-40 минут чтения по оценке ФФ.

Ну и по традции вот вам сразу содержание, чтобы привлечь внимание мякоткой:

* Отправка пушей
* Архитектура
* Абстракция vs очевидность эффектов и разделение ввода и вывода
* И снова про уровни абстракции/стратификацию
* Пример №1: код открытия диплинка по клику
* Пример №2: фетч ресурсов с фолбэком на кэш и кастомную страницу «Сервис недоступен"
* Postgres GENERATED-колонки для ограничений целостности в JSONB
* TrainerAdvisorApis
* В Gradle-плагин GitProperties завезли поддержку git worktrees
* Отказ от Kotest Matchers в пользу shuold*-методов

#case@ergonomic_code #ergo_approach@ergonomic_code #ergo_arch@ergonomic_code #ergo_testing@ergonomic_code #trainer_advisor@ergonomic_code #ergo_persistance@ergonomic_code
🔥4❤3👍2