Программный код программного продукта нужно разделять на несколько слоев.
При этом получается, что Бизнес-слой — это самый внутренний слой, от которого зависят другие слои.
При этом сам Бизнес-слой получается независимым от других слоев.
Бывает ли такое, что бизнес-слой должен управлять поведением (логикой) другого слоя приложения?
Если ответ положительный и так делается, то как реализуется такое управление?
Как сделать внешний слой, о котором по сути Бизнес-логика ничего не знает, управляемым?
Здравствуйте, es3000, Вы писали:
E>Здравствуйте!
E>Программный код программного продукта нужно разделять на несколько слоев.
Есть такой шаблон — многоуровневая архитектура. При его применении да, выделяются слои. Но его же можно и не применять, потому "нужно" — под вопросом.
E>При этом получается, что Бизнес-слой — это самый внутренний слой, от которого зависят другие слои.
Как так? Что-то новое в делении на слои. E>При этом сам Бизнес-слой получается независимым от других слоев.
E>Бывает ли такое, что бизнес-слой должен управлять поведением (логикой) другого слоя приложения?
Обычно так и бывает, что BLL управляет DAL.
E>Если ответ положительный и так делается, то как реализуется такое управление? E>Как сделать внешний слой, о котором по сути Бизнес-логика ничего не знает, управляемым?
Слои для того и придумали, что бы изолировать слои верхнего уровня от нижнего, т.е. что бы нижние не знали о верхних.
Управление в таком случае осуществляется через инверсию зависимостей, т.е. через интерфейсы, которые доступны в низлежащем слое.
S>Есть такой шаблон — многоуровневая архитектура. При его применении да, выделяются слои. Но его же можно и не применять, потому "нужно" — под вопросом.
Начну с примера.
Лежит красное яблоко, помидор, огурец.
Их можно мысленно объединить в группы "фрукты", "овощи", "красные", и т. д.
Хотя лежат они вперемешку.
Также и с кодом.
Физически код может быть и не разделен на слои, то есть код написан "вперемешку".
Но логически этот код является частью какого-то слоя, в соответствии с решаемыми задачами.
Если физическое разделение кода по модулям соответствует логическому, то программа является хорошо спроектированной.
И от этого мы получаем много плюсов.
А если физическое разделение кода не соответствует логическому, то программа является плохо спроектированной.
И от этого мы имеем много минусов.
Так что, многоуровневую архитектуру можно и не применять.
Но (я повторяюсь) в любом случае, логически (мысленно) программный код в соответствии с решаемыми задачами можно отнести к какому-то слою.
Именно такое логическое разделение я имел ввиду, когда задавал вопрос.
E>>При этом получается, что Бизнес-слой — это самый внутренний слой, от которого зависят другие слои. S>Как так? Что-то новое в делении на слои.
А что не так?
E>>При этом сам Бизнес-слой получается независимым от других слоев. S>
Разве нет?
E>>Бывает ли такое, что бизнес-слой должен управлять поведением (логикой) другого слоя приложения? S>Обычно так и бывает, что BLL управляет DAL.
Правильнее сказать: BLL использует DAL, но не управляет.
Под управлением я имел ввиду влияние на алгоритмическое поведение.
Допустим в DAL заложен какой-то алгоритм извлечения данных из БД.
Может ли BLL влиять на этот алгоритм?
Или вообще подсунуть в DAL свой алгоритм?
S>Слои для того и придумали, что бы изолировать слои верхнего уровня от нижнего, т.е. что бы нижние не знали о верхних. S>Управление в таком случае осуществляется через инверсию зависимостей, т.е. через интерфейсы, которые доступны в низлежащем слое.
Инверсия зависимости скорее предназначена для передачи данных обратно в верхний слой.
То есть нижний слой отрабатывает по своему "заложенному" в него алгоритму, а ответ возвращает через инверсию зависимостей.
Правильно я понимаю?
Здравствуйте, es3000, Вы писали:
S>>Есть такой шаблон — многоуровневая архитектура. При его применении да, выделяются слои. Но его же можно и не применять, потому "нужно" — под вопросом.
E>Начну с примера. E>Лежит красное яблоко, помидор, огурец. E>Их можно мысленно объединить в группы "фрукты", "овощи", "красные", и т. д. E>Хотя лежат они вперемешку.
E>Также и с кодом.
Замечательно. Пусть будет "лишний", "не работает", "васин".
E>Физически код может быть и не разделен на слои, то есть код написан "вперемешку". E>Но логически этот код является частью какого-то слоя, в соответствии с решаемыми задачами.
Т.е. для каждой строчки кода найдется слой или два, в которой она решает некую задачу. А что если строчка кода решает несколько задач? Кто определяет задачи? Напечатать отчет — это задача или нет?
E>Если физическое разделение кода по модулям соответствует логическому, то программа является хорошо спроектированной.
Я привел пример. "лишний", "не работает", "васин". Разнесли по модулям с соответствующими именами. Это хорошо спроектированная программа? E>И от этого мы получаем много плюсов.
Каких, например?
E>А если физическое разделение кода не соответствует логическому, то программа является плохо спроектированной. E>И от этого мы имеем много минусов.
E>Так что, многоуровневую архитектуру можно и не применять.
E>Но (я повторяюсь) в любом случае, логически (мысленно) программный код в соответствии с решаемыми задачами можно отнести к какому-то слою. E>Именно такое логическое разделение я имел ввиду, когда задавал вопрос.
"Такое логическое" разделение мне непонятно. Т.е. я не могу оперировать конкретным слоем БЛ в некотором "таком логическом" разделении...
E>>>При этом получается, что Бизнес-слой — это самый внутренний слой, от которого зависят другие слои. S>>Как так? Что-то новое в делении на слои.
E>А что не так?
Например, найдется слой, не зависящий от БЛ. Обычно это DAL, но я не знаю, как это устроено в "таком логическом" разделении.
E>>>При этом сам Бизнес-слой получается независимым от других слоев. S>>
E>Разве нет?
S>>Обычно так и бывает, что BLL управляет DAL.
E>Правильнее сказать: BLL использует DAL, но не управляет. E>Под управлением я имел ввиду влияние на алгоритмическое поведение.
Не понимаю, что такое влияние на алгоритмическое поведение.
E>Допустим в DAL заложен какой-то алгоритм извлечения данных из БД. E>Может ли BLL влиять на этот алгоритм?
Конечно. E>Или вообще подсунуть в DAL свой алгоритм?
Элементарно.
S>>Слои для того и придумали, что бы изолировать слои верхнего уровня от нижнего, т.е. что бы нижние не знали о верхних. S>>Управление в таком случае осуществляется через инверсию зависимостей, т.е. через интерфейсы, которые доступны в низлежащем слое.
E>Инверсия зависимости скорее предназначена для передачи данных обратно в верхний слой.
Зачем тут инверсия? E>То есть нижний слой отрабатывает по своему "заложенному" в него алгоритму, а ответ возвращает через инверсию зависимостей. E>Правильно я понимаю?
Это я уже не понимаю, о чем речь.
Здравствуйте, es3000, Вы писали:
S>>Слои для того и придумали, что бы изолировать слои верхнего уровня от нижнего, т.е. что бы нижние не знали о верхних. S>>Управление в таком случае осуществляется через инверсию зависимостей, т.е. через интерфейсы, которые доступны в низлежащем слое.
E>Инверсия зависимости скорее предназначена для передачи данных обратно в верхний слой. E>То есть нижний слой отрабатывает по своему "заложенному" в него алгоритму, а ответ возвращает через инверсию зависимостей. E>Правильно я понимаю?
Инверсия зависимостей не означает инверсные зависимости. Она относится только к фазе инициализации программы, когда BLL получает экземпляры нужных ему DAL. В этом и состоит инверсия — он эти экземпляры получает извне, а не создает сам.
BLL вызывает DAL самым обыкновенным образом, через вызовы функций. Если требуется более сложное поведение, можно добавить новый метод в DAL или расширить API DAL конфигурацией или паттерном Стратегия.
Здравствуйте, es3000, Вы писали:
S>>Слои для того и придумали, что бы изолировать слои верхнего уровня от нижнего, т.е. что бы нижние не знали о верхних. S>>Управление в таком случае осуществляется через инверсию зависимостей, т.е. через интерфейсы, которые доступны в низлежащем слое.
E>Инверсия зависимости скорее предназначена для передачи данных обратно в верхний слой. E>То есть нижний слой отрабатывает по своему "заложенному" в него алгоритму, а ответ возвращает через инверсию зависимостей. E>Правильно я понимаю?
Я бы не стал весь этот подход называть "инверсией зависимости", т.к. это лишь технический прием представения абстрактных сущностей.
В более широком смысле разделение на слои выполняется введением абстракций, которыми манипулирует слой вышестоящего уровня, а реализация этих абстракций относится к нижележащим слоям.
В идеале определении абстракции должно фиксировать необходимые требования и оставлять допустимые степени свободы.
В зависимости от используемых инструментов разработки, это достигается определением контрактов, проверкой пред- и пост-условий, заданием очевидно интерпретируемых имен, написанием комментариев в коде и документации.
Например, мы делаем текстовый редактор. На уровне модели умеем хранить буковки, делать поиск и замену.
Наряду с конкретными типа FindRequest, ReplaceRequest на уровне модели может быть определен абстрактный интерфейс ISpellChecker.
Реализация конкретных проверщиков орфографии для разных языков может быть реализована за пределами модели и даже за пределами приложения — в плагине.
S>Замечательно. Пусть будет "лишний", "не работает", "васин".
Можно и так разделить.
Только какой толк от такого разделения?
S>Т.е. для каждой строчки кода найдется слой или два, в которой она решает некую задачу. А что если строчка кода решает несколько задач? Кто определяет задачи? Напечатать отчет — это задача или нет?
Задачи определяются целью программы.
Напечатать отчет — конечно это задача — если это входит в назначение программы.
S>Я привел пример. "лишний", "не работает", "васин". Разнесли по модулям с соответствующими именами. Это хорошо спроектированная программа?
Это плохо спроектированная программа.
Так как разделение на модули выполняется по критерию соответствия решаемым задачам, а не каким-то другим критериям (по автору, по полезности).
E>>И от этого мы получаем много плюсов. S>Каких, например?
Не вижу смысла заниматься плагиатом.
Пару книжек пролистайте по проектированию программ — там все написано.
S>Например, найдется слой, не зависящий от БЛ. Обычно это DAL, но я не знаю, как это устроено в "таком логическом" разделении.
Не согласен.
DAL как раз проектируется так, чтобы выдавать то, что нужно для BLL.
S>Не понимаю, что такое влияние на алгоритмическое поведение.
Алгори́тм (лат. algorithmi — от арабского имени математика Аль-Хорезми[1]) — конечная совокупность точно заданных правил решения произвольного класса задач или набор инструкций, описывающих порядок действий исполнителя для решения некоторой задачи.
Допустим, в каком то слое (пусть это будет DAL) заложен какой-то алгоритм — то есть "...набор инструкций, описывающих порядок действий для решения некоторой задачи ...".
Мы в Бизнес-слое используем этот слой и хотим изменить этот "порядок действий".
Вот это и есть влияние на алгоритмическое поведение.
S>>>Слои для того и придумали, что бы изолировать слои верхнего уровня от нижнего, т.е. что бы нижние не знали о верхних. S>>>Управление в таком случае осуществляется через инверсию зависимостей, т.е. через интерфейсы, которые доступны в низлежащем слое.
Наверно, правильнее сказать так: "Управление в таком случае осуществляется через инверсию зависимостей, т.е. через интерфейсы, которые предоставляются из низлежащего слоя".
Правильно?
scf>Инверсия зависимостей не означает инверсные зависимости. Она относится только к фазе инициализации программы, когда BLL получает экземпляры нужных ему DAL. В этом и состоит инверсия — он эти экземпляры получает извне, а не создает сам.
А кто создает эти экземпляры DAL, которые будут подсунуты в BLL?
scf>BLL вызывает DAL самым обыкновенным образом, через вызовы функций. Если требуется более сложное поведение, можно добавить новый метод в DAL или расширить API DAL конфигурацией или паттерном Стратегия.
А если используется асинхронное взаимодействие верхнего и нижнего уровней?
Верхний уровень отправил сообщение.
Как верхний уровень потом получит ответ от нижнего уровня?
Q>Например, мы делаем текстовый редактор. На уровне модели умеем хранить буковки, делать поиск и замену. Q>Наряду с конкретными типа FindRequest, ReplaceRequest на уровне модели может быть определен абстрактный интерфейс ISpellChecker. Q>Реализация конкретных проверщиков орфографии для разных языков может быть реализована за пределами модели и даже за пределами приложения — в плагине.
ISpellChecker — это же и есть инверсия зависимости?
Здравствуйте, es3000, Вы писали:
S>>Замечательно. Пусть будет "лишний", "не работает", "васин".
E>Можно и так разделить. E>Только какой толк от такого разделения?
Если отталкиваться от толка, то можно найти ответ и на вопрос, по какому принципу код делится на слои и располагается (выше/ниже). А так же, почему BLL будет видеть DAL, но не наоборот.
S>>Т.е. для каждой строчки кода найдется слой или два, в которой она решает некую задачу. А что если строчка кода решает несколько задач? Кто определяет задачи? Напечатать отчет — это задача или нет?
E>Задачи определяются целью программы. E>Напечатать отчет — конечно это задача — если это входит в назначение программы.
Что такое цель программы? Нарубить бабла?
S>>Я привел пример. "лишний", "не работает", "васин". Разнесли по модулям с соответствующими именами. Это хорошо спроектированная программа?
E>Это плохо спроектированная программа. E>Так как разделение на модули выполняется по критерию соответствия решаемым задачам, а не каким-то другим критериям (по автору, по полезности).
Так модули или слои? Это не одно и то же. В заголовке темы обозначены слои.
E>>>И от этого мы получаем много плюсов. S>>Каких, например?
E>Не вижу смысла заниматься плагиатом. E>Пару книжек пролистайте по проектированию программ — там все написано.
Читал и не пару. Просто я привык принимать решения для достижения определенных целей, а не потому, что так в книжке написано.
S>>Например, найдется слой, не зависящий от БЛ. Обычно это DAL, но я не знаю, как это устроено в "таком логическом" разделении.
E>Не согласен. E>DAL как раз проектируется так, чтобы выдавать то, что нужно для BLL.
Это противоречит чему-то?
S>>Не понимаю, что такое влияние на алгоритмическое поведение.
E>
E>Алгори́тм (лат. algorithmi — от арабского имени математика Аль-Хорезми[1]) — конечная совокупность точно заданных правил решения произвольного класса задач или набор инструкций, описывающих порядок действий исполнителя для решения некоторой задачи.
А что такое "алгоритмическое поведение"?
E>Допустим, в каком то слое (пусть это будет DAL) заложен какой-то алгоритм — то есть "...набор инструкций, описывающих порядок действий для решения некоторой задачи ...".
Допустим E>Мы в Бизнес-слое используем этот слой и хотим изменить этот "порядок действий".
допустим, бизнес-слой указывает критерий выборки данных... E>Вот это и есть влияние на алгоритмическое поведение.
Вот это и есть, выходит, ситуация, "что бизнес-слой должен управлять поведением (логикой) другого слоя приложения?"
Скажем точнее — назначение программы.
S>Так модули или слои? Это не одно и то же. В заголовке темы обозначены слои.
Да, вопрос про слои.
Но в смысле данного вопроса не вижу особой разницы: и слои зависят друг от друга, и модули зависят друг от друга.
И слой может управлять другим слоем.
И модуль может управлять другим модулем.
E>>Пару книжек пролистайте по проектированию программ — там все написано. S>Читал и не пару. Просто я привык принимать решения для достижения определенных целей, а не потому, что так в книжке написано.
Вот эти определенные цели в книжках и указаны.
Хочется, чтобы программа, кроме своего назначения, еще соответствовала и этим целям проектирования.
S>Слои для того и придумали, что бы изолировать слои верхнего уровня от нижнего, т.е. что бы нижние не знали о верхних. S>Например, найдется слой, не зависящий от БЛ. Обычно это DAL... E>DAL как раз проектируется так, чтобы выдавать то, что нужно для BLL. S>Это противоречит чему-то?
E>>Мы в Бизнес-слое используем этот слой и хотим изменить этот "порядок действий". S>допустим, бизнес-слой указывает критерий выборки данных... E>>Вот это и есть влияние на алгоритмическое поведение. S>Вот это и есть, выходит, ситуация, "что бизнес-слой должен управлять поведением (логикой) другого слоя приложения?"
То есть управлением поведения можно только через установку параметров.
Так получается?
Здравствуйте, es3000, Вы писали: E>ISpellChecker — это же и есть инверсия зависимости?
Да, как технический прием.
В более общем смысле — это абстракция, которую проектировщик выделил и наделил определенным смыслом в рамках решения задачи создания текстового редактора.
Здравствуйте, es3000, Вы писали:
S>>Так модули или слои? Это не одно и то же. В заголовке темы обозначены слои.
E>Да, вопрос про слои.
E>Но в смысле данного вопроса не вижу особой разницы: и слои зависят друг от друга, и модули зависят друг от друга. E>И слой может управлять другим слоем. E>И модуль может управлять другим модулем.
Так и с методами(функциями) тоже нет особой разницы. Функции зависят друг от друга и управляют друг другом. Может о функциях поговорим?
E>>>Пару книжек пролистайте по проектированию программ — там все написано. S>>Читал и не пару. Просто я привык принимать решения для достижения определенных целей, а не потому, что так в книжке написано.
E>Вот эти определенные цели в книжках и указаны. E>Хочется, чтобы программа, кроме своего назначения, еще соответствовала и этим целям проектирования.
Здравствуйте, es3000, Вы писали:
S>>Слои для того и придумали, что бы изолировать слои верхнего уровня от нижнего, т.е. что бы нижние не знали о верхних. S>>Например, найдется слой, не зависящий от БЛ. Обычно это DAL... E>>DAL как раз проектируется так, чтобы выдавать то, что нужно для BLL. S>>Это противоречит чему-то?
E>Да, противоречит. E>Так как внутренним считается слой BLL, а DAL по отношению к нему считается внешним. E>https://habr.com/ru/company/mobileup/blog/335382/
У Мартина зависимость BLL от DAL инвертирована, BLL не видит DAL напрямую, вместо этого обращается к Repositories. При этом BLL все еще может формировать критерии для выборки, т.е. управлять инструкциями, которые выполняет DAL.
E>Значит, DAL должен не зависеть от BLL.
У Мартина, но не вообще. E>Но как же он не зависит, если выдает данные в формате BLL?
Он выдает Entity, которое является общим для нескольких слоев. В точности как integer или string.
Здравствуйте, es3000, Вы писали:
S>>Вот это и есть, выходит, ситуация, "что бизнес-слой должен управлять поведением (логикой) другого слоя приложения?"
E>То есть управлением поведения можно только через установку параметров. E>Так получается?
Именно так и получается, если учесть что в качестве параметра можно передавать и инструкции в том числе, которые будут выполнены в том месте, куда были переданы.
S>Так и с методами(функциями) тоже нет особой разницы. Функции зависят друг от друга и управляют друг другом. Может о функциях поговорим?
S>Хорошо. Когда хочется, аргументация бесполезна.
Зачем к словам цепляться?
Смысл то понятен.
Ну скажем не "хочется", а "требуется для сохранения возможности поддержки и дальнейшего развития приложения".
И где хоть какая-то аргументация?
Ты даже тезис никакой не высказал, аргументировать пока еще нечего.
Суть та же самая: сделать код одного слоя независимым от других слоев.
Причем это даже не архитектура, а общие формулировки.
S>У Мартина зависимость BLL от DAL инвертирована, BLL не видит DAL напрямую, вместо этого обращается к Repositories. При этом BLL все еще может формировать критерии для выборки, т.е. управлять инструкциями, которые выполняет DAL.
S>У Мартина, но не вообще.
Просто подход Мартина — внес новую идею в разделение слоев.
За счет этого это разделение слоев стало еще лучше.
Ну это как постепенное развитие например бензиновых двигателей.
Ясное дело, что современные двигатели во много превосходят двигатели 50-летней давности.
Так и подход к разделению слоев.
Подход Мартина — более конкретен чем предыдущие общие формулировки.
S>>Так и с методами(функциями) тоже нет особой разницы. Функции зависят друг от друга и управляют друг другом. Может о функциях поговорим?
S>>Хорошо. Когда хочется, аргументация бесполезна.
E>Зачем к словам цепляться? E>Смысл то понятен. E>Ну скажем не "хочется", а "требуется для сохранения возможности поддержки и дальнейшего развития приложения".
Если взять довольно простое приложение и вкорячить в него правильную архитектуру, оно внезапно перестанет быть простым, поддерживать и развивать его станет сложнее. Потому, не всякому приложению это полезно.
E>И где хоть какая-то аргументация? E>Ты даже тезис никакой не высказал, аргументировать пока еще нечего.
Аргументация на что? Ты послал меня книжки полистать и найти там тезисы. Им оппонировать?
Здравствуйте, es3000, Вы писали:
E>А если используется асинхронное взаимодействие верхнего и нижнего уровней? E>Верхний уровень отправил сообщение. E>Как верхний уровень потом получит ответ от нижнего уровня?
Через очередь сообщений. Поставил в очередь задание, через некоторое время проверил статус, приспичило отменил задание.
Здравствуйте, es3000, Вы писали:
E>Здравствуйте, samius, Вы писали: S>>Да, у Мартина так. Но есть примеры других архитектур, где не так. S>>https://www.oreilly.com/library/view/software-architecture-patterns/9781491971437/ch01.html S>>https://en.wikipedia.org/wiki/Multitier_architecture#Common_layers
E>Суть та же самая: сделать код одного слоя независимым от других слоев. E>Причем это даже не архитектура, а общие формулировки.
Верно. Сначала делали независимыми функции, потом объекты, далее слои. Идея та же, масштаб растет. Но нового ничего.
S>>У Мартина зависимость BLL от DAL инвертирована, BLL не видит DAL напрямую, вместо этого обращается к Repositories. При этом BLL все еще может формировать критерии для выборки, т.е. управлять инструкциями, которые выполняет DAL.
S>>У Мартина, но не вообще.
E>Просто подход Мартина — внес новую идею в разделение слоев.
Спрятать БЛ за репозиторием и нарисовать во внешнем слое на диаграмме? E>За счет этого это разделение слоев стало еще лучше.
Можно конкретнее?
E>Ну это как постепенное развитие например бензиновых двигателей. E>Ясное дело, что современные двигатели во много превосходят двигатели 50-летней давности. E>Так и подход к разделению слоев. E>Подход Мартина — более конкретен чем предыдущие общие формулировки.
Более конкретен чем?
Если БЛ у нас управляет тем, какие Entity ему нужны, какая разница, где мы нарисовали DAL, во внешнем слое, или во внутреннем? Линейно расположили слои, или луковицей?
S>Аргументация на что? Ты послал меня книжки полистать и найти там тезисы. Им оппонировать?
Нет, не книжкам оппонировать.
А аргументировать свои тезисы.
Мы сильно в "философию" углубились.
Предлагаю "откатиться назад" и вспомнить первые вопросы:
Бывает ли такое, что бизнес-слой должен управлять поведением (логикой) другого слоя приложения?
Если ответ положительный и так делается, то как реализуется такое управление?
Как сделать внешний слой, о котором по сути Бизнес-логика ничего не знает, управляемым?
Еще раз, скажи пожалуйста, свои тезисы в ответ на эти вопросы.
Только я уточню, что речь идет не о нижележащих слоях, которыми управляют через их интерфейсы.
А речь идет об управлении вышестоящим слоем, который как бы неизвестен.
Насколько я понимаю, единственный способ общения нижестоящего слоя с вышестоящим — это через механизм событий-подписок.
Например, Слой приложения (вышестоящий) использует Бизнес-слой.
Слой приложения отправляет команды Бизнес-слою через интерфейс, а в ответ получает какие-то события.
Надо, чтобы Бизнес-слой как-то повлиял на логику Слоя приложения.
E>>А если используется асинхронное взаимодействие верхнего и нижнего уровней? E>>Верхний уровень отправил сообщение. E>>Как верхний уровень потом получит ответ от нижнего уровня? _>Через очередь сообщений. Поставил в очередь задание, через некоторое время проверил статус, приспичило отменил задание.
Если так все просто, почему через очередь сообщений не может общаться верхний уровень с нижним?
А можно ведь сделать, что все друг с другом будут общаются через очередь сообщений.
Полная независимость. Красота.
E>>Просто подход Мартина — внес новую идею в разделение слоев. S>Спрятать БЛ за репозиторием и нарисовать во внешнем слое на диаграмме?
Да.
E>>За счет этого это разделение слоев стало еще лучше. S>Можно конкретнее?
Ну раньше ломали голову: как отделить БЛ от хранения данных.
А теперь Мартин предложил удобное решение.
E>>Подход Мартина — более конкретен чем предыдущие общие формулировки. S>Более конкретен чем?
Тем, что предложил удобное архитектурное решение.
S>Если БЛ у нас управляет тем, какие Entity ему нужны, какая разница, где мы нарисовали DAL, во внешнем слое, или во внутреннем? Линейно расположили слои, или луковицей?
Не знаю какая разница. Может быть и никакой.
А что ты сказать-то хочешь?
А то всё задаешь вопросы. Ты мысль свою вырази.
E>>Как сделать внешний слой, о котором по сути Бизнес-логика ничего не знает, управляемым?
V>Через абстракции — добавить интерфейс, который внешний слой должен реализовывать. Или же генерировать события, на которые внешний слой подпишется.
Тогда получается, что оба слоя и внешний и внутренний ничем друг от друга не отличаются:
— и внутренний слой предоставляет свои интерфейсы для управления им извне
— и внешний слой предоставляет свои интерфейсы для управления им извне.
— оба слоя вызывают подписчики событий в ответ
Тут уже и нельзя сказать: какой из них внутренний, а какой внешний.
Они оба получились на одном уровне.
Здравствуйте, es3000, Вы писали:
E>Предлагаю "откатиться назад" и вспомнить первые вопросы: E>
E>Бывает ли такое, что бизнес-слой должен управлять поведением (логикой) другого слоя приложения?
E>Если ответ положительный и так делается, то как реализуется такое управление?
E>Как сделать внешний слой, о котором по сути Бизнес-логика ничего не знает, управляемым?
E>Еще раз, скажи пожалуйста, свои тезисы в ответ на эти вопросы.
1. Да, бывает. Вообще обычно бизнес-слой всем и управляет. Пример: пришла почта, нужно показать уведомление в UI. Кто инициатор? Логично что бизнес-слой.
2. Что как? В чем проблема заключается? Я не понимаю, на что отвечать. События, интерфейсы, что по сути одно и то же. События — интерфейсы для бедных. Интерфейсы — события для бедных.
3. См. 2.
E>Только я уточню, что речь идет не о нижележащих слоях, которыми управляют через их интерфейсы. E>А речь идет об управлении вышестоящим слоем, который как бы неизвестен.
Аналогично. E>Насколько я понимаю, единственный способ общения нижестоящего слоя с вышестоящим — это через механизм событий-подписок.
Что значит, единственный? БЛ может выставлять флаг/команду, а UI его проверять в каждом оконном сообщении. Суть-то не меняется.
E>Например, Слой приложения (вышестоящий) использует Бизнес-слой. E>Слой приложения отправляет команды Бизнес-слою через интерфейс, а в ответ получает какие-то события. E>Надо, чтобы Бизнес-слой как-то повлиял на логику Слоя приложения.
Бизнес-слой тоже может отправлять команды.
Здравствуйте, es3000, Вы писали:
E>>>Просто подход Мартина — внес новую идею в разделение слоев. S>>Спрятать БЛ за репозиторием и нарисовать во внешнем слое на диаграмме?
E>Да.
Это не нововведение Мартина
E>>>За счет этого это разделение слоев стало еще лучше. S>>Можно конкретнее?
E>Ну раньше ломали голову: как отделить БЛ от хранения данных. E>А теперь Мартин предложил удобное решение.
Барьеры абстракций известны много лет (десятков лет). Их использовали даже в LISP. Кому надо было что-то отделить, отделяли.
Чего там ломать голову? Над чем?
E>>>Подход Мартина — более конкретен чем предыдущие общие формулировки. S>>Более конкретен чем?
E>Тем, что предложил удобное архитектурное решение.
Мне удобнее отделять в тот момент, когда это стало нужно. В каком месте на диаграмме слоев что нарисовано — дело десятое.
S>>Если БЛ у нас управляет тем, какие Entity ему нужны, какая разница, где мы нарисовали DAL, во внешнем слое, или во внутреннем? Линейно расположили слои, или луковицей?
E>Не знаю какая разница. Может быть и никакой. E>А что ты сказать-то хочешь? E>А то всё задаешь вопросы. Ты мысль свою вырази.
Я пытаюсь выяснить, чем лучше архитектура, предложенная Мартином? Но конкретно, а не просто "удобнее". Удобнее чем что? Удобнее чем? Для какого типа приложений?
S>1. Да, бывает. Вообще обычно бизнес-слой всем и управляет. Пример: пришла почта, нужно показать уведомление в UI. Кто инициатор? Логично что бизнес-слой.
А если подробнее — какой "сценарий"?
Что бизнес-слой передает в UI?
Просто сообщение "пришла почта"? Или команду "покажи уведомление"?
S>События, интерфейсы, что по сути одно и то же. События — интерфейсы для бедных. Интерфейсы — события для бедных.
А что есть для "богатых"?
S>Бизнес-слой тоже может отправлять команды.
Если два слоя отправляют друг другу команды, тогда они оба связаны друг с другом.
Они оба "равноправные", нету ни "вышестоящего" ни "нижележащего".
S>>1. Да, бывает. Вообще обычно бизнес-слой всем и управляет. Пример: пришла почта, нужно показать уведомление в UI. Кто инициатор? Логично что бизнес-слой.
E>А если подробнее — какой "сценарий"? E>Что бизнес-слой передает в UI? E>Просто сообщение "пришла почта"? Или команду "покажи уведомление"?
Я пример просто выдумал. Что бы ответить на эти вопросы, мне придется выдумать ответы. Но есть ли в этом какой-то смысл? Принципиальна ли разница между сообщением и командой?
S>>События, интерфейсы, что по сути одно и то же. События — интерфейсы для бедных. Интерфейсы — события для бедных.
E>А что есть для "богатых"?
Теория категорий и монады
S>>Бизнес-слой тоже может отправлять команды.
E>Если два слоя отправляют друг другу команды, тогда они оба связаны друг с другом. E>Они оба "равноправные", нету ни "вышестоящего" ни "нижележащего".
А если они отправляют команды не друг другу, а на деревню дедушке?
S>Это не нововведение Мартина
S>Барьеры абстракций известны много лет (десятков лет). Их использовали даже в LISP. Кому надо было что-то отделить, отделяли.
S>Мне удобнее отделять в тот момент, когда это стало нужно.
Ну вот как раз мне стало "нужно" отделять.
Поэтому я и задал вопрос.
Нужно разделить два слоя: Бизнес-слой внутренний, его использует внешний слой.
Я их разделил.
И теперь стало "нужно", чтобы внутренний слой повлиял на поведение внешнего.
S>В каком месте на диаграмме слоев что нарисовано — дело десятое.
В принципе я согласен.
Но что ты предлагаешь то?
Делать как попало? Не придерживаясь никаких принципов?
S>Я пытаюсь выяснить, чем лучше архитектура, предложенная Мартином? Но конкретно, а не просто "удобнее". Удобнее чем что? Удобнее чем? Для какого типа приложений?
А зачем это выяснять?
Ты все время пытаешься разговор куда-то в сторону увести.
Ну какая разница какая архитектура лучше или хуже?
Я уверен, что ты и сам понимаешь почему и зачем придуманы архитектуры.
E>>А если подробнее — какой "сценарий"? E>>Что бизнес-слой передает в UI? E>>Просто сообщение "пришла почта"? Или команду "покажи уведомление"? S>Я пример просто выдумал. Что бы ответить на эти вопросы, мне придется выдумать ответы. Но есть ли в этом какой-то смысл?
Я думаю, что смысл есть.
А то странно получается: какие-то философские вопросы мы обсуждаем, а как только дело дошло до конкретного решения — "нет смысла"?
Как будто ты избегаешь предложить конкретное решение.
S>Принципиальна ли разница между сообщением и командой?
По моему мнению, с технической точки зрения — разницы нету.
А вот с точки зрения назначения — есть.
А твое мнение? И как эта разница влияет на решение?
E>>А что есть для "богатых"? S>Теория категорий и монады
Когда-то пытался читать теорию категорий. Не понял ее практической применимости в программировании.
Про монады не слышал, сейчас почитаю.
Как теория категорий и монады могут помочь в озвученном вопросе?
E>>Если два слоя отправляют друг другу команды, тогда они оба связаны друг с другом. E>>Они оба "равноправные", нету ни "вышестоящего" ни "нижележащего". S>А если они отправляют команды не друг другу, а на деревню дедушке?
Опять одни вопросы. Ты кому вопросы задаешь? Тоже наверно на деревню дедушке?
Что сказать то хочешь? Ты как будто стесняешься.
Говори прямо — не стесняйся.
Мне любое мнение интересно.
Здравствуйте, es3000, Вы писали:
E>Ну вот как раз мне стало "нужно" отделять. E>Поэтому я и задал вопрос. E>Нужно разделить два слоя: Бизнес-слой внутренний, его использует внешний слой. E>Я их разделил. E>И теперь стало "нужно", чтобы внутренний слой повлиял на поведение внешнего.
О, как... (1)
S>>В каком месте на диаграмме слоев что нарисовано — дело десятое.
E>В принципе я согласен. E>Но что ты предлагаешь то? E>Делать как попало? Не придерживаясь никаких принципов?
До этого момента было обсуждение вопроса о том, "бывает ли такое". Никаких предложений не требовалось до сообщения, на которое я отвечаю. Я даже и подумать не мог, что мне нужно что-то предложить.
А теперь мне не хватает данных, что бы что-то предлагать. Даже не считаю что тезиса (1) достаточно для качественных предложений.
S>>Я пытаюсь выяснить, чем лучше архитектура, предложенная Мартином? Но конкретно, а не просто "удобнее". Удобнее чем что? Удобнее чем? Для какого типа приложений?
E>А зачем это выяснять? E>Ты все время пытаешься разговор куда-то в сторону увести.
— Она лучше
— Чем лучше?
— Ты все время пытаешься разговор увести
Ну ладно.
E>Ну какая разница какая архитектура лучше или хуже?
Если разницы нет, бери архитектуру God Project (по аналогии с God Object) и не парься над слоями.
E>Я уверен, что ты и сам понимаешь почему и зачем придуманы архитектуры.
Затем, что бы удовлетворять требованиям. Техническим, и не только. И мне представляется нелепым обсуждать то, чем такое расположение слоев лучше чем другое в контексте неопределенных требований. Собственно, я и пытаюсь склонить обсуждение к перечислению факторов, в рамках которых принято что одна архитектура лучше другой и слои должны взаимодействовать именно таким образом.
Если считаешь мои наводящие вопросы неконструктивными, то я найду куда пойти и чем заняться.
Здравствуйте, es3000, Вы писали:
S>>Я пример просто выдумал. Что бы ответить на эти вопросы, мне придется выдумать ответы. Но есть ли в этом какой-то смысл?
E>Я думаю, что смысл есть. E>А то странно получается: какие-то философские вопросы мы обсуждаем, а как только дело дошло до конкретного решения — "нет смысла"? E>Как будто ты избегаешь предложить конкретное решение.
Конкретное решение чего именно? Отправки сообщения?
S>>Принципиальна ли разница между сообщением и командой?
E>По моему мнению, с технической точки зрения — разницы нету. E>А вот с точки зрения назначения — есть.
E>А твое мнение? И как эта разница влияет на решение?
Не принципиальна, пока оба слоя разрабатываются и используются одной командой.
E>>>А что есть для "богатых"? S>>Теория категорий и монады
E>Когда-то пытался читать теорию категорий. Не понял ее практической применимости в программировании. E>Про монады не слышал, сейчас почитаю. E>Как теория категорий и монады могут помочь в озвученном вопросе?
Они позволяют представить источник событий монадой и связать их bind-ом с нужным кодом.
E>>>Если два слоя отправляют друг другу команды, тогда они оба связаны друг с другом. E>>>Они оба "равноправные", нету ни "вышестоящего" ни "нижележащего". S>>А если они отправляют команды не друг другу, а на деревню дедушке?
E>Опять одни вопросы. Ты кому вопросы задаешь? Тоже наверно на деревню дедушке? E>Что сказать то хочешь? Ты как будто стесняешься. E>Говори прямо — не стесняйся. E>Мне любое мнение интересно.
Хорошо, вот мой наводящий вопрос в форме утверждения:
Если слои отправляют команды не друг другу, а кому-то еще, то тогда они не будут связаны друг с другом.
E>>Я уверен, что ты и сам понимаешь почему и зачем придуманы архитектуры. S>Затем, что бы удовлетворять требованиям. Техническим, и не только. И мне представляется нелепым обсуждать то, чем такое расположение слоев лучше чем другое в контексте неопределенных требований. S>Собственно, я и пытаюсь склонить обсуждение к перечислению факторов, в рамках которых принято что одна архитектура лучше другой и слои должны взаимодействовать именно таким образом.
Ну так бы прямо и сказал: перечисли факторы такие-то и такие-то.
А то как-то не понятно что и к чему.
S>Если считаешь мои наводящие вопросы неконструктивными, то я найду куда пойти и чем заняться.
Я всегда "ЗА" общение, которое помогает решить проблему.
Только, пожалуйста, выражайся яснее.
S>До этого момента было обсуждение вопроса о том, "бывает ли такое". Никаких предложений не требовалось до сообщения, на которое я отвечаю. Я даже и подумать не мог, что мне нужно что-то предложить.
Ну как же...
Вот я прямо в первом сообщении написал (помимо "бывает ли такое"): "Если ... так делается, то как реализуется такое управление?
Как сделать внешний слой, о котором по сути Бизнес-логика ничего не знает, управляемым?"
S>А теперь мне не хватает данных, что бы что-то предлагать. Даже не считаю что тезиса (1) достаточно для качественных предложений.
Попробую объяснить подробнее.
Есть сложный бизнес-сценарий, который читает из БД данные, преобразует их, записывает в преобразованном виде во внешний файл, сохраняет в БД новые сформированные данные.
В этом сценарии много ответвлений, условий и "развилок", которые зависят от конкретного состояния данных, от результата выполнения действий с БД и файловой системой.
При возникновении этих развилок нужны дополнительные данные, которые надо запросить у пользователя.
И выполнение сценария продолжается в зависимости от ответа пользователя.
Каким образом запросить данные у пользователя так, чтобы не зависеть от слоя UI?
Ведь слой Бизнес-логики не должен зависеть от UI.
Очень упрощенный пример.
Мы в редакторе сохраняем файл, указываем имя.
Бизнес-логика при попытке сохранения файла обнаруживает, что такой файл уже существует.
Дальше она задает вопрос пользователю: "Что дальше делать?". Обычно предлагаются следующие варианты "Заменить, Сохранить под другим именем, Отменить".
Однако, таких вариантов может быть больше и они определяются возможностями бизнес-слоя.
Например, более "продвинутый" бизнес-слой мог бы предложить такие дополнительные варианты действий: "Дописать в существующий файл", "Сохранить под указанным именем переименовав существующий файл" и т. д.
S>>>Я пытаюсь выяснить, чем лучше архитектура, предложенная Мартином? Но конкретно, а не просто "удобнее". Удобнее чем что? Удобнее чем? Для какого типа приложений?
E>>Ну какая разница какая архитектура лучше или хуже? S>Если разницы нет, бери архитектуру God Project (по аналогии с God Object) и не парься над слоями.
Ты сам прекрасно понимаешь, почему так не следует делать.
Следуя твоему примеру, покажу как выглядит наш диалог с моей точки зрения:
— Я: "Как разделить слои"
— Ты: "Надо делить для конкретной цели"
— Я: "Цель такая-то. Как делить?"
— Ты: "Можешь не делить"
— Я: "Но лучше разделить. Как правильно разделить?"
— Ты: "А зачем делить?"
— Я: "За тем-то и затем-то"
— Ты: "Не обязательно делить"
S>Конкретное решение чего именно? Отправки сообщения?
Напоминаю: >> Вообще обычно бизнес-слой всем и управляет. Пример: пришла почта, нужно показать уведомление в UI. Кто инициатор? Логично что бизнес-слой.
Решение отображения уведомления в UI при поступлении почты.
Мне интересно как это правильно сделать по твоему мнению.
Какие действия при этом выполняются и какими объектами\слоями?
E>>Просто сообщение "пришла почта"? Или команду "покажи уведомление"? S>Принципиальна ли разница между сообщением и командой?
E>>По моему мнению, с технической точки зрения — разницы нету. E>>А вот с точки зрения назначения — есть.
E>>А твое мнение? И как эта разница влияет на решение?
S>Не принципиальна, пока оба слоя разрабатываются и используются одной командой.
И все-таки разница есть.
В случае сообщения, отправитель не ждет никакого ответа, его логика и дальнейшее поведение продолжается по прямолинейному алгоритму.
А в случае команды, важен результат ее обработки, поэтому логика отправителя зависит от ответа и далее разветвляется.
E>>Как теория категорий и монады могут помочь в озвученном вопросе? S>Они позволяют представить источник событий монадой и связать их bind-ом с нужным кодом.
S>Хорошо, вот мой наводящий вопрос в форме утверждения: S>Если слои отправляют команды не друг другу, а кому-то еще, то тогда они не будут связаны друг с другом.
Ты имеешь ввиду что-то типа шаблона "Медиатор"?
Про него выше говорили.
Здравствуйте, 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>Какое же это управление?
Самое обычное. Передача сообщения и ожидание реакции. Бывает по-другому?
Здравствуйте, es3000, Вы писали:
E>Ты говоришь про "сервис". Под "сервисом" ты имеешь ввиду сервисный слой? E>По другому он кажется называется "Application Layer". E>Это про него идет речь?
да, это слой который оркестрирует другими слоям, workflow, service — точное название не имеет особого значения. В данном примере немного неудачно выбрано назначение бизнес-логики, сохранение файла будет ответственностью DAL (хотя для редактора это скорее надо будет назвать как-то по-другому), именно он будет кидать исключения о существующем файле, недоступности папки на запись и прочее, а бизнес слой в редакторе будет управлять всяким форматированием, проверкой синкаксиса и прочее.
Сервис получит ссылки на бизнес-обьекты, DAL (через интерфейс что-бы замокить можно было для тестов) и (возможно) UI.
E>Можете предложить какой-то вариант для примера?
Очень трудно предлагать вариант, не имея конкретной задачи. Потому что проектирование — оно всегда идёт от задачи. Иначе опять получаем культ карго: по инструкции, разработанной для паровоза, собирается велосипед. Или наоборот.
В целом лично у меня всегда идёт перекос в сторону 3-tier архитектуры — когда над слоем persistence живёт логика приложения, а весь UI спроектирован в виде клиента, который общается с application tier через API. При этом в API границы транзакций совпадают с границами методов. Любой "сценарий" реализуется в виде набора атомарных шагов, каждый из которых можно выполнить независимо.
Всё взаимодействие tier-ов идёт строго в одном направлении: сверху вниз. Application tier не имеет возможностей типа "а давайте-ка покажем модальный диалог" или "сделаем кнопку save недоступной". Максимум интерактива — это возврат 400 Bad Request со структурированным body, по которому клиент может понять, что он сделал не так.
Если хочется иметь оповещения за пределами рамок текущей операции, то для этого делается специальный метод ReceiveNotification(since: notificationMarker), которого клиент периодически поллит и показывает нотификации в удобном ему стиле.
Это всё сильно отражается не столько на структуре слоёв, сколько на устройстве логики приложения. Если в Delphi-event-handler-style можно было просто начать операцию, и ветвиться по ней в процессе общения как со storage, так и с UI, то в таком подходе так делать нельзя. Нужно всё время думать о том, что может пойти не так, и как продолжить операцию в случае чего. То есть, скажем, "заявка на перевод" из эфемерной формы диалога превращается в полноценный объект, хранящийся в базе. У него появляются этапы конструирования, правила перевода из одного состояния в другое, и прочие штуки.
Иногда такой подход приводит вообще к замене ядра приложения на workflow management system типа jira, где вся логика сводится к описанию атрибутов сущности, состояний сущности, и правил перехода между этими состояниями в зависимости от значений атрибутов.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
S>да, это слой который оркестрирует другими слоям, workflow, service — точное название не имеет особого значения. В данном примере немного неудачно выбрано назначение бизнес-логики, сохранение файла будет ответственностью DAL (хотя для редактора это скорее надо будет назвать как-то по-другому), именно он будет кидать исключения о существующем файле, недоступности папки на запись и прочее, S>а бизнес слой в редакторе будет управлять всяким форматированием, проверкой синкаксиса и прочее. S>Сервис получит ссылки на бизнес-обьекты, DAL (через интерфейс что-бы замокить можно было для тестов) и (возможно) UI.
Получается, что это слой "Сервис" должен выдавать пользователю запрос на дальнейшую обработку файла.
То есть он должен работать с UI.
Тут два вопроса:
1) Этот код — запрос на дальнейшую обработку файла — это часть логики приложения.
Это не часть UI.
Получается, что важный для логики работы код находится вне бизнес-слоя, в каком-то другом слое.
Мне кажется это не хорошо.
2) На всех схемах дизайна приложения слой "Сервис" рисуется подчиненным по отношению к UI.
То есть согласно этим схемам не "Сервис" управляет пользовательским интерфейсом, а пользовательский интерфейс управляет "Сервисом".
Ты же предлагаешь сделать наоборот.
Тогда, наш "Сервис" будет супер-главным слоем, а все остальные ему подчинены.
Нету тут противоречия?
Здравствуйте, Sinclair, Вы писали:
S>В целом лично у меня всегда идёт перекос в сторону 3-tier архитектуры — когда над слоем persistence живёт логика приложения, а весь UI спроектирован в виде клиента, который общается с application tier через API. При этом в API границы транзакций совпадают с границами методов. Любой "сценарий" реализуется в виде набора атомарных шагов, каждый из которых можно выполнить независимо.
Поддерживаю.
От себя добавлю, что оформление бизнес-логики в виде черного ящика с API очень удобно для написания юнит тестов.
И, как раз, при написании юнит тестов, где никакого UI нет, прекрасно выявляются "плохие" места логики.
Бывают случаи, когда бизнес-логика (сервер), требует выбора или подтверждения какого-то действия клиентом
(это может быть как интерактивный клиент с UI, так и что-то другое).
Я для таких случаев применял API из пары функций:
Здесь "действия" могут быть битовыми флагами, списком строк или еще чем-то в этом роде.
Предполагается, что клиент заранее не знает какие действия надо выполнять (например, сохранить изменения в файл).
Сначала вызывается QueryActions(маска — "Все действия"), она возвращает — "сохранение в файл".
Т.е. модуль бизнес-логики сообщает клиенту свои хотелки.
Далее клиент может показать UI (если клиент с UI) и вызвать (или не вызывать) PerformActions("сохранение в файл").
Вот такой протокол взаимодействия я применял для избавления от блокирующего callback из модуля бизнес-логики.
Подход не лишен недостатков. Например, нет транзакционности, т.е. информация возвращенная QueryActions может протухнуть ко времени вызова PerformActions.
С этим надо что-то решать, опираясь на специфику задачи. Серебряной пули нет.
S>Это всё сильно отражается не столько на структуре слоёв, сколько на устройстве логики приложения. Если в Delphi-event-handler-style можно было просто начать операцию, и ветвиться по ней в процессе общения как со storage, так и с UI, S>то в таком подходе так делать нельзя. Нужно всё время думать о том, что может пойти не так, и как продолжить операцию в случае чего. То есть, скажем, "заявка на перевод" из эфемерной формы диалога превращается в полноценный объект, хранящийся в базе. S>У него появляются этапы конструирования, правила перевода из одного состояния в другое, и прочие штуки.
Тогда получается, что оперировать этими "этапами" и "правилами перевода" должен какой-то код более высокого уровня, какой-то отдельный слой.
Этот слой должен реализовать логику, которая использует "этапы" и "правила перевода" бизнес-объекта.
То есть, это должен быть достаточно "умный" слой.
Ведь без него по сути и приложения никакого не получится, даже несмотря на наличие отлично спроектированного и реализованного бизнес-слоя (но не имеющего этой логики).
Причем, этот слой не зависит от конкретного UI, DAL и других внешних слоев, так как он реализует только логику.
Получается, что вся суть приложения разделена между двумя слоями:
— Бизнес-слой реализует этапы, состояния и правила бизнес-объектов
— "Умный" слой, который реализует логику приложения и оперирует бизнес-слоем.
Правильно я рассуждаю?
Если да, то тогда есть такие вопросы:
1) Что это за "Умный" слой? Как его обычно называют?
2) На каком уровне "иерархии" слоев он находится?
По идее это должен быть самый высокий уровень, так как он оперирует также и другими слоями в том числе и UI.
Q>Бывают случаи, когда бизнес-логика (сервер), требует выбора или подтверждения какого-то действия клиентом Q>(это может быть как интерактивный клиент с UI, так и что-то другое). Q>Я для таких случаев применял API из пары функций: Q>
Q>Здесь "действия" могут быть битовыми флагами, списком строк или еще чем-то в этом роде.
Идея понятна.
Q>Предполагается, что клиент заранее не знает какие действия надо выполнять (например, сохранить изменения в файл). Q>Сначала вызывается QueryActions(маска — "Все действия"), она возвращает — "сохранение в файл".
А кто вызывает QueryActions?
Клиент?
Если клиент, тогда зачем и в какой момент он вызывает эту функцию?
Q>Т.е. модуль бизнес-логики сообщает клиенту свои хотелки. Q>Далее клиент может показать UI (если клиент с UI) и вызвать (или не вызывать) PerformActions("сохранение в файл").
Для выполнения этого действия надо передать параметры.
Как это делается?
Здравствуйте, es3000, Вы писали: E>А кто вызывает QueryActions? E>Клиент? E>Если клиент, тогда зачем и в какой момент он вызывает эту функцию?
Да, если под клиентом понимается "тот, кто использует API модуля бизнес логики".
Q>>Т.е. модуль бизнес-логики сообщает клиенту свои хотелки. Q>>Далее клиент может показать UI (если клиент с UI) и вызвать (или не вызывать) PerformActions("сохранение в файл").
E>Для выполнения этого действия надо передать параметры. E>Как это делается?
Псевдокод:
filename = "foo.dat";
response = QueryMyFileActions(filename, ActionType::Save); // есть ли вопросы по поводу сохранения файла "foo.dat"?
if (response.has(ActionResponse::Overwrite)) // требуется перезапись файла
if (!QuestionUI(filename, ActionResponse::Overwrite)) // показываем MessageBox
return; // пользователь отказался от перезаписи
try
{
PerformMyFileAction(filename, ActionType::Save); // выполняем сохранение
}
catch(ex)
{
ErrorUI(ex.msg()); // не шмогла...
}
E>Как сделать внешний слой, о котором по сути Бизнес-логика ничего не знает, управляемым?
Придумал вот такой пример — игра, например, в шахматы пользователя с компьютером.
Понятно, что пользователь взаимодействует с программой через интерфейс.
Когда ход делает человек, то все нужные функции (в том числе и бизнес-логики) инициируются событиями пользовательского интерфейса.
А во время хода компьютера бизнес-логика должна инициировать действие с пользовательским интерфейсом.
Но у бизнес-логики нет прямого взаимодействия с интерфейсом.
Вот где должен располагаться код обработки хода компьютера и, как организуется последующее взаимодействие с пользовательским интерфейсом?
S>Очень трудно предлагать вариант, не имея конкретной задачи.
Придумал пример — игра пользователя с компьютером в шахматы.
Чтобы здесь ветку не раздувать, написал этот пример в новой ветке: http://rsdn.org/forum/design/7456335.1
Q>Да, если под клиентом понимается "тот, кто использует API модуля бизнес логики".
Q>Псевдокод: Q>
Q>filename = "foo.dat";
Q>response = QueryMyFileActions(filename, ActionType::Save); // есть ли вопросы по поводу сохранения файла "foo.dat"?
Q>if (response.has(ActionResponse::Overwrite)) // требуется перезапись файла
Q> if (!QuestionUI(filename, ActionResponse::Overwrite)) // показываем MessageBox
Q> return; // пользователь отказался от перезаписи
Q>try
Q>{
Q> PerformMyFileAction(filename, ActionType::Save); // выполняем сохранение
Q>}
Q>catch(ex)
Q>{
Q> ErrorUI(ex.msg()); // не шмогла...
Q>}
Q>
Здравствуйте, es3000, Вы писали: E>То есть, это должен быть достаточно "умный" слой. E>Ведь без него по сути и приложения никакого не получится, даже несмотря на наличие отлично спроектированного и реализованного бизнес-слоя (но не имеющего этой логики). E>Причем, этот слой не зависит от конкретного UI, DAL и других внешних слоев, так как он реализует только логику.
Если мы говорим об UI-приложении, то этот слой — наш пользователь.
Это пользователь дергает за ниточки UI, которые в конечном счете ведут к функциям бизнес-логики.
Также это может быть юнит тест, который моделирует различные сценарии работы пользователя, или какой-то скрипт/макрос, который написал опытный пользователь.
Например, в Excel пользователь может воздействовать на модель (документ) через UI, а может написать макрос на VB, который работает с моделью через тот же API (OLE Automation).
E>Получается, что вся суть приложения разделена между двумя слоями: E>- Бизнес-слой реализует этапы, состояния и правила бизнес-объектов E>- "Умный" слой, который реализует логику приложения и оперирует бизнес-слоем.
E>Если да, то тогда есть такие вопросы: E>1) Что это за "Умный" слой? Как его обычно называют? E>2) На каком уровне "иерархии" слоев он находится? E>По идее это должен быть самый высокий уровень, так как он оперирует также и другими слоями в том числе и UI.
Насколько пользователи бывают умные и наоборот, существуют разные точки зрения.
Мой опыт подсказывает, что все что можно испортить и где напортачить, они сделают...
Поэтому, разработчику следует априори рассматривать "умный слой", как обезьяну с гранатой.
Если приложение не такого рода, что "в любой момент творю, что хочу", то надо выделять некие control flow, ориентированные на решение большой задачи.
Выполнение очередного шага большой задачи переводит объекты бизнес-логики из одного состояния в другое.
При выполнении большой задачи могут возникать циклические шаги, например, ввод информации для нескольких элементов данных.
UI может выстраиваться с учетом решения больших задач (привет от мастера-wizard'a).
В каких-то случаях это может оказаться невозможными или очень трудоемким — тогда следование логике большой задачи целиком возлагается на пользователя (см. User's Guide стр. 55555).
Пример большой задачи — расчет з/п на предприятии.
Прежде чем сделать платежную ведомость, надо ввести отработанные дни, больничные, рассчитать налоги — все это шаги большой задачи.
Здравствуйте, es3000, Вы писали: E>>Как сделать внешний слой, о котором по сути Бизнес-логика ничего не знает, управляемым? E>Придумал вот такой пример — игра, например, в шахматы пользователя с компьютером.
Я тебе другой пример предложу — микроволновку. Вот ты положил в нее пиццу и поставил её на разогрев. Глупо ожидать от микроволновки, что она положит тебе кусок пиццы в рот и разжует. Микроволновка тобой не управляет. Это твои проблемы, когда и как ты вспомнишь про пиццу в микроволновке, достанешь ты ее сам или попросишь коллегу, съешь пиццу сам или угостишь симпатишную тестеровщицу. Микроволновке пофиг. Максимум, что микроволновка может — пискнуть и выключиться.
Так вот, микроволновка это и есть твой BLL. Не пытайся всунуть в неё несвойственные ей функции, это как минимум странно.
Всё, что нас не убивает, ещё горько об этом пожалеет.
Q>Если мы говорим об UI-приложении, то этот слой — наш пользователь.
Нет, я имел ввиду не пользователя.
Q>Если приложение не такого рода, что "в любой момент творю, что хочу", то надо выделять некие control flow, ориентированные на решение большой задачи.
Вот что я имел ввиду под "умным слоем" — слой control flow.
Q>Выполнение очередного шага большой задачи переводит объекты бизнес-логики из одного состояния в другое. Q>При выполнении большой задачи могут возникать циклические шаги, например, ввод информации для нескольких элементов данных. Q>UI может выстраиваться с учетом решения больших задач (привет от мастера-wizard'a).
Вот я и веду речь как раз о таких "умных" control flow, которые должны управлять wizard-ом.
Q>Пример большой задачи — расчет з/п на предприятии. Q>Прежде чем сделать платежную ведомость, надо ввести отработанные дни, больничные, рассчитать налоги — все это шаги большой задачи.
Только я считал (да и сейчас считаю), что логика этих control flow — это чисто логика имеющая отношение к бизнес-слою.
Расчет зарплаты — типичный пример. Ну разве это не бизнес-логика?
Получается, что есть бизнес-логика, которая завязана на действия пользователя.
Которая диктует пользователю — что надо делать.
То есть бизнес-логика управляет пользовательским интерфейсом.
Вот в чем мой вопрос.
В каком слое надо делать эту логику? Как она должна управлять взаимодействием с пользователем, оставаясь при этом независимой от интерфейса?
Р>Я тебе другой пример предложу — микроволновку. Вот ты положил в нее пиццу и поставил её на разогрев. Глупо ожидать от микроволновки, что она положит тебе кусок пиццы в рот и разжует. Микроволновка тобой не управляет.
Я говорю не про управление человеком.
А про управление пользовательским интерфейсом со стороны логики приложения.
Микроволновка хотя бы пропищит "забирай свою пицу" и будет ждать ответа.
И компьютер в шахматной партии сделает ход и будет ждать ответа.
То есть они в любом случае дадут знать пользовательскому интерфейсу — что надо сделать на следующем шаге.
Р>Микроволновке пофиг. Максимум, что микроволновка может — пискнуть и выключиться.
Вот этот писк — это и есть действия пользовательского интерфейса микроволновки.
И этот писк был инициирован ее логикой работы: закончила греть — пискни.
Р>Так вот, микроволновка это и есть твой BLL.
Нет. Микроволновка — это UI + BLL.
UI — Это мигающие лампочки и писки. И они включаются тогда когда посчитает нужным BLL микроволновки.
Это полностью соотвествует моему вопросу.
Компьютер (бизнес-логика) придумал ход — говорит интерфейсу — нарисуй на шахматной доске, спроси у пользователя ответ.
Или в случае визарда: бизнес-логика что-то обрабатывает, ей не хватает данных, она говорит интерфейсу — запроси у пользователя дополнительные данные.
Здравствуйте, es3000, Вы писали: Q>>Пример большой задачи — расчет з/п на предприятии. Q>>Прежде чем сделать платежную ведомость, надо ввести отработанные дни, больничные, рассчитать налоги — все это шаги большой задачи.
E>Только я считал (да и сейчас считаю), что логика этих control flow — это чисто логика имеющая отношение к бизнес-слою. E>Расчет зарплаты — типичный пример. Ну разве это не бизнес-логика?
Вводить исходные данные для расчета з/п могут несколько человек в течении нескольких дней, у каждого свой UI в определенном состоянии, который они закрывают в конце рабочего дня.
Т.е. состояние объектов бизнес-логики пассивно по отношению к UI.
Пользователь воздействует на UI, UI вызывает функции BL API (прямо или через какой-нибудь контроллер).
Когда в UI открыли какой-то диалог, ввели данные и нажали ОК, то изменяется состояние объектов бизнес-логики (и состояние БД, в конечном счете).
Если происходит нарушение условий бизнес-логики, то генерируется ошибка.
Чтобы UI-диалог не закрывался при заведомо ошибочных условиях, у модуля бизнес-логики должен быть API валидации данных.
E>Получается, что есть бизнес-логика, которая завязана на действия пользователя.
Бизнес-логика не завязана на действия пользователя. Бизнес-логика проверяет входящие запросы и генерирует ошибку, если запросы противоречат ее внутренним правилам.
E>Которая диктует пользователю — что надо делать.
Пользователь делает, что хочет, но если это противоречит правилам бизнес логики, то получает ошибку.
E>То есть бизнес-логика управляет пользовательским интерфейсом.
Нет. Бизнес-логика прекрасно живет без всякого UI, ей не важно кто дергает API.
Разработчик для создания дружественного пользователю UI может предусмотреть дополнительное API для подсказок по control flow и валидации данных.
При этом UI будет руководствоваться тем, что вернули функции BL API.
Поэтому правильно говорить, что реализация UI завязана/зависит от BL API.
BL не управляет UI, это UI через BL API отображает и изменяет состояние объектов бизнес логики.
Здравствуйте, es3000, Вы писали: E>Нет. Микроволновка — это UI + BLL.
Не дроби мне слои. Микроволновка — BLL, человек — UI.
E>Компьютер (бизнес-логика) придумал ход — говорит интерфейсу — нарисуй на шахматной доске, спроси у пользователя ответ.
Ничего подобного интерфейсу никто не говорит. Не хватало еще, чтобы мне микроволновка указывала. Что рисовать — решает UI. Может он в это время пользователю рекламу гугла крутит? Какой ход, какое рисование — бабло важнее. В примере с микроволновкой, например, ты с девушкой сексом занимаешься — да пусть хоть обпищится и обмигается своими лампочками.
Максимум, что может BLL — пискнуть и помигать лампочкой. В подавляющем большинстве случаев даже этого никто не делает — путь UI сам разбирается, что и когда рисовать пользователю. Потому что UI на компе пользователя, а BLL на сервере который вообще нифига не знает, что у пользователя творится.
Всё, что нас не убивает, ещё горько об этом пожалеет.
Здравствуйте, es3000, Вы писали:
E>1) Этот код — запрос на дальнейшую обработку файла — это часть логики приложения. E>Это не часть UI. E>Получается, что важный для логики работы код находится вне бизнес-слоя, в каком-то другом слое. E>Мне кажется это не хорошо.
"запрос на дальнейшую обработка файла" не является бизнес логикой. Если файл не может быть сохранен — то сервис ловит исключение и обрабатывает его должным образом (показывая пользователю окно, где он либо должен ввeсти новое имя, либо изменить какие-то настройки, например разрешить перезаписать файл поверх существующего)
E>2) На всех схемах дизайна приложения слой "Сервис" рисуется подчиненным по отношению к UI. E>То есть согласно этим схемам не "Сервис" управляет пользовательским интерфейсом, а пользовательский интерфейс управляет "Сервисом". E>Ты же предлагаешь сделать наоборот. E>Тогда, наш "Сервис" будет супер-главным слоем, а все остальные ему подчинены. E>Нету тут противоречия?
При стандартном подходе UI ответственна только за рисование пикселей и отображение окон. Не знаю читал-ли ты эти паттерны, но там есть несколько вариантов, "тупой" UI — он вообще ничего ни о чем не знает и им управляет контроллер или сервис, контроллер знает о бизнес обьектах, вызывает их методы и соответственно командует интерфейсом для обновления данных и показе новых окон. "Немного более умный" UI завязывается на события и свойства бизнес-обьектов и сам отображает нужные данные при смене состояния бизнес обьектов, есть еще пара разновидностей типа mvvc уже не помню его точных отличий. Но виндовая разработка уже в прошлом, сейчас в моде веб, api, spa, микросервисы и прочее, я-б на твоем месте уделял им внимание т.к. будущее за ними, а не за тем кто и как управляет отображением очередной виндовой формы
S>"запрос на дальнейшую обработка файла" не является бизнес логикой.
Почему?
Может же быть такое приложение, которое работает с файлами.
Допустим мы пишем новый файловый менеджер — для него предметной областью является работа с файлами.
Получается, мы искусственно "вырезаем" логику из слоя BLL и переносим куда-то.
S>При стандартном подходе UI ответственна только за рисование пикселей и отображение окон. S>... контроллер знает о бизнес обьектах, вызывает их методы и соответственно командует интерфейсом для обновления данных и показе новых окон.
Получается, все контроллеры в совокупности и есть этот "Сервис"-слой?
Здравствуйте, es3000, Вы писали:
E>Вот где должен располагаться код обработки хода компьютера и, как организуется последующее взаимодействие с пользовательским интерфейсом?
Очень просто. Весь интерфейс шахмат выглядит примерно так:
public BoardState HandleUserMove(BoardState currentState, Move userMove)
То есть мы передаём состояние доски и свой ход. "Бизнес логика" проверяет, что предложенный ход корректен, и выкинет исключение, если что-то не так.
В состояние доски, помимо расположения фигур, входит также набор уже срубленных фигур, и история ходов обеих сторон.
Это позволяет нам обойтись полностью Stateless реализацией, что имеет массу очевидных преимуществ.
UI при этом может быть совершенно произвольным — графическим, текстовым, 3d с анимациями, да хоть почтовым роботом.
Мы получаем от BL описание нового состояния доски, и "рисуем" именно его. Всё.
Это — основа, на которую можно наворачивать дополнительную логику.
Если мы вдруг хотим улучшить удобство интерфейса и подготовиться к экзотической ситуации "правила шахмат могут внезапно меняться", то можно добавить метод GetValidMoves(BoardState currentState) (Вряд ли нам потребуется более мелкая гранулярность, т.к. количество возможных ходов всеми фигурами для любого состояния доски едва ли превышает несколько сотен).
Впрочем, как product manager я бы зарубил реализацию этого метода в BL вплоть до получения внятного обоснования. Полезность этого метода слишком зависит от специфики UI, поэтому для начала я бы предложил реализовать его именно на стороне UI. Вот если у нас окажется больше одной параллельной реализации UI, которым нужен такой метод, при этом переиспользование кода традиционным путём невозможно, то можно будет задуматься о расширении BL.
Если у нас есть желание предотвратить "жульничество" пользователем, который подсовывает рукодельную историю ходов и положение доски, то всегда можно добавить в BoardState цифровую подпись сервера.
И, скажем, постановка мата нашему сервису будет заслуживать приза только если currentState подписан корректно. А если не подписан — то это просто кто-то играет с нами этюд.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, es3000, Вы писали: E>Решение отображения уведомления в UI при поступлении почты. E>Мне интересно как это правильно сделать по твоему мнению. E>Какие действия при этом выполняются и какими объектами\слоями?
Слой UI по таймеру дёргает метод BL ReceiveNotificationsSince(lastProcessedMarker)
А дальше — по своему усмотрению: всплывающая плашка в трее, кружочек с количеством непрочитанных нотификаций над иконкой "почта", воспроизведение звука/цвета/запаха...
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Q>Вводить исходные данные для расчета з/п могут несколько человек в течении нескольких дней, у каждого свой UI в определенном состоянии, который они закрывают в конце рабочего дня. Q>Т.е. состояние объектов бизнес-логики пассивно по отношению к UI.
А почему бизнес-логика не может повести себя активно?
Например, при запуске программы на следующий день, инициализируется бизнес-логика.
Бизнес-логика видит, что для завершения расчета зарплаты нужна следующая порция данных, и дает сигнал.
А UI реагирует на этот сигнал и выводит красивое уведомление "Для завершения расчета зарплаты надо ввести данные такие-то".
Q>Когда в UI открыли какой-то диалог, ввели данные и нажали ОК, то изменяется состояние объектов бизнес-логики (и состояние БД, в конечном счете). Q>Если происходит нарушение условий бизнес-логики, то генерируется ошибка. Q>Чтобы UI-диалог не закрывался при заведомо ошибочных условиях, у модуля бизнес-логики должен быть API валидации данных. Q>Бизнес-логика не завязана на действия пользователя. Бизнес-логика проверяет входящие запросы и генерирует ошибку, если запросы противоречат ее внутренним правилам. Q>Пользователь делает, что хочет, но если это противоречит правилам бизнес логики, то получает ошибку.
Это все понятно. Это стандартное "книжное" взаимодействие UI и BLL.
Я просто веду разговор к тому, что есть масса задач, где именно BLL должна инициировать какие-то действия.
E>>То есть бизнес-логика управляет пользовательским интерфейсом. Q>Нет. Бизнес-логика прекрасно живет без всякого UI, ей не важно кто дергает API.
Ну а вот такой пример. Допустим имеем приложение для индивидуального предпринимателя.
Он запускает программу в последний день сдачи декларации.
Нужно, чтобы программа при запуске ему вежливо и красиво нарисовала окошко "Дорогой предприниматель! Беги скорее в налоговую, сегодня последний день!".
Анализ текущей даты и даты сдачи отчетности — это не дело UI.
Значит, этот анализ надо поместить в BLL.
И "заставить" UI нарисовать это окошко.
А если UI не нарисует — значит это плохой UI, его надо переделать.
Q>Разработчик для создания дружественного пользователю UI может предусмотреть дополнительное API для подсказок по control flow и валидации данных.
Вот может быть такие "подсказки" будут решением.
Как реализуются такие подсказки по control flow?
E>Как сделать внешний слой, о котором по сути Бизнес-логика ничего не знает, управляемым?
Длинное получилось обсуждение.
Есть еще два вопроса, которые связаны с исходным вопросом.
Задам каждый в отдельной ветке, чтобы удобнее было осуждать.
Первый вопрос.
BLL при изменении своего состояния оповещает вышестоящие слои.
Какой механизм оповещений используется? Что-то типа подписок на события.
Типа "финансовый отчет готов".
Обработчик события — это обычная функция.
В чем разница между вызовом обработчика события, который дергает подписчиков, и прямой передачи какой-то команды?
Ведь передача команды — это такая же функция.
Почему нельзя вместо события вызвать функцию вышестоящего слоя "Забирай отчет и покажи его"?
E>Как сделать внешний слой, о котором по сути Бизнес-логика ничего не знает, управляемым?
Еще вопрос.
По всем книжкам BLL управляет слоем DAL.
BLL прямо "говорит" DAL: дай мне такие-то данные.
BLL не оповещает его, не события выкидывает, а дает прямые команды и инструкции DAL-у.
Ну конечно через интерфейс-адаптер, но это сути не меняет.
Более того.
Если оказывается, что бизнес-данные лежат например в разных СУБД, то бизнес-слой будет запрашивать данные уже у двух разных DAL.
То есть BLL может и должен управлять другими слоями, если ему это надо для решения своей задачи.
Почему же BLL не может отправить такую же команду запроса данных у другого слоя, отличного от DAL?
Также через интерфейс-адаптер.
Вот в чем вопрос.
Может ли BLL запрашивать данные у других слоев, а не только у DAL?
Если нет — то почему?
Здравствуйте, es3000, Вы писали:
E>А почему бизнес-логика не может повести себя активно? E>Например, при запуске программы на следующий день, инициализируется бизнес-логика. E>Бизнес-логика видит, что для завершения расчета зарплаты нужна следующая порция данных, и дает сигнал. E>А UI реагирует на этот сигнал и выводит красивое уведомление "Для завершения расчета зарплаты надо ввести данные такие-то".
При минимальном масштабировании из простой однопользовательской локальной программы в простейший клиент-сервер такой подход развалится.
Конечно, если есть желание в 2019 году разрабатывать софт в духе Turbo Pascal для MS DOS, то можно и активную бизнес логику сделать.
В масштабируемой архитектуре бизнес-логика живет на сервере (или серверах — для распределенных систем).
При этом никто не мешает элементарно сделать напоминашки при запуски какого-то клиентского приложения.
Т.е. при запуске или по таймеру UI может спросить у сервера текущее состояние объектов бизнес логики и послать пользователя в налоговую или еще куда-нибудь.
Здравствуйте, es3000, Вы писали:
E>Почему? E>Может же быть такое приложение, которое работает с файлами. E>Допустим мы пишем новый файловый менеджер — для него предметной областью является работа с файлами. E>Получается, мы искусственно "вырезаем" логику из слоя BLL и переносим куда-то.
для редактора файлов — не будет. Не надо путать различные типы приложений, у них у каждого будет свой бизнес слой для конкретной предметной области
E>Получается, все контроллеры в совокупности и есть этот "Сервис"-слой?
Здравствуйте, es3000, Вы писали:
E>По всем книжкам BLL управляет слоем DAL. E>BLL прямо "говорит" DAL: дай мне такие-то данные. E>BLL не оповещает его, не события выкидывает, а дает прямые команды и инструкции DAL-у. E>Ну конечно через интерфейс-адаптер, но это сути не меняет.
да нет такого, не знаю в каких ты это книжках прочитал, но бизнес слой это всегда чистая логика не знающая ни о чем и ни чем не управляющая
Здравствуйте, es3000, Вы писали:
E>А почему бизнес-логика не может повести себя активно? E>Например, при запуске программы на следующий день, инициализируется бизнес-логика. E>Бизнес-логика видит, что для завершения расчета зарплаты нужна следующая порция данных, и дает сигнал. E>А UI реагирует на этот сигнал и выводит красивое уведомление "Для завершения расчета зарплаты надо ввести данные такие-то".
Ну, откуда же здесь "активность"? Пока программу не запустили, бизнес-логика позорно спала.
А если бизнес-логика начинает "активничать" только при запуске программы, то в ней активности не больше, чем в дверном звонке: пока не нажмёшь на кнопку, он не звонит.
В нормальной архитектуре программа при запуске (и не только при запуске) дёргает метод BL GetNotifications(...), который выдаёт ей все оповещения.
У этого подхода — масса преимуществ. Например, что будет делать ваша программа, когда "красивое оповещение" захотят показать сразу два модуля — зарплата и налоги?
Получается, каждый из них должен учитывать не только свои хотелки, но и то, что параллельно могут работать другие модули.
В классической архитектуре именно UI решает, как с этим быть. То ли показать модальные диалоги в foreach(); то ли показать окно со списком оповещений; то ли вывести плашку с оповещениями и кнопками "вперёд"/"назад" в главном окне.
А может быть это вообще не UI, а сервис, который запускается по расписанию, и превращает каждое оповещение в email/SMS, который отправляется пользователю.
Бизнес-логика ничего этого не знает, её не нужно переписывать для того, чтобы научить работать в каждом из этих сценариев. Ей не нужно отслеживать, получил ли пользователь эти уведомления, или нет.
E>Я просто веду разговор к тому, что есть масса задач, где именно BLL должна инициировать какие-то действия.
Нет таких задач.
E>Ну а вот такой пример. Допустим имеем приложение для индивидуального предпринимателя. E>Он запускает программу в последний день сдачи декларации. E>Нужно, чтобы программа при запуске ему вежливо и красиво нарисовала окошко "Дорогой предприниматель! Беги скорее в налоговую, сегодня последний день!". E>Анализ текущей даты и даты сдачи отчетности — это не дело UI. E>Значит, этот анализ надо поместить в BLL.
См. выше. E>И "заставить" UI нарисовать это окошко. E>А если UI не нарисует — значит это плохой UI, его надо переделать.
"Заставление" UI можно делать десятком способов. Вхреначивать их в BL — максимально плохая идея.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, es3000, Вы писали:
E>BLL при изменении своего состояния оповещает вышестоящие слои. E>Какой механизм оповещений используется? Что-то типа подписок на события. E>Типа "финансовый отчет готов". E>Обработчик события — это обычная функция.
E>В чем разница между вызовом обработчика события, который дергает подписчиков, и прямой передачи какой-то команды? E>Ведь передача команды — это такая же функция. E>Почему нельзя вместо события вызвать функцию вышестоящего слоя "Забирай отчет и покажи его"?
Во-первых, потому, что у одного события может быть одновременно несколько обработчиков.
Во-вторых, потому, что вышестоящий слой может не хотеть "забирать и показывать" отчёт вот прямо вот сейчас. Например, пользователь сейчас выступает с презентацией перед потенциальным партнёром, и тут ваш мерзкий вышестоящий слой ХОБА! и показал "отчёт об убытках за 2 квартал", полностью опровергающий презентацию. Событие — это "вот что произошло", ReportReady. А "функция" — это "вот что я хочу сделать", это ShowReport().
В-третьих, оба решения плохи. Потому что в обоих BLL требует, чтобы рядом был вышестоящий слой, причём с очень конкретными возможностями. Если вышестоящий слой по какой-то причине недоступен — всё, копец, сломалась бизнес-логика. Отчёт, который строился 40 часов, не удалось показать пользователю, потому что в это время уже был запущен модальный диалог. Всё, упс, запускаем отчёт заново. Или реализация перевода денег со счёта на счёт перед коммитом решила дёрнуть событие BeforeTransferComplete, а UI-программист влепил в OnBeforeTransferComplete модальный message box. Пользователь кнопочку submit нажал, вышел покурить, да так и ушёл на выходные. Диалог висит, управление в BLL так и не вернулось, транзакция открыта, поступление денег на оба счёта заблокировано в ожидании окончания транзакции.
Оттого что в кузнице не было гвоздя. Не надо так делать. BLL всегда должна быть пассивной. Хочется поделиться чем-то с пользователем — есть стандартный паттерн:
1. Есть общий для всех BLL-модуль "оповещения о событиях".
2. Каждый модуль может пользоваться SubmitNotification, который записывает оповещения в очередь.
3. Степень развесистости этого модуля бывает различной. От примитивных текстовых сообщений, до структур с ShortDescription, FullDescription, Stage(Begin/Progres/Complete), Status(Info/Error/Warning), ProposedAction, и вложенностью нотификаций.
4. Те клиенты UI, которые хотят работать с нотификациями, пишут свою реализацию опроса нотификаций поверх конкретно этого модуля. Это позволяет написать клиента (или как минимум эту его часть) один раз, и не приделывать ему новые функции всякий раз, как BLL хочет сообщить о чём-то новом (готовность отчёта, необходимость рассчитать зарплату, запрос от налоговой и т.п.).
5. Эта реализация может приоритизировать нотификации, помечать статусы прочитанности, и так далее. А может и ничего не делать — к примеру, просто форвардить нотификации в почту, WhatsApp, SMS, и.т.п. При этом BLL не надо переписывать каждый раз при добавлении нового канала отправки, и она не встаёт колом при каждой потере связи с сервисом WhatsApp.
Вот это — нормальный способ реализовать "финансовый отчёт готов".
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
E>>Бизнес-логика видит, что для завершения расчета зарплаты нужна следующая порция данных, и дает сигнал. E>>А UI реагирует на этот сигнал и выводит красивое уведомление "Для завершения расчета зарплаты надо ввести данные такие-то".
S>Ну, откуда же здесь "активность"? Пока программу не запустили, бизнес-логика позорно спала. S>А если бизнес-логика начинает "активничать" только при запуске программы, то в ней активности не больше, чем в дверном звонке: пока не нажмёшь на кнопку, он не звонит.
S>В нормальной архитектуре программа при запуске (и не только при запуске) дёргает метод BL GetNotifications(...), который выдаёт ей все оповещения.
Но все-таки уведомления должен вычислить именно бизнес-слой.
Правильно?
А их обработку — UI.
E>>И "заставить" UI нарисовать это окошко. E>>А если UI не нарисует — значит это плохой UI, его надо переделать. S>"Заставление" UI можно делать десятком способов.
Можете привести примеры?
Например, какие три самых "лучших" способа с точки зрения правильного проектирования?
E>>В чем разница между вызовом обработчика события, который дергает подписчиков, и прямой передачи какой-то команды? E>>Ведь передача команды — это такая же функция. E>>Почему нельзя вместо события вызвать функцию вышестоящего слоя "Забирай отчет и покажи его"?
S>Во-первых, потому, что у одного события может быть одновременно несколько обработчиков.
Ну и функцию можно также реализовать.
S>Во-вторых, потому, что вышестоящий слой может не хотеть "забирать и показывать" отчёт вот прямо вот сейчас. S>Событие — это "вот что произошло", ReportReady. А "функция" — это "вот что я хочу сделать", это ShowReport().
Это назначение метода мы же сами придумываем.
Технически это одно и тоже.
Кроме того, функция может быть асинхронной.
Она может сразу ничего не вернуть. А потом спустя какое-то время вызвать метод BLL.ShowReportFinished()
Все-таки разница не очень понятна.
S>Хочется поделиться чем-то с пользователем — есть стандартный паттерн: S>1. Есть общий для всех BLL-модуль "оповещения о событиях". S>2. Каждый модуль может пользоваться SubmitNotification, который записывает оповещения в очередь. S>4. ... клиенты ... пишут свою реализацию опроса нотификаций поверх конкретно этого модуля.
А чем этот способ отличается от подписки на события?
S>Вот это — нормальный способ реализовать "финансовый отчёт готов".
Как в этом случае, UI найдет среди все очереди нотификаций именно эту — "финансовый отчёт готов"?
Значит, надо вводить какие-то признаки типа NotificationType?
А если например параллельно в BL выполняется расчет двух финансовых отчетов.
В очереди 10 нотификаций.
Как понять какая нотификация к первому отчету, а какая ко второму?
Здравствуйте, es3000, Вы писали: E>Но все-таки уведомления должен вычислить именно бизнес-слой. E>Правильно?
В данном варианте — да. E>А их обработку — UI.
Совершенно необязательно. Может и никто не делать. Никакой зависимости поведения бизнес-логики от того, получено ли уведомление, быть не должно.
E>>>И "заставить" UI нарисовать это окошко. E>>>А если UI не нарисует — значит это плохой UI, его надо переделать. S>>"Заставление" UI можно делать десятком способов. E>Можете привести примеры? E>Например, какие три самых "лучших" способа с точки зрения правильного проектирования?
Я давал подробную инструкцию по работе с уведомлениями в параллельных ветках.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, es3000, Вы писали:
S>>Во-первых, потому, что у одного события может быть одновременно несколько обработчиков. E>Ну и функцию можно также реализовать.
Нет, для этого "функция" должна знать обо всех потребителях оповещения. А стандартные события дотнета сделаны так, чтобы каждый обработчик знал только о событии; ему всё равно, сколько ещё есть обработчиков.
Это упрощает тестирование, т.к. каждого из обработчиков можно тестировать независимо.
Но при наивной реализации подписка на события всё ещё плоха — потому что один "сломанный" обработчик может заклинить всех остальных.
S>>Во-вторых, потому, что вышестоящий слой может не хотеть "забирать и показывать" отчёт вот прямо вот сейчас. S>>Событие — это "вот что произошло", ReportReady. А "функция" — это "вот что я хочу сделать", это ShowReport().
E>Это назначение метода мы же сами придумываем. E>Технически это одно и тоже.
Забудьте про технику. Внутри процессора вообще нет ни методов, ни событий, ни имён функций. Технически все программы эквивалентны машине Тьюринга.
Архитектура — она про семантику.
E>Кроме того, функция может быть асинхронной. E>Она может сразу ничего не вернуть. А потом спустя какое-то время вызвать метод BLL.ShowReportFinished()
Отлично. Вы только что увеличили стоимость разработки ещё втрое. Без малейшего увеличения полезности.
E>А чем этот способ отличается от подписки на события?
Тем, что модуль, порождающий оповещения, ни формально ни фактически не зависит от их потребителей.
Даже если никто не обрабатывает оповещения — ничего не сломается. Если клиент сломался в процессе доставки оповещения — при перезапуске он сможет показать его снова.
UI может вообще отсутствовать в момент порождения оповещения — в понедельник пользователь придёт в офис, запустит программу, и получит все оповещения, сгенерированные за выходные. E>Как в этом случае, UI найдет среди все очереди нотификаций именно эту — "финансовый отчёт готов"?
А зачем ему искать "именно эту"?
Ваш почтовый клиент ищет среди всех писем именно то, которое из банка? Или просто показывает "в инбоксе 180 непрочитанных писем"? E>Значит, надо вводить какие-то признаки типа NotificationType?
Зачем? E>А если например параллельно в BL выполняется расчет двух финансовых отчетов. E>В очереди 10 нотификаций. E>Как понять какая нотификация к первому отчету, а какая ко второму?
Наверное, из содержимого нотификации. А вы как думаете?
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
E>>Как в этом случае, UI найдет среди все очереди нотификаций именно эту — "финансовый отчёт готов"?
S>А зачем ему искать "именно эту"? S>Ваш почтовый клиент ищет среди всех писем именно то, которое из банка? Или просто показывает "в инбоксе 180 непрочитанных писем"?
Пример почтового клиента не подходит.
Почтовый клиент просто принимает письма. Он не выполянет никаких специфических действий.
Я же спрашиваю про такую ситуацию, когда UI инициировал некоторую асинхронную операцию в BL.
И по окончании этой операции должен показать ее результат.
Мы говорили, что когда BL закончит операцию, он сформирует нотификацию.
Но, поскольку операции в BL выполняются асинхронно, например, разными клиентами с разных ПК, то в BL будет много нотификаций в очереди.
Как конкретный клиент найдет именно свою нотификацию о той операции, которую он инициировал?
E>>А если например параллельно в BL выполняется расчет двух финансовых отчетов. E>>В очереди 10 нотификаций. E>>Как понять какая нотификация к первому отчету, а какая ко второму?
S>Наверное, из содержимого нотификации. А вы как думаете?
Это значит, что нотификации должны быть объектами разных классов.
Либо содержать данные в виде объектов-данных разных типов.
Так как данные в нотификации могут быть разными в зависимости от типа операции.
Так?
Здравствуйте, es3000, Вы писали:
E>Пример почтового клиента не подходит. E>Почтовый клиент просто принимает письма. Он не выполянет никаких специфических действий.
И это — правильно.
E>Я же спрашиваю про такую ситуацию, когда UI инициировал некоторую асинхронную операцию в BL. E>И по окончании этой операции должен показать ее результат.
Ну, во-первых, ничего он не должен. Главный тут нe BL и не UI, а пользователь.
Я же вам приводил уже пример — возможно, пользователь сейчас показывает презентацию на большом экране. UI, который вдруг решает "показать результат асинхронной операции" тут нафиг не нужен.
Логичным является сделать специальный раздел в UI, что-то типа "история операций", где видно, какая операция когда была запрошена, каков её прогресс (если известно), когда ожидается завершение (если известно).
Если статус — "готово", то с оповещением могут быть связаны результаты, которые можно увидеть, кликнув по ним.
E>Мы говорили, что когда BL закончит операцию, он сформирует нотификацию. E>Но, поскольку операции в BL выполняются асинхронно, например, разными клиентами с разных ПК, то в BL будет много нотификаций в очереди. E>Как конкретный клиент найдет именно свою нотификацию о той операции, которую он инициировал?
Смотря как вы это спроектируете. Посмотрите на это под другим углом: важно не "какой клиент инициировал операцию", а кто заинтересован в результате.
В простом случае у нас есть личность пользователя. Тогда каждый клиент должен выгребать оповещения обо всех операциях, инициированных текущим пользователем. Независимо, естественно, от того, через какого клиента был выполнен запрос. То есть я могу заказать отчёт с мобильного телефона, а потом сесть за десктоп, и получить оповещение о готовности отчёта.
В более сложном случае у каждого оповещения есть группа заинтересованных пользователей. Например, "готовность квартального отчёта" получает все пользователи с ролью "бухгалтерия".
"исчерпание свободного места в разделе для временных данных" получают все с ролью "администрирование IT ресурсов".
S>>Наверное, из содержимого нотификации. А вы как думаете?
E>Это значит, что нотификации должны быть объектами разных классов. E>Либо содержать данные в виде объектов-данных разных типов. E>Так как данные в нотификации могут быть разными в зависимости от типа операции. E>Так?
Совершенно необязательно. У вас какая версия Windows? Если 10, то вы, наверное, заметили, что их notification center способен показывать оповещения о совершенно разных событиях — и об исчерпании места на диске, и о приходе почты, и вообще о чём угодно. Достаточно сделать так, чтобы внутри оповещения была сохранена "ссылка" на то действие, которое оно предлагает пользователю выполнить.
Для отчёта, к примеру, основным действием будет "открыть окно отчёта", вместе с параметрами, которые указывают на конкретный экземпляр отчёта.
Это позволит вам избежать необходимости полностью переписывать ту часть клиента, которая отвечает за обработку оповещений, после добавления каждого типа оповещений.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
E>>Я же спрашиваю про такую ситуацию, когда UI инициировал некоторую асинхронную операцию в BL. E>>И по окончании этой операции должен показать ее результат.
S>Ну, во-первых, ничего он не должен. Главный тут нe BL и не UI, а пользователь. S>Я же вам приводил уже пример — возможно, пользователь сейчас показывает презентацию на большом экране. UI, который вдруг решает "показать результат асинхронной операции" тут нафиг не нужен. S>Логичным является сделать специальный раздел в UI, что-то типа "история операций", где видно, какая операция когда была запрошена, каков её прогресс (если известно), когда ожидается завершение (если известно). S>Если статус — "готово", то с оповещением могут быть связаны результаты, которые можно увидеть, кликнув по ним.
В общем согласен.
Ну а если все-таки есть такое требование к программе: чтобы программа отображала пользователю окно с результатами выполнения асинхронной BL-операции, когда она завершается.
Как это надо сделать?
E>>... когда BL закончит операцию, он сформирует нотификацию. E>>Но, поскольку операции в BL выполняются асинхронно, например, разными клиентами с разных ПК, то в BL будет много нотификаций в очереди. E>>Как конкретный клиент найдет именно свою нотификацию о той операции, которую он инициировал?
S>Смотря как вы это спроектируете. Посмотрите на это под другим углом: важно не "какой клиент инициировал операцию", а кто заинтересован в результате. S>В простом случае у нас есть личность пользователя. Тогда каждый клиент должен выгребать оповещения обо всех операциях, инициированных текущим пользователем.
Тоже согласен.
Получается, надо нотификации привязывать к пользователю, роли или группе пользователей.
Для простоты давайте рассмотрим случай, когда надо привязываться к пользователю.
Получается, этот параметр надо передавать во все операции BL-слоя, и хранить в каждой нотификации.
Правильно? Как это делается?
Через параметры методов операций?
E>>Это значит, что нотификации должны быть объектами разных классов. E>>Либо содержать данные в виде объектов-данных разных типов. E>>Так как данные в нотификации могут быть разными в зависимости от типа операции. E>>Так?
S>Совершенно необязательно. У вас какая версия Windows? Если 10, то вы, наверное, заметили, что их notification center способен показывать оповещения о совершенно разных событиях — и об исчерпании места на диске, и о приходе почты, и вообще о чём угодно. Достаточно сделать так, чтобы внутри оповещения была сохранена "ссылка" на то действие, которое оно предлагает пользователю выполнить. S>Для отчёта, к примеру, основным действием будет "открыть окно отчёта", вместе с параметрами, которые указывают на конкретный экземпляр отчёта.
S>Это позволит вам избежать необходимости полностью переписывать ту часть клиента, которая отвечает за обработку оповещений, после добавления каждого типа оповещений.
Тут не согласен.
Вы говорите о нотификациях, которые отображаются конечному пользователю.
Мы же начинали разговор о "внутренних" нотификациях, которые создаются BL-слоем для UI-слоя.
UI слой должен, "прочитав" нотификации, понять какая каждая из этих нотификаций к какой инициированной им операции имеет отношение.
И отобразить результаты этих операций.
Пример.
Пользователь1 запустил асинхронные операции: загрузка на Кассу1, выгрузка на Кассу2.
Пользователь2 запустил асинхронные операции: выгрузка на Кассу3, загрузка на Кассу4.
В течении следующих 30 мин эти операции завершились, BL сформировал 7 нотификаций (допустим, что кроме этих 4 операций выполнялось еще каких-то 3 операции, и по ним тоже сформировались нотификации).
Как UI конкретного пользователя поймет какая из нотификаций имеет отношение к запущенной им операции?
По типу (классу) нотификации или по каким-то "универсальным" реквизитам нотификации?
E>>По всем книжкам BLL управляет слоем DAL. E>>BLL прямо "говорит" DAL: дай мне такие-то данные. E>>BLL не оповещает его, не события выкидывает, а дает прямые команды и инструкции DAL-у. E>>Ну конечно через интерфейс-адаптер, но это сути не меняет.
S>да нет такого, не знаю в каких ты это книжках прочитал, но бизнес слой это всегда чистая логика не знающая ни о чем и ни чем не управляющая
А какой же слой читает\записывает данные из БД при помощи DAL-слоя?
Здравствуйте, es3000, Вы писали: E>В общем согласен. E>Ну а если все-таки есть такое требование к программе: чтобы программа отображала пользователю окно с результатами выполнения асинхронной BL-операции, когда она завершается. E>Как это надо сделать?
Точно так же, как описано.
"Главное окно" по таймеру опрашивает метод "получить оповещения", обрабатывает его результаты. Например, для оповещния "отчёт готов" показывает окно с отчётом.
Пользователи будут вас проклинать, но чо уж там.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, es3000, Вы писали: E>Тоже согласен. E>Получается, надо нотификации привязывать к пользователю, роли или группе пользователей. E>Для простоты давайте рассмотрим случай, когда надо привязываться к пользователю. E>Получается, этот параметр надо передавать во все операции BL-слоя, и хранить в каждой нотификации. E>Правильно? Как это делается?
Да. E>Через параметры методов операций?
Конечно.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, es3000, Вы писали: E>Пример. E>Пользователь1 запустил асинхронные операции: загрузка на Кассу1, выгрузка на Кассу2. E>Пользователь2 запустил асинхронные операции: выгрузка на Кассу3, загрузка на Кассу4. E>В течении следующих 30 мин эти операции завершились, BL сформировал 7 нотификаций (допустим, что кроме этих 4 операций выполнялось еще каких-то 3 операции, и по ним тоже сформировались нотификации).
E>Как UI конкретного пользователя поймет какая из нотификаций имеет отношение к запущенной им операции?
Я не вполне понимаю, что значит "поймёт". Например, UI легко получает список нотификаций, привязанных к текущему пользователю. E>По типу (классу) нотификации или по каким-то "универсальным" реквизитам нотификации?
Ну, очевидно, что по классу мы ничего не получим — параллельно может выполняться много операций одного класса.
Есть два способа, которым UI может привязывать нотификацию к инициированной им операции.
Один — по реквизитам. То есть где-то в UI мы заводим список ожидания, в котором, скажем, в момент запуска операции "загрузка на Кассу 1" появляется запись "ждём оповещения об окончании загрузки на Кассу 1". UI при выгребании списка оповещений сравнивает их с теми, что в листе ожидания, и при совпадении активирует заранее заготовленную логику.
Второй — по хэндлу. То есть все асинхронные операции в BL возвращают некоторый handle, по которому потом можно дёрнуть метод GetAsyncOperationStatus.
Этот способ менее надёжен, потому что если UI упадёт во время асинхронной операции, то после перезапуска информация о хэндлах исчезнет, и статус операции проверить будет нельзя.
Способ "по реквизитам" по крайней мере даёт нам возможность просмотреть пришедшие оповещения.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, es3000, Вы писали:
E>Мы же начинали разговор о "внутренних" нотификациях, которые создаются BL-слоем для UI-слоя. E>UI слой должен, "прочитав" нотификации, понять какая каждая из этих нотификаций к какой инициированной им операции имеет отношение. E>И отобразить результаты этих операций.
E>Пример. E>Пользователь1 запустил асинхронные операции: загрузка на Кассу1, выгрузка на Кассу2. E>Пользователь2 запустил асинхронные операции: выгрузка на Кассу3, загрузка на Кассу4. E>В течении следующих 30 мин эти операции завершились, BL сформировал 7 нотификаций (допустим, что кроме этих 4 операций выполнялось еще каких-то 3 операции, и по ним тоже сформировались нотификации).
E>Как UI конкретного пользователя поймет какая из нотификаций имеет отношение к запущенной им операции? E>По типу (классу) нотификации или по каким-то "универсальным" реквизитам нотификации?
Каждый запрос нужно делать транзакционным с уникальным идентификатором.