Fork me on GitHub
#clojure-russia
<
2015-11-03
>
kirillov05:11:35

Как же все-таки Ruby с Rails (в особенности) расслабляет да? Ни о чем думать не надо - клепай из компонентиков уже написанных гемов проекты и живи спокойно. Не подумайте ничего плохого - сам с 2009 года на Rails (и до сих пор)… просто привыкли к тому что фреймворк все сам за нас делает, вот и непривычно ручками все делать 😉

rm06:11:19

думаю, что на honeysql тоже можно напилить похожих абстракций, просто ни у кого руки не дошли все правильно обобщить

rm06:11:58

проблема рельс не в том, что там уже все есть (было бы странно), а в том, что когда приходится сражаться с фреймворком, начинается боль и ужас

kirillov07:11:12

с любым фремворком, не только с рельсами...

deeper4k07:11:22

спасибо) учу clojure понемногу)

edvorg08:11:54

Воу, почему мне пришла нотификация без mention?)

ul08:11:39

потому что слак тупо ищет подстроку

ul08:11:55

представь, сколько мне нотификаций приходит, когда сниппеты вьюшек постят )))

rm08:11:34

мне немного проще :)

niquola09:11:16

@kirillov: да клепаешь пока не надо внутрь simple_smile тут либо вначале думаешь, либо потом "не знаешь что и думать". Реализовать большую часть магии ORM на clojure не очень сложно, если понимаешь как она работает

zharinov11:11:42

Проект простой: «оперировать запросами напрямую, фу». Прошло два года: «ух ты, всё нужное вытаскивается одним эпичным запросом». Может, и не в проекте дело.

razum2um12:11:11

@kirillov: поддерживаю абсолютно, с недавнего времени начал понимать, как много вещей в рельсе мы принимаем как должное, сам хотел дать такой ответ, но это же грустно. вот приходит новичок а @nicola ему "можешь для этого парочку generic хэлперов” 😕 нужны примеры и наверное даже лучше - скаффолды таких хелперов (ну уж не нам ли с макросами и "code is data" генераторы кода то писать?)

razum2um12:11:41

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

deeper4k13:11:28

@nicola > Умное обновление пиши ручками в сервисной функции, можешь для этого парочку generic хэлперов написать (update-association task :subtasks) Такая функция будет выглядеть вот так?

(defn save [task]
  ;; make updates in database
  )
или вот так:
(defn save [id new-title new-subtasks updated-subtasks deleted-subtasks]
  ;; make updates in database
  )
Для реализации первого варианта, чтобы обновить в базе только действительно обновленные поля придется написать свою орм. Ведь нам нужно знать какой объект был до его "обновления", чтобы обновить только то, что нужно. Если же использовать второй вариант, то работа с этой функцией будет выглядит довольно таки сложно в случае обновления агрегата. Кто как поступает? Есть ли best practice как абстрагироваться от бд в фп?

rm14:11:28

я не понял проблему. Ты что хочешь сделать? Обновить сабтаск? Обновляй сабтаск, чего ты к таске пристал?

deeper4k14:11:31

тут проблема в том, что у меня сабтаск это часть агрегата task(в терминал domain driven design), то есть сабтаск это часть объекта task. Соответственно обновлять таск и сабтаски должна одна функция.

deeper4k14:11:35

Попробую упростить вопрос)

deeper4k14:11:42

Возьмем пример попроще. Есть объект task: { id: 123, title: "Buy a milk" }, бизнес правило: title не может быть пустым. Есть функция update-task которая принимает данные от пользователя:

(ns 'app.api)
(defn update-task [id title]
  (let [
    task find-task-by-id(id)
    updated-task update-title(task title)
    ]
    (save task) ;; or (save id (:title updated-task))
  )

(defn update-title [task title]
  (check-not-empty title)
  (assoc task :title title)
  )
так вот вопрос, правильно ли я написал код с точки зрения фп(достал из базы таск, "обновил" его, сохранил изменения в базу)? И второй вопрос, какой вариант функции save вы бы выбрали: (save task) или (save id title)?

rm14:11:53

по-моему, ты с точки зрения sql все неправильно сделал.

update
from tasks
where task_id = 42
set title = :title

rm14:11:05

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

rm14:11:19

в смысле, зачем тебе сначала находить?

niquola14:11:23

@deeper4k: update-task легко параметризуется именем таблицы и валидатором в общую функцию update

niquola14:11:14

и не чистые функции по определению не функциональны simple_smile

niquola14:11:14

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

deeper4k14:11:17

@nicola: > update-task легко параметризуется именем таблицы и валидатором в общую функцию update а если у меня обновление таска это не только валидация и сохранение, но и вызовы например каких-то других сервисов?

niquola14:11:05

ну тогда я скорее про save говорил simple_smile

deeper4k14:11:05

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

deeper4k14:11:17

> в смысле, зачем тебе сначала находить? потому что мне возможно понадобятся другие атрибуты таска, например для валидации

deeper4k14:11:18

@nicola: я ведь правильно понимаю, что лучше параметризовать мою чистую функцию функцией save, которая сделает грязную работу(сохранит изменения в базу)?

niquola14:11:08

Если ты хочешь абстрагироваться от базы можно и подобный Dependency Injection сделать - но много писать придеться

niquola14:11:29

Как извращаться для очистки функций каждый сам выбирает simple_smile

niquola15:11:04

Можно вместо выполнения запросов возвращать план выпролнения, эдакую IO:monad

niquola15:11:19

Я обычно не заморачиваюсь

niquola15:11:24

Пишешь generic набор грязных функций для работы с базой - порождения sql : update, create, delete, select etc

niquola15:11:00

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

niquola15:11:36

Потом в сервисных (грязных функциях) или прямо в HTTP handlers их дергаешь

deeper4k15:11:27

спасибо, я как раз пытался выяснить есть ли готовое решение для абстракции, выходит что нет

niquola15:11:40

оно сильно от проекта зависит

niquola15:11:31

базовый ORM (ala ActiveRecord || Hibernate) впринципе не сложно повторить - но будет выглядить не clojure way

deeper4k15:11:57

а почему будет выглядеть не clojure way?

niquola15:11:17

потому что много решений нужно хардкодить

niquola15:11:28

или покрывать соглашениями

niquola15:11:09

в общем виде симулировать можно так (action metadata data)

niquola15:11:26

в мета-дата ты например данными описал ассоциации

deeper4k15:11:35

ага, понимаю

niquola15:11:47

а action может прочитать эти метаданный и сделать что-нить умное

niquola15:11:27

обычно половину подобной логики можно выразить чистыми функциями

niquola15:11:56

например (let [to-create to-remove to-update] (merge-association old new))

niquola15:11:04

2/3 валидаций тоже (prismatic schema)

niquola15:11:21

у нас в парочке проектов ушло вначале по недельке на написание подточенного под проблемы ala-ORM и потом постепенное эвалюционированние в разных направлениях

niquola15:11:07

например метаданные о сущностях можно в стиле ActiveRecord доставать из метаданных базы или получать из swagger

niquola15:11:42

вобщем как раз появляеться композабельность и тонкая подгонка под проблемы решаемые приложением

deeper4k15:11:37

ясно, придется свое решение пилить)

deeper4k15:11:42

может оно и к лучшему)

niquola15:11:16

jdbc & honeysql будет хорошим фундаментом

deeper4k15:11:27

а ты используешь агрегаты в своих фп приложениях?

niquola15:11:47

в одном проекте мы например расширили honeysql, чтобы он делал жадную загрузку: {:select [:*] , :from [:user], :with [:groups :settings]} - там на пол странички кода

niquola15:11:02

аггрегаты? в понимании DDD?

niquola15:11:28

да, но с ними всегда проблема

niquola15:11:38

где граница?

niquola15:11:46

не всегда понятно

niquola15:11:24

поэтому используем, но не религиозно

deeper4k15:11:00

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

deeper4k15:11:55

и тоже используем не религиозно, на сотню сущностей пару агрегатов

niquola15:11:15

я больше склоняюсь к use-case driven - те вычленять не структурную модель, а поведеньческую

razum2um17:11:31

@nicola: может соберешь гисты ассоциаций, форм билдера и что есть в репо типа howto/cookbook?

prepor21:11:19

@deeper4k: раз уж тут заговорили про всякие ассоциации с sql то автор honeysql как-то пытался пилить https://github.com/jkk/cantata. не в плане “стоит использовать”, но”посмотреть"