[Разработка веб-сайтов, ReactJS] ReactJs и бизнес логика в Актерах

Автор Сообщение
news_bot ®

Стаж: 6 лет 3 месяца
Сообщений: 27286

Создавать темы news_bot ® написал(а)
05-Июл-2021 06:30

Где же держать бизнес логику приложения? "Та чего уж там париться - прямо в компонентах." - скажут некоторые. И в некоторых ситуациях это правильное и удобное решение.Но что если мы все-таки хотим чего-то большего? Например, масштабируемости, несколько юзер интерфейсов, лёгкой смены дизайна. Тогда логично разделить приложение на два слоя. Слой бизнес логики и слой представления. Как написано на сайте reactjs.org, React - это библиотека для создания UI. А для модели и бизнес логики написано несколько других замечательных библиотек.Так уж получилось, что проекты, на которых я работал, использовали redux. Поэтому дальше речь пойдет про то, как построить бизнес логику в большом react/redux приложении, и чтобы потом не закрывать рукой глаза при виде того огромного количества редюсеров, экшенов и так далее и тому подобное.СлоиЧтобы не запутаться в своем коде, я привык все делить на разные слои. Вы тоже это делаете, когда стили храните в одном файле, контейнер - в другом, компонент - в третьем, константы в четвертом, переводы в пятом, стейтлесс компоненты - в шестом, а ещё редюсеры, экшены, селекторы, Что? Не храните в разных файлах? Не, ну пусть даже вы назвали это duck подходом и запихнули все в один файл, вы все равно имеете в виду что он состоит из разных слоев.Деление на слои помогает ориентироваться в коде. Вы уже не просто смотрите 125-ю строчку кода вывода таблицы X, а слой контейнера таблицы X.Деление на слои помогает заменять отдельные слои. Например, у вас есть стейтлесс компонент. Пока редакс не готов, вы можете временно подключить к стейтфулл компоненту, а потом стейтфулл компонент заменить на контейнер подключенный к редаксу.Деление на слои помогает разделить работу между разработчиками.
Итак, деление на слои - это хорошо.
Что нам даёт отдельный слой бизнес логики?Как правило когда мы выводим пользователю какою-то страницу с десятком компонентов, то все эти компоненты не сами по себе, они взаимодействуют друг с другом ради одного общего дела. Многие делают ошибку - инкапсулируют логику в отдельные соответствующие компоненты.Например, представим страницу с таблицей и фильтрацией и Вы все-таки расположили логику фильтрации в компоненте FilterComponent.И вдруг понадобилось добавить панель с кнопкой (BottomPanel) сброса фильтра в другой части UI. Часто я вижу такое решение: Оставляют FilterComponent с его состоянием и добавляют еще BottomPanel в которой дублируется тоже состояние, и потом пытаются эти два состояния синхронизировать. И первое время все работает как положено. Но постепенно появляются другие связанные компоненты и контроль над ситуацией теряется. Из такого подхода вырастает очень не тривиальный и сложный в поддержке код.Правильнее будет вынести эту логику в корневой компонент, в котором все взаимодействующие компоненты рендерятся. Тогда поток данных будет предсказуем - пропсы от родителя к дочерним компонентам, события от дочернего компонента к родителю.
Размещать бизнес логику в корневом компоненте - это хорошо.
Слой  бизнес логики без ReduxДо появления хуков общую логику компонентов выносили либо в HOC либо описывали прямо в корневом для этой логики компоненте . С появлением хуков все стало наглядней. Хуки отличный претендент для слоя бизнеса логики и переиспользования общей логики в разных компонентах. Они очень удобные и если бы не парочку нюансов, можно было бы ими ограничится.
Хуки для бизнес логики - это хорошо
ReduxПервое, чего не хватает при работе с хуками и то что даёт редакс - это общее состояние всего приложения.Общее состояние - это отдельный слой, а как мы выяснили в самом начале статьи - чем больше разных слоев, тем лучше.Редакс предоставляет инструменты для дебага общего состояния.Общее состояние позволяет передвигаться по истории, что открывает дополнительные возможности при тестировании. Можно сохранить состояние и потом восстанавливать для нескольких веток теста.Можно пролистать все виды интерфейса пользователя просто меняя состояние.
Общее состояние приложения в одном месте - это хорошо.
Thunk, Saga и АктерыТ.к. у нас redux, то часть логики приложения попала в редюсеры. Но с редюсерами одна проблема - они синхронные. Нельзя в редюсере сделать запрос на сервер и результат вернуть в редакс. Чтобы добавить асинхронности, разработали несколько расширений для редакса используя специальному механизм - middleware. Этот механизм позволяет выполнить асинхронные действия вместо/до/после реального диспатча экшена.Я рассматривал два варианта Thunk и Saga и выбрал Thunk. Saga отпала по простой причине - мне не нравятся генераторы. Но принципиально Thunk и Saga ничем не отличаются. Они оба добавляют в поток данных асинхронные действия, при этом поток данных так и остается однонаправленным:
  • Пользователь вызвал событие в UI.
  • Выполнился редюсер (изменился стейт) либо вызвался экшен thunk или Saga.
  • Экшен  thunk или Saga сделал асинхронные действия и вызвал один или несколько экшенов редакса что ведет обратно к пункту 1.
Но есть кардинально другой подход - Актеры. Актеры подписываются на изменение состояния и делают асинхронные действия в ответ на изменения какого-то значения в состоянии. Поток данных становится таким:
  • Пользователь вызвал событие в UI
  • Вызвался экшен redux и поменялось состояние стора.
  • Актер подписанный на изменение этого состояния сделал асинхронные действия и вызвал один или несколько экшенов редакса.
На первый взгляд бросается в глаза недостаток актеров в том, что перед асинхронным действием надо задиспатчить дополнительный экшен и изменить состояние стора. Иначе актер не поймет что надо делать. В thunk ничего не мешает сразу начать асинхронные действия.Но те кто делал реальный проект на thunk, а не просто смотрел примеры в документации, знают, что нельзя просто так начать асинхронное действие. Пользователю надо сообщить что программа начала что то делать. И как правило в экшен thunk-а добавляют dispatch({type: ‘START’}) перед асинхронным действием и dispatch({type: ‘END’}) после.Таким образом с точки зрения частоты смены состояния между мидлварой и актером нет никакой разницы.
Как ни крути, при появлении асинхронности будет как минимум два изменения состояния:1. Чтобы сообщить что что-то начало грузится/обрабатываться2. Непосредственно сохранение результата в редакс
Бизнес логика в Redux и ThunkТут все просто. Синхронную логику мы заносим в Redux, асинхронную - в Thunk.Важно отметить, основное что делала бизнес логика в thunk actions - это сравнение предыдущего состояния с новым и в зависимости от результата вызывала другие action.Что не так с Thunk и Saga?Я ничего не имею против Thunk и Saga. Но любая дополнительная библиотека увеличивает базу знаний исходного кода. Для каждой библиотеки каждому разработчику нужно изучить документацию, почитать лучшие практики и получить опыт использования.Поэтому мой минималистичный дух считает что чем меньше подходов используется в приложении тем лучше. 
Чем меньше подходов используется при разработке тем лучше. 
Разработка актеровПризнаюсь, сначала я пошел по сложному пути. Я начал выдумывать свои подходы к организации бизнес логики. Начал выдумывать некие модули-актеры чтобы в них хранить бизнесс логику. Немаловажное значение имел мой опыт с организаци бизнес логики в thunk.Я подписался на состояние редакса, При изменении состояния, я проверял что поменялось и вызывал dispatch(action).Основной проблемой было придумать как сделать композицию из нескольких актеров и чтобы одни актеры можно было вставлять в другие, и еще передавать параметры от одного актера другому.И когда я решил вынести логику сравнения предыдущего состояния с новым в отдельные хелперные функции, я начал подозревать, что я делаю что-то очень знакомое, что-то, что уже есть готовое, что уже установлено у меня в npm модулях.Я внезапно пришел к тому что уже есть в библиотеке React. Мои модули-актеры - это то что в реакте называется компонентом. Одни компоненты могут вставлятся в другие по условию - это то что мне нужно было для актеров. Есть передача пропсов от одного компонента в другой, есть хуки которые в момент изменения состояния могут задиспатчить экшен в редакс. Все есть.ПризракЕдинственное что мне не нравилось - это определения. Я пытался вынести бизнес логику из react-компонентов, так как это слой UI, а в результате в проекте у меня опять могли получиться везде компоненты: и UI на react-компонентах и актеры на react-компонентах.Мне хотелось четко разделить эти слои, поэтому я сделал две функции:
  • ghost - аналог createElement
  • ghosts - аналог Fragment
Да, я прям вот так просто присвоил:
export const ghost = createElement;
export const ghosts = createElement.bind(null, Fragment, null);
В переводе с английского означает “призрак”. Мне показалось подходящим названием для противопоставления видимым реакт компонентам.С одной стороны - это отдельное понятие для слоя бизнес логики, не связанное с UI, а с другой стороны включает в себя весь опыт реакта.Вот так выглядит использование:
const AppActor = ({ param1, param2, showModalX }) => ghosts(
   ghost(MenuActor, { param1 }),
   ghost(PagesActor, { param2 }),
   showModalX && ghost(ModalXActor)
)
Никакого jsx и можно спокойно пользоваться привычными хуками.
const HomePageActor = () => {
    const dispatch = useDispatch()
    useEffect(() => {
      const interval = setInterval(() => {
          dispatch({type: 'COUNTER_PLUS'})
      }, 1000)
      return () => clearInterval(interval)
    })
    // you should explicitly point that this ghost hasn't child ghosts
    return null
}
Естественно, я создал себе npm модуль react-ghost с этими двумя строчками и уже применил его на нескольких проектах. Результат меня радует, мне получилось с минимальным усложнением провести четкую границу между бизнесс логикой и UI. Но людям в команде все же приходится объяснять что это, зачем это нужно и что вместо thunk достаточно использовать useEffect.Собственно, поэтому и захотелось поделиться этим подходом и узнать ваше мнение.
===========
Источник:
habr.com
===========

Похожие новости: Теги для поиска: #_razrabotka_vebsajtov (Разработка веб-сайтов), #_reactjs, #_react, #_react.js, #_reactjs, #_biznes_logika (бизнес логика), #_aktery (актеры), #_thunk, #_saga, #_razrabotka_vebsajtov (
Разработка веб-сайтов
)
, #_reactjs
Профиль  ЛС 
Показать сообщения:     

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

Текущее время: 10-Май 02:21
Часовой пояс: UTC + 5