например, есть класс User с кучей полей, и он прекрасно пишется и читается из базы.
Возникает задача — сделать некое действие на юзером, например, сделать его активным, чтобы он мог пользоваться всеми благами в системе.
user.setActive(true) — недостаточно, потому что действие по активации включает в себя кучу действий типа :
* уведомление юзера
* запись события в системный журнал
* и тп.
Вопрос — где место всей этой логике?
На данный момент используется такой подход — имеется абстрактный класс Action, который и выполняет подобные действия.
ActivateUserAction extends Action.
То есть мы разделили собственно ORM с сохранением простых объектов и бизнес действия.
Но тут возникает проблема — эти Action'ы начинают плодиться кучами и уже есть необходимость задуматься о новом уровне абстракции всего этого.
Что можете предложить, коллеги?
Здравствуйте, chernyh, Вы писали:
C>Hi All!
C>Имеется приложение с ORM.
C>например, есть класс User с кучей полей, и он прекрасно пишется и читается из базы.
C>Возникает задача — сделать некое действие на юзером, например, сделать его активным, чтобы он мог пользоваться всеми благами в системе. C>user.setActive(true) — недостаточно, потому что действие по активации включает в себя кучу действий типа : C>* уведомление юзера C>* запись события в системный журнал C>* и тп.
C>Вопрос — где место всей этой логике?
С точки зрения ООП "Сделать активным" — не поведение юзера. Это поведение того, кто управляет юзерами. То есть создается класс UserManager, который содержит в себе необходимые методы работы с юзерами.
C>На данный момент используется такой подход — имеется абстрактный класс Action, который и выполняет подобные действия. C>ActivateUserAction extends Action. C>То есть мы разделили собственно ORM с сохранением простых объектов и бизнес действия. C>Но тут возникает проблема — эти Action'ы начинают плодиться кучами и уже есть необходимость задуматься о новом уровне абстракции всего этого.
Один Action — одно действие? Это имхо перебор. Лучше один класс с несколькими методами.
Спасибо, идея понятна.
G>Один Action — одно действие? Это имхо перебор. Лучше один класс с несколькими методами.
тут возникают сомнения — такой класс с несколькими методами в итоге вырастает в страшного монстра с методами на все случаи жизни...
Здравствуйте, chernyh, Вы писали:
C>Спасибо, идея понятна.
G>>Один Action — одно действие? Это имхо перебор. Лучше один класс с несколькими методами. C>тут возникают сомнения — такой класс с несколькими методами в итоге вырастает в страшного монстра с методами на все случаи жизни...
1 Акшен на 1 Клас это класно. Но я бы в этом случае таки сделал бы фасад. Иначе у пользователей вашего АПИ не будет единой точки поиска нужной активити.
Здравствуйте, Mike Chaliy, Вы писали: C>>тут возникают сомнения — такой класс с несколькими методами в итоге вырастает в страшного монстра с методами на все случаи жизни... MC>1 Акшен на 1 Клас это класно. Но я бы в этом случае таки сделал бы фасад. Иначе у пользователей вашего АПИ не будет единой точки поиска нужной активити.
Что-то типа UserFacade с методами activate() и тп?
Чем это отличается от UserManager?
Я что-то не так понял?
Здравствуйте, chernyh, Вы писали:
C>Что можете предложить, коллеги? C>
В ДДД такие проблемы решаються при помощи сервисов. Там было бы что-то наподобие вашего акшена тока называлось бы ЮзерАктиваторСервис (или ЮзерАктивейшенСервис, ).
Если бы было время и желание я бы пошел чуть дальше. Я бы попробовал реализовать пабилш/субскрайбер. В таком случае ЮзерАктиваторСервис должен был бы всех заинтересованных оповестить что что ЮзерАктивирован. А уже все кто в этом заинтерсован это обработали. Кто-то послал нотификейшен, кто-то записал в лог, кто-то начислил бонус. Но я обычно этого не реализовываю ввиду некторой сложности. Есть готовые продукты типа nServiceBus (или недавно Rhino ServiceBus, но у него вообще зверские требования), но всерано пока что сложно...
Здравствуйте, chernyh, Вы писали:
C>Здравствуйте, Mike Chaliy, Вы писали: C>>>тут возникают сомнения — такой класс с несколькими методами в итоге вырастает в страшного монстра с методами на все случаи жизни... MC>>1 Акшен на 1 Клас это класно. Но я бы в этом случае таки сделал бы фасад. Иначе у пользователей вашего АПИ не будет единой точки поиска нужной активити. C>Что-то типа UserFacade с методами activate() и тп? C>Чем это отличается от UserManager? C>Я что-то не так понял?
Тока тем что
1) у UserFacade не будет физически милиона зависимосетей. Они будут у акшенов.
2) UserFacade не будет мостроидальным, он будет тока менджером активитей. Фактически это будет обозначать что размер это класса буде [КоличествоАктивитией] * [КличествоСтрокПодготовкиАктивити].
Почему мне болльше нравяться подходы 1 Акшен на 1 Клас, потому что код выходит значительно более структурированным. Ну и не выходить тупых стейтлес классов. Так как очевидно что в активити я могу свободно юзать филды. Ну и в теории такой код проще реюзать, паралелить, декомпозить активити на субактивити, делать АОП(достаточно один раз добоавить в базовый класс нужный функционал, а не плясать с бубно вокруг каждого метода), ексепшен хендлинг и так далее итого подобное, я уже начинаю повторяться.
Здравствуйте, chernyh, Вы писали:
C>Hi All!
C>Имеется приложение с ORM. C>например, есть класс User с кучей полей, и он прекрасно пишется и читается из базы. C>Возникает задача — сделать некое действие на юзером, например, сделать его активным, чтобы он мог пользоваться всеми благами в системе.
...
C>Но тут возникает проблема — эти Action'ы начинают плодиться кучами и уже есть необходимость задуматься о новом уровне абстракции всего этого. C>Что можете предложить, коллеги? C>
1. "1 Action — 1 Действие" — это хорошо и правильно. Action есть инкапсулированный запрос со всеми вытекающими отсюда. Включая и то, что со временем, при увеличении количества Actions, вам понадобится такие объекты инфраструктуры, как фабрика запросов и процессор запросов — будьте к этому готовы.
2. Сам Action не должен реализовывать сколько-нибуль сложные действия — за ними ему следует обращаться в слой сервисов. В зависимости от избранной "тяжести" *Action, объект ActivateUserAction может самостоятельно тянуться в слой сервисов и вызывать всё, что надо и в нужной последовательности, или делегировать это фасаду IUserService. Объект IUserService (операции с пользователями) и будет новым уровнем абстракции. При этом в любом случае у вас User ничего не будет знать про IUserService — про него знает сам ActivateUserAction.
Здравствуйте, chernyh, Вы писали:
C>Спасибо, идея понятна.
G>>Один Action — одно действие? Это имхо перебор. Лучше один класс с несколькими методами. C>тут возникают сомнения — такой класс с несколькими методами в итоге вырастает в страшного монстра с методами на все случаи жизни...
Если вы начинаете проектирование с функционала системы, то ничего никуда у вас не выростает.
У вас изнчально будут классы с наборами методов (как всегда 7±2 методов), например для работы с пользователями.
Здравствуйте, Mike Chaliy, Вы писали:
MC>Здравствуйте, chernyh, Вы писали:
C>>Что можете предложить, коллеги? C>>
MC>В ДДД такие проблемы решаються при помощи сервисов. Там было бы что-то наподобие вашего акшена тока называлось бы ЮзерАктиваторСервис (или ЮзерАктивейшенСервис, ).
MC>Если бы было время и желание я бы пошел чуть дальше. Я бы попробовал реализовать пабилш/субскрайбер. В таком случае ЮзерАктиваторСервис должен был бы всех заинтересованных оповестить что что ЮзерАктивирован. А уже все кто в этом заинтерсован это обработали. Кто-то послал нотификейшен, кто-то записал в лог, кто-то начислил бонус. Но я обычно этого не реализовываю ввиду некторой сложности. Есть готовые продукты типа nServiceBus (или недавно Rhino ServiceBus, но у него вообще зверские требования), но всерано пока что сложно...
Не хочу спорить, но я думаю, что использовать MOS (Message-Oriented Software) типа NServiceBus, да еще и завязываться на него, в проекте, у которого границы обозримы, иммет мало смысла Rhino Service Bus еще настолько сырой, что страшно пробовать. Да и у Ayende вряд ли найдется время, чтобы его поддерживать
И еще — мне-таки кажется, что SB/ESB реализует модель, отличную от publisher/subscriber; похожую, да, но не ее.
Здравствуйте, meowth, Вы писали:
M>Не хочу спорить, но я думаю, что использовать MOS (Message-Oriented Software) типа NServiceBus, да еще и завязываться на него, в проекте, у которого границы обозримы, иммет мало смысла Rhino Service Bus еще настолько сырой, что страшно пробовать. Да и у Ayende вряд ли найдется время, чтобы его поддерживать
Собсно если вам не нравяться эти монстрики, паб/саб не сложно реализовать и ручками. Что мне нравиться в этих монстриках, это отложенная обработка прямо в коробке. Реально пользователю не иммет смысла например ждать пока разошлються нотификиены полутора сотни заинтересованных менеджеров....
M>И еще — мне-таки кажется, что SB/ESB реализует модель, отличную от publisher/subscriber; похожую, да, но не ее.
publisher/subscriber — это одна из фитч которую ти продукты потдерживают. Я бы не стал спорить про идеологичискую семантику pub/sub на SB/ESB...
Здравствуйте, Mike Chaliy, Вы писали:
MC>Собсно если вам не нравяться эти монстрики, паб/саб не сложно реализовать и ручками.
Отвечу вашей цитатой: >>Но я обычно этого не реализовываю ввиду некторой сложности.
Здесь я с вами полностью согласен — реализовывать их вручную та еще песня. Как альтернативу могу указать на возможности, предлагаемые Spring'ом по части интеграции с очередями сообщений — тем более, что у автора Java. Для дотнету — Spring.NET, соответственно. В настоящее время занят как раз таким.
M>>И еще — мне-таки кажется, что SB/ESB реализует модель, отличную от publisher/subscriber; похожую, да, но не ее. MC>publisher/subscriber — это одна из фитч которую ти продукты потдерживают. Я бы не стал спорить про идеологичискую семантику pub/sub на SB/ESB...
Здравствуйте, meowth, Вы писали:
M>Здесь я с вами полностью согласен — реализовывать их вручную та еще песня. Как альтернативу могу указать на возможности, предлагаемые Spring'ом по части интеграции с очередями сообщений — тем более, что у автора Java. Для дотнету — Spring.NET, соответственно. В настоящее время занят как раз таким.
Аха, я setActive не заметил . Spring.NET — да она у меня уже давно списке поглядеть. Собсно я собирался тока предложить идею, а не конкретную реализацию. И вполне чесно наисал что автору саому думать нужно это или нет. Я работаю над системой в кторой очень большая нагрузка. И нам такое маштабирование очень даже не мешает .
Здравствуйте, chernyh, Вы писали:
C>Hi All!
C>Имеется приложение с ORM.
C>например, есть класс User с кучей полей, и он прекрасно пишется и читается из базы.
C>Возникает задача — сделать некое действие на юзером, например, сделать его активным, чтобы он мог пользоваться всеми благами в системе. C>user.setActive(true) — недостаточно, потому что действие по активации включает в себя кучу действий типа : C>* уведомление юзера C>* запись события в системный журнал C>* и тп.
возможно подойдет AOP, если у вас подобные доп. действия (audit, notifications) проходят через всю логику системы.
C>Вопрос — где место всей этой логике? C>На данный момент используется такой подход — имеется абстрактный класс Action, который и выполняет подобные действия. C>ActivateUserAction extends Action. C>То есть мы разделили собственно ORM с сохранением простых объектов и бизнес действия. C>Но тут возникает проблема — эти Action'ы начинают плодиться кучами и уже есть необходимость задуматься о новом уровне абстракции всего этого. C>Что можете предложить, коллеги? C>
Здравствуйте, chernyh, Вы писали:
C>Hi All!
C>Но тут возникает проблема — эти Action'ы начинают плодиться кучами и уже есть необходимость задуматься о новом уровне абстракции всего этого. C>Что можете предложить, коллеги?
Возможны несколько вариантов:
1. Для объединения нескольких action в одно действие очень удобно использовать конвейеры, т.е. выполняющиеся один за другим последовательно action, в которых с выхода одного передается на вход следующего. Позволяет сократить захардкоденые action. Например, выбор из справочника по кнопке в поле будет последовательностью: сформировать ограничения для справочника, выбрать данные справочника, создать визуализатор справочника, выложить и отобразить модальную форму, сохранить результат в поле.
2. Когда много action являются одной подсистемой удобно подключать их к объектам сразу всем скопом. Т.е. получается такой своебразный package, содержащий action и дополнительные поля или свойства (можно их еще объединить в аспекты) для подсистемы. Например, если мы хотим реализовать версионность данных, то модифицировать нужно как объекты доступа к данным (процесс загрузки данных и сохранения), так и внешний вид представлений пользовательского интерфейса.
Делаем package и подключаем его как на сервере приложений, так и на клиенте.
3. Для структуризации удобно использовать именования через точку типа namespace в # или package в Java. Основная проблема тут — проблема найти уже существующий action для нового блока системы. Структуризация решает эту проблему.
4. Очень удобно декларативно задавать все вышеперечисленное и хранить в объектном persistent хранилище.