Ответ 1
Отказ от ответственности: Это длинный ответ, но я думаю, что стоит прочитать все его ссылки. И ИМХО приводит к окончательному ответу.
Я боролся с этими темами последние пару дней, и, если бы я все правильно прочитал, ответ:
Event-driven! == Запрос-ориентированный
"[...] Я нахожу это наиболее интересное различие в событии сотрудничество, перефразировать Jon Udell: программное обеспечение, управляемое запросами когда говорят, управляемое событиями программное обеспечение говорит, когда есть что сказать.
Следствием этого является то, что ответственность за управление государством сдвиги. При запросе сотрудничества вы стремитесь обеспечить, чтобы каждая деталь данных имеет один дом, и вы смотрите его из этого дома, если хотите Это. Этот дом отвечает за структуру данных, как долго это хранится, как получить к нему доступ. В случае сценария совместной работы источник новых данных можно забыть, а второй - передан в его конечную точку сообщения."
Мартин Фаулер - Коллаборация событий (раздел запросов)
Основываясь на этом утверждении, IIRC, современные фреймворки PHP реализуют шаблон Observer + перехватывающие фильтры + Singal и Slots, чтобы инициировать некоторые события во время цикла запроса.
Но, несмотря на то, что он принимает некоторые идеи управляемых событиями архитектур, он, похоже, не поддерживает то, что вся инфраструктура управляется событиями (т.е. Symfony2 является фреймворком, управляемым событиями).
Мы используем деление программ на несколько компонентов, которые сотрудничать вместе. (Я использую неопределенное слово "компонент" здесь сознательно, поскольку в этом контексте я имею в виду много вещей: в том числе объектов внутри программы и нескольких процессов, взаимодействующих через сети.) Наиболее распространенный способ заставить их сотрудничать - это стиль запроса/ответа. Если объект клиента хочет получить некоторые данные из salesman object, он вызывает метод на объекте продавца, чтобы спросить его для этих данных.
Другим стилем сотрудничества является Event Collaboration. В этом стиле у вас никогда не будет одного компонента, который попросит другого сделать что-нибудь, вместо этого каждый компонент сигнализирует о событии, когда что-либо изменяется. Другие компоненты слушают это событие и реагируют, как бы они этого ни пожелали. хорошо известный шаблон наблюдателя является примером совместной работы Event.
Мартин Фаулер - Фокус на события (раздел: Использование событий для сотрудничать)
Я думаю, что приложения PHP более тесно управляются событиями, чем запрос , только когда фокус находится на событиях. Если эти приложения/фреймворки используют события только для сквозных задач (АОП), то это не связано с событиями. Точно так же вы не будете называть его управляемым с помощью теста или управляемым доменом только потому, что у вас есть некоторые объекты домена и модульные тесты.
Примеры реального мира
Я выбрал несколько примеров, чтобы показать, почему эти фреймворки не полностью управляются событиями. Несмотря на события AOP, все управляются запросами:
Примечание. Хотя он может быть адаптирован к событиям
Zend Framework 2
Изучите компонент \Zend\Mvc\Application:
Он реализует \Zend\EventManager\EventManagerAwareInterface и полагается на \Zend\Mvc\MvcEvent, который описывает возможные события:
class MvcEvent extends Event
{
/**#@+
* Mvc events triggered by eventmanager
*/
const EVENT_BOOTSTRAP = 'bootstrap';
const EVENT_DISPATCH = 'dispatch';
const EVENT_DISPATCH_ERROR = 'dispatch.error';
const EVENT_FINISH = 'finish';
const EVENT_RENDER = 'render';
const EVENT_ROUTE = 'route';
// [...]
}
\Zend\Mvc\Application сам по себе управляется событиями, потому что он не сообщает напрямую с другими компонентами, а вместо этого, он просто запускает события:
/**
* Run the application
*
* @triggers route(MvcEvent)
* Routes the request, and sets the RouteMatch object in the event.
* @triggers dispatch(MvcEvent)
* Dispatches a request, using the discovered RouteMatch and
* provided request.
* @triggers dispatch.error(MvcEvent)
* On errors (controller not found, action not supported, etc.),
* populates the event with information about the error type,
* discovered controller, and controller class (if known).
* Typically, a handler should return a populated Response object
* that can be returned immediately.
* @return ResponseInterface
*/
public function run()
{
$events = $this->getEventManager();
$event = $this->getMvcEvent();
// Define callback used to determine whether or not to short-circuit
$shortCircuit = function ($r) use ($event) {
if ($r instanceof ResponseInterface) {
return true;
}
if ($event->getError()) {
return true;
}
return false;
};
// Trigger route event
$result = $events->trigger(MvcEvent::EVENT_ROUTE, $event, $shortCircuit);
if ($result->stopped()) {
$response = $result->last();
if ($response instanceof ResponseInterface) {
$event->setTarget($this);
$events->trigger(MvcEvent::EVENT_FINISH, $event);
return $response;
}
if ($event->getError()) {
return $this->completeRequest($event);
}
return $event->getResponse();
}
if ($event->getError()) {
return $this->completeRequest($event);
}
// Trigger dispatch event
$result = $events->trigger(MvcEvent::EVENT_DISPATCH, $event, $shortCircuit);
// Complete response
$response = $result->last();
if ($response instanceof ResponseInterface) {
$event->setTarget($this);
$events->trigger(MvcEvent::EVENT_FINISH, $event);
return $response;
}
$response = $this->getResponse();
$event->setResponse($response);
return $this->completeRequest($event);
}
Это управляемое событиями: вы не имеете понятия о том, какой код будет использоваться для маршрутизатора, диспетчера и средства визуализации рендеринга, вы знаете, что эти события будут запущены. Вы можете подключить почти любой совместимый компонент для прослушивания и обработки событий. Нет прямой связи между компонентами.
Но есть одна важная вещь, которую нужно отметить: Это уровень представления (Controller + View). Доменный слой действительно может управляться событиями, но это не тот случай, когда почти все приложения вы видите там. ** Там есть смесь между управляемыми событиями и управляемыми запросами:
// albums controller
public function indexAction()
{
return new ViewModel(array(
'albums' => $this->albumsService->getAlbumsFromArtist('Joy Division'),
));
}
Компонент контроллера не управляется событиями. Он напрямую связывается с компонентом службы. Вместо этого службы должны подписываться на события, поднятые контроллерами, которые являются частью уровня представления. (Я укажу ссылки на то, что было бы моделью, управляемой событиями, в конце этого ответа).
Symfony 2
Теперь рассмотрим то же самое на Symfony2 Application/FrontController: \Symfony\Component\HttpKernel\HttpKernel
На самом деле у него есть основные события во время запроса: Symfony\Component\HttpKernel\KernelEvents
/**
* Handles a request to convert it to a response.
*
* Exceptions are not caught.
*
* @param Request $request A Request instance
* @param integer $type The type of the request (one of HttpKernelInterface::MASTER_REQUEST or HttpKernelInterface::SUB_REQUEST)
*
* @return Response A Response instance
*
* @throws \LogicException If one of the listener does not behave as expected
* @throws NotFoundHttpException When controller cannot be found
*/
private function handleRaw(Request $request, $type = self::MASTER_REQUEST)
{
// request
$event = new GetResponseEvent($this, $request, $type);
$this->dispatcher->dispatch(KernelEvents::REQUEST, $event);
if ($event->hasResponse()) {
return $this->filterResponse($event->getResponse(), $request, $type);
}
// load controller
if (false === $controller = $this->resolver->getController($request)) {
throw new NotFoundHttpException(sprintf('Unable to find the controller for path "%s". Maybe you forgot to add the matching route in your routing configuration?', $request->getPathInfo()));
}
$event = new FilterControllerEvent($this, $controller, $request, $type);
$this->dispatcher->dispatch(KernelEvents::CONTROLLER, $event);
$controller = $event->getController();
// controller arguments
$arguments = $this->resolver->getArguments($request, $controller);
// call controller
$response = call_user_func_array($controller, $arguments);
// view
if (!$response instanceof Response) {
$event = new GetResponseForControllerResultEvent($this, $request, $type, $response);
$this->dispatcher->dispatch(KernelEvents::VIEW, $event);
if ($event->hasResponse()) {
$response = $event->getResponse();
}
if (!$response instanceof Response) {
$msg = sprintf('The controller must return a response (%s given).', $this->varToString($response));
// the user may have forgotten to return something
if (null === $response) {
$msg .= ' Did you forget to add a return statement somewhere in your controller?';
}
throw new \LogicException($msg);
}
}
return $this->filterResponse($response, $request, $type);
}
Но помимо того, что он "способен к событиям", он напрямую связывается с компонентом ControllerResolver, поэтому он не полностью управляется событиями с начала процесса запроса, хотя он вызывает некоторые события и позволяет некоторым компонентам быть подключаемыми (что не в случае ControllerResolver, который вводится как параметр конструктора).
Вместо этого, чтобы быть полностью управляемым событиями компонентом, он должен быть как в компоненте Application ZF2:
// Trigger dispatch event
$result = $events->trigger(MvcEvent::EVENT_DISPATCH, $event, $shortCircuit);
Prado
У меня недостаточно времени для изучения исходного кода, но сначала он не создается в SOLID путь. В любом случае, что контроллер на MVC-подобных платформах, Prado называет его TPage (еще не уверен):
http://www.pradosoft.com/demos/blog-tutorial/?page=Day3.CreateNewUser
И он действительно напрямую связывается с компонентами:
class NewUser extends TPage
{
/**
* Checks whether the username exists in the database.
* This method responds to the OnServerValidate event of username custom validator.
* @param mixed event sender
* @param mixed event parameter
*/
public function checkUsername($sender,$param)
{
// valid if the username is not found in the database
$param->IsValid=UserRecord::finder()->findByPk($this->Username->Text)===null;
}
[...]
}
Я понимаю, что TPage
является прослушивателем событий и может быть подключаемым. Но это не приводит к тому, что ваша модель домена управляется событиями. Поэтому я думаю, что в некоторой степени это ближе к предложению ZF2.
Примеры, управляемые событиями
Чтобы закончить этот длинный ответ, вот что будет иметь полномасштабное приложение, управляемое событиями:
Развязка приложений с доменами http://www.whitewashing.de/2012/08/25/decoupling_applications_with_domain_events.html
Поиск источников событий http://martinfowler.com/eaaDev/EventSourcing.html
Шаблон событий домена http://martinfowler.com/eaaDev/DomainEvent.html
Сотрудничество с событиями http://martinfowler.com/eaaDev/EventCollaboration.html
Перехват событий http://martinfowler.com/bliki/EventInterception.html
Конечная точка сообщения http://www.enterpriseintegrationpatterns.com/MessageEndpoint.html
... и т.д.