Здравствуйте, es3000, Вы писали:
E>Попробую объяснить подробнее. E>Есть сложный бизнес-сценарий, который читает из БД данные, преобразует их, записывает в преобразованном виде во внешний файл, сохраняет в БД новые сформированные данные. E>В этом сценарии много ответвлений, условий и "развилок", которые зависят от конкретного состояния данных, от результата выполнения действий с БД и файловой системой. E>При возникновении этих развилок нужны дополнительные данные, которые надо запросить у пользователя. E>И выполнение сценария продолжается в зависимости от ответа пользователя.
E>Каким образом запросить данные у пользователя так, чтобы не зависеть от слоя UI? E>Ведь слой Бизнес-логики не должен зависеть от UI.
E>Очень упрощенный пример. E>Мы в редакторе сохраняем файл, указываем имя. E>Бизнес-логика при попытке сохранения файла обнаруживает, что такой файл уже существует. E>Дальше она задает вопрос пользователю: "Что дальше делать?". Обычно предлагаются следующие варианты "Заменить, Сохранить под другим именем, Отменить". E>Однако, таких вариантов может быть больше и они определяются возможностями бизнес-слоя. E>Например, более "продвинутый" бизнес-слой мог бы предложить такие дополнительные варианты действий: "Дописать в существующий файл", "Сохранить под указанным именем переименовав существующий файл" и т. д.
Навскидку, не зная деталей, можно предложить паттерн Strategy. Аналог того, как если бы в алгоритм сортировки нужно было передать частный способ сравнения элементов.
S>>>>Я пытаюсь выяснить, чем лучше архитектура, предложенная Мартином? Но конкретно, а не просто "удобнее". Удобнее чем что? Удобнее чем? Для какого типа приложений?
E>>>Ну какая разница какая архитектура лучше или хуже? S>>Если разницы нет, бери архитектуру God Project (по аналогии с God Object) и не парься над слоями.
E>Ты сам прекрасно понимаешь, почему так не следует делать.
А так же знаю, почему так следует делать. Все зависит от обстоятельств, требований. Нет категорических ответов на все случаи жизни.
Здравствуйте, es3000, Вы писали:
S>>Конкретное решение чего именно? Отправки сообщения?
E>Напоминаю: >>> Вообще обычно бизнес-слой всем и управляет. Пример: пришла почта, нужно показать уведомление в UI. Кто инициатор? Логично что бизнес-слой.
E>Решение отображения уведомления в UI при поступлении почты. E>Мне интересно как это правильно сделать по твоему мнению. E>Какие действия при этом выполняются и какими объектами\слоями?
Это был пример ситуации, когда БЛ управляет. Нельзя просто по примеру выкатить правильное решение. Требуется зафиксировать требования.
Здравствуйте, es3000, Вы писали:
E>>>Просто сообщение "пришла почта"? Или команду "покажи уведомление"?
E>>>А твое мнение? И как эта разница влияет на решение?
S>>Не принципиальна, пока оба слоя разрабатываются и используются одной командой.
E>И все-таки разница есть.
В общем случае — да, есть. E>В случае сообщения, отправитель не ждет никакого ответа, его логика и дальнейшее поведение продолжается по прямолинейному алгоритму. E>А в случае команды, важен результат ее обработки, поэтому логика отправителя зависит от ответа и далее разветвляется.
Но не в примере обнаружения почтового сообщения и показывании уведомления.
Здравствуйте, es3000, Вы писали:
E>>>Как теория категорий и монады могут помочь в озвученном вопросе? S>>Они позволяют представить источник событий монадой и связать их bind-ом с нужным кодом.
E>Можно пояснить на примере для ООП?
нет
Здравствуйте, es3000, Вы писали:
S>>Хорошо, вот мой наводящий вопрос в форме утверждения: S>>Если слои отправляют команды не друг другу, а кому-то еще, то тогда они не будут связаны друг с другом.
E>Ты имеешь ввиду что-то типа шаблона "Медиатор"? E>Про него выше говорили.
Что-то вроде.
Здравствуйте, es3000, Вы писали:
E>Бывает ли такое, что бизнес-слой должен управлять поведением (логикой) другого слоя приложения?
нет, бизнес-слой ни в коем случае не управляет другими слоями, он существует сам по себе и содержит чистую бизнес-логику
Другие слои от него зависимы, например DAL, который получает и возвращает бизнес обьекты
E>Очень упрощенный пример. E>Мы в редакторе сохраняем файл, указываем имя. E>Бизнес-логика при попытке сохранения файла обнаруживает, что такой файл уже существует. E>Дальше она задает вопрос пользователю: "Что дальше делать?". Обычно предлагаются следующие варианты "Заменить, Сохранить под другим именем, Отменить".
У бизнес логики будет метод File.Save(), сервис при попытке сохранить файл получит исключение NameAlreadyExists, если для дальнейшего процесса требуется вмешательство пользователя, то сервис выведет ему опции и на основе действий пользователя установит какие-нибудь флаги в бизнес-обьекте после чего опять вызовет File.Save()
>> Вообще обычно бизнес-слой всем и управляет. Пример: пришла почта, нужно показать уведомление в UI. Кто инициатор? Логично что бизнес-слой.
E>>Решение отображения уведомления в UI при поступлении почты. E>>Мне интересно как это правильно сделать по твоему мнению. E>>Какие действия при этом выполняются и какими объектами\слоями?
S>Это был пример ситуации, когда БЛ управляет. Нельзя просто по примеру выкатить правильное решение. Требуется зафиксировать требования.
Давай зафиксируем.
Каких еще данных не хватает, чтобы зафиксировать требования?
S>Но не в примере обнаружения почтового сообщения и показывании уведомления.
Да, в данном примере достаточно отправить сообщение о событии, никакой команды не нужно.
Этот пример ты предложил, как пример управления со стороны Бизнес-слоя пользовательским интерфейсов.
Значит, неудачный пример. Тут нет управления.
E>>>>Как теория категорий и монады могут помочь в озвученном вопросе? S>>>Они позволяют представить источник событий монадой и связать их bind-ом с нужным кодом.
E>>Можно пояснить на примере для ООП? S>нет
Такой ответ ожидался
А зачем ты тогда упоминал про категории и монады?
S>нет, бизнес-слой ни в коем случае не управляет другими слоями, он существует сам по себе и содержит чистую бизнес-логику S>Другие слои от него зависимы, например DAL, который получает и возвращает бизнес обьекты
Мнения разделились. Выше писали про то, что именно "Бизнес-слой всем и управляет".
E>>Очень упрощенный пример. E>>Мы в редакторе сохраняем файл, указываем имя. E>>Бизнес-логика при попытке сохранения файла обнаруживает, что такой файл уже существует. E>>Дальше она задает вопрос пользователю: "Что дальше делать?". Обычно предлагаются следующие варианты "Заменить, Сохранить под другим именем, Отменить".
S>У бизнес логики будет метод File.Save(), сервис при попытке сохранить файл получит исключение NameAlreadyExists, если для дальнейшего процесса требуется вмешательство пользователя, то сервис выведет ему опции и на основе действий пользователя установит какие-нибудь флаги в бизнес-обьекте после чего опять вызовет File.Save()
Ты говоришь про "сервис". Под "сервисом" ты имеешь ввиду сервисный слой?
По другому он кажется называется "Application Layer".
Это про него идет речь?
S>У бизнес логики будет метод File.Save(), сервис при попытке сохранить файл получит исключение NameAlreadyExists, если для дальнейшего процесса требуется вмешательство пользователя, то сервис выведет ему опции и на основе действий пользователя установит какие-нибудь флаги в бизнес-обьекте после чего опять вызовет File.Save()
А как сервис узнает про доступные опции бизнес-слоя?
Допустим мы доработали бизнес-слой и добавили еще один вариант действий.
Здравствуйте, es3000, Вы писали:
E>Попробую объяснить подробнее. E>Есть сложный бизнес-сценарий, который читает из БД данные, преобразует их, записывает в преобразованном виде во внешний файл, сохраняет в БД новые сформированные данные. E>В этом сценарии много ответвлений, условий и "развилок", которые зависят от конкретного состояния данных, от результата выполнения действий с БД и файловой системой. E>При возникновении этих развилок нужны дополнительные данные, которые надо запросить у пользователя. E>И выполнение сценария продолжается в зависимости от ответа пользователя.
E>Каким образом запросить данные у пользователя так, чтобы не зависеть от слоя UI? E>Ведь слой Бизнес-логики не должен зависеть от UI.
E>Очень упрощенный пример. E>Мы в редакторе сохраняем файл, указываем имя. E>Бизнес-логика при попытке сохранения файла обнаруживает, что такой файл уже существует. E>Дальше она задает вопрос пользователю: "Что дальше делать?". Обычно предлагаются следующие варианты "Заменить, Сохранить под другим именем, Отменить". E>Однако, таких вариантов может быть больше и они определяются возможностями бизнес-слоя. E>Например, более "продвинутый" бизнес-слой мог бы предложить такие дополнительные варианты действий: "Дописать в существующий файл", "Сохранить под указанным именем переименовав существующий файл" и т. д.
Даже ваш упрощённый пример подходит для того, чтобы иллюстрировать сложности проектирования. Уже в описании сценария вы перемешали концепты из областей UI, технических особенностей, и бизнес-логики.
В таком виде никакого деления ни на какие слои не получится. Любые попытки изобрести слои приведут просто к раздуванию объёма кода без улучшения каких-либо характеристик.
Вся слоистость делается не для того, чтобы дедушка Мартин порадовался за вас, а для достижения конкретных, прикладных целей.
Вот вы сделаете слой "хранения данных", который типа абстрагирует БД и файловую систему, слой UI, который абстрагирует взаимодействие с пользователем, и собствено "слой" бизнес-логики, который всё это оркестрирует.
То есть оперирует абстрактными интерфейсами с методами типа SaveFile() или RequestFileNameFromUser(). Ну, и ляжете спать довольным.
А наутро придёт менеджер и скажет "ок, теперь давай перенесём бизнес-логику в облако — она будет крутиться в ажуре, на сервер фарме".
Оппа! А нету никакого UI, в котором посреди вашего "сложного бизнес-сценария" можно показать модальный диалог. И пользователя никакого нет, всё общение с серверным кодом — через REST API.
И файлухи тоже нет — она далеко, у пользователя локально; и понятие "преобразование файла" имеет совсем другой вид.
Что осталось от ваших слоёв? Нихрена не осталось — проектирование было проведено неверно. Всё выкидываем в помойку и начинаем заново.
Нет, это, конечно, необязательно. Можно честно сказать себе "нет, такие изменения выходят за рамки нашей архитектуры".
Но тогда тоже должно быть какое-то объяснение тому, как вы режете композит на элементы и слои. В этой области плотность культов карго значительно выше, чем обычно — молодёжь, начитавшись Фаулера и прочих авторитетов, начинает лепить архитектуру ради архитектуры. При этом она оптимизируется под ситуации, которых в жизни не произойдёт никогда.
Оптимизируйтесь под то, что происходит чаще.
Обычно быстрее всего меняется именно бизнес-логика; все эти "сложные сценарии" переделываются ежеквартально, и чем они сложнее — тем чаще. В подавляющем большинстве случаев эти изменения требуют сквозных изменений по всем слоям, уменьшая их ценность до нуля. То есть появилось внезапно требование "для переводов из иностранных банков в местные на сумму больше 1000 USD надо приложить документ "подтверждение источника доходов".
Ок, поехали — в форму "заявки на перевод" добавляется UI по прикладыванию файла; в базе данных в таблицу "заявки на перевод" добавляется соответствующее поле; в таблицу "конфигурация" добавляется строка ("максимальная сумма перевода без подтверждения источника", "USD",999.99), в бизнес-метод добавляем параметр "подтверждение источника", в бизнес-логику дописывается проверка бизнес-правила, поехали, коммит.
Тестируем — дорабатываем: надо подпилить UI, чтобы он не давал отправить заявку на перевод. Ок, добавляем бизнес-метод "максимальную сумму перевода без подтверждения источника", вызываем его из формы, прячем файл-селектор либо делаем его обязательным в зависимости от результата этого метода и значения поля "сумма перевода". Поехали, коммит.
При внимательном изучении таких вот ситуаций возникает желание распилить систему совсем не по границам "UI, BLL, Storage". Потому что от такой "изоляции" нет никакой пользы.
Она бы помогла, если бы мы, к примеру, хотели заменить одну UI-библиотеку на другую, такую же. Такую же — это значит номенклатура элементов в ней такая же: есть понятия "поле ввода", "модальный диалог", "оповещение", и так далее.
В реальных приложениях смена реализации GUI происходит от 0 до 2х раз за время жизни проекта; причём с большим перекосом в сторону нуля.
Ещё такое деление здорово помогает при смене одной СУБД на другую, такую же. Такую же — это значит, что функциональность примерно совпадает: версионность/блокировочность, умение в CTE, поддержка генераторов / автоинкрементов, поддержка всяких оконных функций, поддержка динамического SQL. Лучше всего, если диалекты будут отличаться мелочами синтаксиса, типа имена переменных с @ или с $.
В реальности большинство проектов подобного типа честно говорят: "да, мы работаем на любой СУБД, но рекомендуем постгрю (оракл, mssql), т.к. на других мы тестируем редко и не всё, и там в лучшем случае будет хуже производительность, а скорее всего не будет работать какая-то экзотика", а переезд с одной базы на другую стоит дороже пожара, несмотря на изобилие репозиториев, CRUD-хранимок, и прочих талисманов.
Поэтому надо чётко понимать, чего вы хотите добиться, какие у вас для этого есть ресурсы, и какие инструменты могут вам помочь.
Помимо самих по себе требований вроде "бизнес-логика может захотеть сохранить локальный файл" важно понимать и то, каким образом требования могут изменится в будущем.
Может быть, запись файла имеет смысл вынести за пределы бизнес-сценария. То есть у вас есть там сценарий, скажем, "проведение согласования проектно-сметной документации", по ходу которого хочется сохранить подробный отчёт.
Ну так запросто можно развернуть реализацию так, что согласование — оно само по себе, а "экспорт отчёта" — он сам по себе. И дорогостоящий бизнес-процесс не будет застревать на полпути, удерживая блокировки в базе в ожидании пользователя, ушедшего на перекур. И вместе с этим перепроектированием внезапно исчезнет и нужда давать бизнес-логике управление UI.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, es3000, Вы писали:
S>>Это был пример ситуации, когда БЛ управляет. Нельзя просто по примеру выкатить правильное решение. Требуется зафиксировать требования.
E>Давай зафиксируем. E>Каких еще данных не хватает, чтобы зафиксировать требования?
Ниже ты сказал что пример неудачный. Какой смысл продолжать?
Здравствуйте, es3000, Вы писали:
S>>Но не в примере обнаружения почтового сообщения и показывании уведомления.
E>Да, в данном примере достаточно отправить сообщение о событии, никакой команды не нужно. E>Этот пример ты предложил, как пример управления со стороны Бизнес-слоя пользовательским интерфейсов. E>Значит, неудачный пример. Тут нет управления.
А что же тут тогда, если не управление?
Здравствуйте, es3000, Вы писали:
E>>>Можно пояснить на примере для ООП? S>>нет
E>Такой ответ ожидался E>А зачем ты тогда упоминал про категории и монады?
В ответ на твой вопрос, в котором не упоминалось ООП.
S>Даже ваш упрощённый пример подходит для того, чтобы иллюстрировать сложности проектирования. Уже в описании сценария вы перемешали концепты из областей UI, технических особенностей, и бизнес-логики. S>В таком виде никакого деления ни на какие слои не получится.
А как правильно написать мой упрощенный пример, чтобы в нем были разделены "концепты из областей UI, технических особенностей, и бизнес-логики"?
S>... S>...И вместе с этим перепроектированием внезапно исчезнет и нужда давать бизнес-логике управление UI.
Суть я понял: мой подход разделения на слои "в лоб" не правильный.
А какой будет правильный?
Можете предложить какой-то вариант для примера?
E>>Да, в данном примере достаточно отправить сообщение о событии, никакой команды не нужно. E>>Этот пример ты предложил, как пример управления со стороны Бизнес-слоя пользовательским интерфейсов. E>>Значит, неудачный пример. Тут нет управления. S>А что же тут тогда, если не управление?
Тут просто передача сообщения.
Приемник этого сообщения может вообще никак не отреагировать, а может отрегулировать неизвестным нам способом.
Какое же это управление?
Здравствуйте, es3000, Вы писали:
E>>>Да, в данном примере достаточно отправить сообщение о событии, никакой команды не нужно. E>>>Этот пример ты предложил, как пример управления со стороны Бизнес-слоя пользовательским интерфейсов. E>>>Значит, неудачный пример. Тут нет управления. S>>А что же тут тогда, если не управление?
E>Тут просто передача сообщения. E>Приемник этого сообщения может вообще никак не отреагировать, а может отрегулировать неизвестным нам способом. E>Какое же это управление?
Самое обычное. Передача сообщения и ожидание реакции. Бывает по-другому?