Я много читал о том, как вы, ребята, организовываете свою бизнес-логику, и очевидно, что представление состоит в том, что нет неправильной реализации, если она отделена от других уровней в вашем приложении.
Мой вопрос касается физической реализации вашего слоя, а не концептуальной. Как вы предпочитаете на самом деле реализовать структуру вашего уровня бизнес-логики?
У меня, как правило, есть папка «services», в которой хранятся персистентность и классы обслуживания запросов для каждого из модулей / отделов приложения.
Каковы ваши предпочтения относительно структуры папок бизнес-уровня, поэтому, если вы просматриваете ее в обозревателе решений, какие папки и подпапки вы предпочитаете / предпочитаете создавать?
Я спрашиваю, что вы предпочитаете пометить ваши папки как. Я называю папки моего модуля «сервисами», я также видел их с пометкой «EntityHelpers».
user5149542 14 окт ’18 в 12:57 2018-10-14 12:57
2018-10-14 12:57
2 ответа
Вы задаете фундаментальный вопрос архитектуры: «У меня много логики. как мне ее структурировать?» Трудно ответить на такой общий вопрос вкратце, так как многочисленные книги написали о различных аспектах этой проблемы.
Postgres-вторник №10. Бизнес-логика в БД: за и против. Олег Правдин, CTO LinguaLeo
Фундаментальные принципы проектирования: разделение на слои, разделение, разделение задач, одиночная ответственность, высокая когезия, низкая связь и т. Д. Должны применяться на всех уровнях архитектуры, а не только на верхнем уровне.
user3036123 14 окт ’18 в 13:17 2018-10-14 13:17
2018-10-14 13:17
«Папка» не будет слишком простой для организации вашего решения. Хотя, если вы работаете с относительно небольшой проблемой, этого должно быть достаточно. Думая о наслоении и сохраняя свою бизнес-логику жесткой и последовательной, я бы посоветовал вам прочитать больше об DDD от Эрика Эванса.
Вы должны создать свою бизнес-логику на своем доменном уровне, отделив ее от представления (не обязательно веб), логики приложения и инфраструктуры.
Посмотрите на пример Microsoft, он отражает суть DDD. Есть также книга DDD Vaughn Vernon, в которой дается практический подход к пониманию ее использования.
Источник: stackru.com
Как вы храните бизнес логику
Современные базы данных не только хранят эти самые данные, но и обрабатывают их с помощью пользовательских функций, хранимых процедур, триггеров. После знакомства с этими инструментами у разработчика возникает мысль перенести часть бизнес-логики из кода приложения в базу. Сторонники такого подхода утверждают, что это повышает быстродействие, так как для обработки данных не требуется передавать их с сервера БД в приложение.
Я расскажу, почему в промышленном программировании так делать не надо.
Масштабирование производительности. У приложений и базы данных различные возможности масштабирования при росте нагрузки на систему. Для масштабирования обычных приложений на современных фреймворках достаточно запустить несколько копий на разных серверах. А база остается одна. Чтобы снизить нагрузку на чтение, применяют репликацию master-slave.
Что такое бизнес-логика и как ее изолировать
Снизить нагрузку на запись не так просто. Но, возможно, к этим методам не придется прибегать, если не создавать лишнюю нагрузку на базу данных и держать бизнес-логику в приложении.
Масштабирование разработки. Когда вы разрабатываете систему сами, вы используете любые инструменты на свое усмотрение. Когда вы нанимаете разработчиков, лучше применять стандартные общепринятые инструменты. И баги с боевой системы проще воспроизводить локально, когда бизнес-логика располагается только в коде.
Автоматизация разработки. БД как хранилище данных — стандартная практика. Для упрощения разработки в такой парадигме есть развитые инструменты — библиотеки ORM, которые умеют, в частности, автоматически генерировать скрипты миграции структуры БД. Изменение бизнес-логики в коде облегчается средствами среды разработки: автодополнением, автоматическим рефакторингом. Пользовательские функции в БД придется изменять полностью вручную.
Версионирование. Все разработчики научились работать с системами контроля версий кода. Git — стандарт де-факто, используется и для разработки, и для выкладки на сервер. Выкладка кода для каждого серверного языка — понятная и отработанная процедура. Одновременное версионирование структуры данных в базе, хранимых процедур и скриптов миграции как минимум вызывает вопросы.
Тестирование. Бизнес-логика в коде покрывается стандартными юнит-тестами и функциональными тестами. Написать юнит-тест на функцию в базе данных если и не невозможно, то весьма сложно.
Отладка и профилирование. Для отладки и мониторинга серверных приложений существуют инструменты: логирование, отладчики, профилировщики, мониторинг. Искать баги в триггерах и хранимых процедурах вам придется вслепую.
Я помню, как на прошлой работе в CityAds в проекте была одна InnoDB-табличка на несколько сотен записей. Она пополнялась из триггера при изменении записей в другой таблице. Но иногда вставки по непонятным причинам не происходило.
Я долго не мог докопаться до настоящей причины, пока не сделал добавление записей из кода, а не из триггера. Оказалось, что вставка иногда не срабатывает из-за ошибки типа unique constraint violation на автоинкрементном первичном ключе. Стандартный механизм исключений и логирование помогли отследить эту ошибку.
Админы подтвердили, что это известная проблема в MySQL 5.6, но быстро перевести production на 5.7 они не могли. Пришлось переключить тип таблицы с InnoDB на MyISAM. Проблема исчезла.
Не храните бизнес-логику в базе данных.
Источник: parpalak.com
ReactJs + Redux: бизнес логика в Акторах
«Та чего уж там париться — прямо в компонентах.» — скажут некоторые. И в некоторых ситуациях это правильное и удобное решение.
Но что если мы все-таки хотим чего-то большего? Например, масштабируемости, несколько юзер интерфейсов, лёгкой смены дизайна. Тогда логично разделить приложение на два слоя. Слой бизнес логики и слой представления.
Как написано на сайте 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() перед асинхронным действием и dispatch() после.
Таким образом с точки зрения частоты смены состояния между мидлварой и актором нет никакой разницы.
Как ни крути, при появлении асинхронности будет как минимум два изменения состояния:
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() >, 1000) return () => clearInterval(interval) >) // you should explicitly point that this ghost hasn’t child ghosts return null >
Естественно, я создал себе npm модуль react-ghost с этими двумя строчками и уже применил его на нескольких проектах. Результат меня радует, мне получилось с минимальным усложнением провести четкую границу между бизнесс логикой и UI. Но людям в команде все же приходится объяснять что это, зачем это нужно и что вместо thunk достаточно использовать useEffect.
Собственно, поэтому и захотелось поделиться этим подходом и узнать ваше мнение.
- Разработка веб-сайтов
- ReactJS
Источник: habr.com