Эргономичный подход на JPoint
November 18, 2022
Привет!
У меня ребёнок начал стабильно ходить в детский сад!
Пока писал пост - снова заболел🤦♂️.
Как это касается вас? Во-первых, если у вас сейчас первый ребёнок до трёх лет - верьте, свет в конце туннеля есть, вместе с садом у вас начнётся новая жизнь:)
Например, у вас снова появится время смотреть видосики в интернете:)
И я вот посмотрел доклад c JPoint "Эргономичный подход TDD&DDD — гайд по разработке бизнес-логики".
Репозиторий доклада.
Это очередной материал, про который я могу сказать "посмотрите его, возьмите Диаграмму Эффектов для декомпозиции и получите ЭП". Поэтому в целом рекомендую к просмотру, но хочу предостеречь от части, которую я попробовал и откинул. А именно - Railway-oriented Programming на монадах.
При том, что сама идея мне нравится и я продолжаю ей придерживаться в императивном стиле (guard-clause с выбросом исключения, "пролетарскйий ROP"), в стиле на монадах она не подошла потому что:
- В моём коде процентов 95% обработки ошибок заключается в их маппинге на правильный статус HTTP-ответа. И Скотт Влашин, сам пишет, что не надо использовать ROP там, где нужны исключения.
- В Котлине она требует слишком много шума. Например, в Котлине буквально любой вызов может выбросить буквально любое исключение, соотвественно, каждую верхне-уровневую функцию надо оборачивать в Result.runCatching, например. И вообще разница в объёмах кода видна из примера ниже.
Вот перечень достоинств (+ мои комментарии) этого подхода из репозитория:
просто прочитать и осознать.
На мой взгляд пролетарский ROP проще читать и осознавать.
является низкоуровневой документацией
В полной мере относится и к ЭП
использует ROP - отсутствуют исключения как возможный неявный результат выполнения бизнес-логики
В Котлине исключения всегда один из возможных неявных результатов выполнения бизнес-логики. И если вся обработка исключения заключается в прервании потока исполнения, то лучше это исключение не обрабатывать, а дать специально предназначенному для этого механизму сделать свою работу
бизнес логика отделена от координирующего слоя
В полной мере относится и к ЭП
при таком подходе сам сервис выступает тупой трубой, который максимум использует паттерн canExecute/execute
В полной мере относится и к ЭП
тестировать его не обязатально - можно покрыть минимум точек верхнеуровневым интеграционным тестом контроллера
В полной мере относится и к ЭП
при тестировании можно сфокусироваться на тестировании сильной доменной модели - тестировании бизнес-логики
В полной мере относится и к ЭП
В общем пишите меньше простого года, продолжая разделять ввод-ввывод и обработку данных и реализую обработку данных (и только её) в виде чистых функций - и будет вам простое девелоперское счастье:)
|
|
(1) взял из оригинального кода.
На мой взгляд, в эргономичной версии намного проще читается, что логика операции следующая:
- запросить данные для обновления абонента
- запросить текущие данные абонента в системе
- сформировать запрос на обновление абонента
- отправить запрос обновления данных абонента
И более того, в эргономичной версии ещё и сразу подсвечен альтернативный ход сценария "обновление не требуется".
А вот свежий пример эргономичного кода c "пролетарским ROP-ом" уже из Проекта Э:
class AnalysisService(
private val diaryService: DiaryService,
private val analysisReportCalculator: AnalysisReportCalculator,
) {
fun getAnalysisReport(userId: Int, start: Instant, end: Instant): AnalysisReport {
val reportEvents = diaryService.getReportEvents(userId, start, end)
val analysisReport = analysisReportCalculator.calculateAnalysisReport(reportEvents, start, end)
return analysisReport
}
}
Реализация calculateAnalysisReport
на 300 строк в виде чистой функции:
Конкретно этот код писал не я, но я его ревьювил и поставил на нём штамп "Соответсвует принципам ЭП". Именно так должен выглядеть идеальный эргономичный код - три линейных строчки в сервисе приложения, и куча чистого кода в доменном сервисе.