19

Symfony2、ZendFramework2などはイベント駆動型であると多くの人が言っています。

デスクトップの世界では、イベント駆動型プログラミングによって、アプリケーションが状態が変化するたびにオブザーバーに通知することを理解しています。

PHPアプリケーションはステートレスであるため、そのようなことを行う方法はありません。IEユーザーがインターフェイスを使用している間、オブザーバーをビューに関連付けて変更を監視します。代わりに、ビューを更新するために新しいリクエストプロセスが必要です。つまり、これはイベントではなく、まったく新しいリクエストです。

一方、同様の概念があります。イベント駆動型アーキテクチャです。

ここで両方を読むことができます:

http://en.wikipedia.org/wiki/Event-driven_programming

http://en.wikipedia.org/wiki/Event-driven_architecture

そしてここにもう1つ:

http://en.wikipedia.org/wiki/Signal_programming

シグナルは、イベントが発生したことをプロセスに通知するものです。信号は、ソフトウェア割り込みとして説明されることがあります。信号は、プログラムの通常の実行フローを中断するという点で、ハードウェア割り込みに類似しています。ほとんどの場合、信号がいつ到着するかを正確に予測することはできません。

  • Stackoverflow[singals]タグの説明

さらに、私が以前イベントドリブンと呼んでいたものは、Qt(オブザーバーパターンの実装)によって導入されたシグナルとスロットパターンに関連しているようです。

例として、イベント駆動型であると主張するPradoフレームワークがあります。

http://www.pradosoft.com/demos/quickstart/?page=Fundamentals.Applications(アプリケーションライフサイクルセクション)

http://www.pradosoft.com/docs/manual/System/TApplication.html#methodonEndRequest

IIRC、これはイベント駆動型アプリケーションではなく、代わりにを実装するクラスによって使用されるプラグインフック(信号とスロット)observable Interfaceです。つまり、デスクトップアプリケーションがイベントを使用する方法と、ステートレスアプリケーションがイベントを使用する方法(プラグインとして)を考慮すると、ビューを含むアプリケーション全体で最初にイベントが使用され、最後はサーバー側の操作でのみ使用されます。

1つはアスペクト指向プログラミング(信号とスロットを使用)に関連しており、もう1つは横断的関心事/AOPに特に関係していません。言い換えれば、それはアプリケーションの状態により関連しています。

では、これらの用語の実際の関係と、それらの用語の違いは何でしょうか。

  1. イベント駆動型プログラミング
  2. イベント駆動型アーキテクチャ
  3. 信号とスロットのパターン

これらの用語は単なる一般的なパターンですか?したがって、オブザーバーパターンを実装するものはすべて、イベント駆動型と見なすことができますか?

アップデート

Zend Framework 2

上記でリンクしたAOPに関する記事(http://mwop.net/blog/251-Aspects,-Filters,-and-Signals,-Oh,-My!.html)は、Matthew Weier O'Phinney( ZFリーダー)。IIRC、「イベント駆動型」については言及されておらず、信号とスロットのみが言及されています。

Symfony 2

Symfony2EventDispatcherコンポーネントの説明には、「イベント駆動型」アプリケーション向けについての言及はありません。http: //symfony.com/doc/current/components/event_dispatcher/introduction.html 「イベント」への参照のみが含まれています(実際、 、SignalとSlotsによって処理されます)。

どちらのフレームワークも、リクエストプロセス中に同期イベントを処理するために、シグナルとスロット内でインターセプトフィルターパターンを使用しているようです。

4

2 に答える 2

16

免責事項:それは長い答えですが、すべての参照とともに読む価値があると思います。そして私見それは決定的な答えにつながります。

私はこの数日間、このテーマに苦労してきました。すべてを正しく読んだ場合、答えは次のとおりです。

イベント駆動型!==リクエスト駆動型

「[...]これはイベントコラボレーションの最も興味深い違いだと思います。言い換えると、Jon Udell:要求駆動型ソフトウェアは話しかけられたときに話し、イベント駆動型ソフトウェアは何か言いたいことがあるときに話します。

この結果、州の管理責任が変化します。リクエストコラボレーションでは、すべてのデータに1つのホームがあることを確認し、必要に応じてそのホームから検索します。この家は、データの構造、データの保存期間、データへのアクセス方法を担当します。イベントコラボレーションのシナリオでは、新しいデータのソースは、メッセージエンドポイントに渡された2番目のデータを忘れることができます。」

マーティンファウラー-イベントコラボレーション(クエリセクション)

そのアサーション、IIRCに基づいて、最新のPHPフレームワークは、リクエストサイクル中にいくつかのイベントをトリガーするために、オブザーバーパターン+インターセプトフィルター+SingalおよびSlotsを実装します。

しかし、イベント駆動型アーキテクチャのいくつかのアイデアを採用しているという事実にもかかわらず、フレームワーク全体がイベント駆動型であることをサポートしていないようです(つまり、Symfony2はイベント駆動型フレームワークです)。

私たちは、プログラムを複数のコンポーネントに分割し、それらを連携させることに慣れています。(ここでは、あいまいな「コンポーネント」という言葉を意図的に使用しています。これは、プログラム内のオブジェクトやネットワークを介して通信する複数のプロセスなど、多くのことを意味するためです。)それらを連携させる最も一般的な方法は、要求/応答スタイルです。 。顧客オブジェクトがセールスマンオブジェクトからのデータを必要とする場合、セールスマンオブジェクトのメソッドを呼び出して、そのデータを要求します。

コラボレーションのもう1つのスタイルは、イベントコラボレーションです。このスタイル では、あるコンポーネントが別のコンポーネントに何かを要求することはありません。代わりに、各コンポーネントは、何かが変更されたときにイベントを通知します。他のコンポーネントはそのイベントをリッスンし、希望どおりに反応します。よく知られているオブザーバーパターンは、イベントコラボレーションの例です。

マーティンファウラー-イベントに焦点を当てる(セクション:イベントを使用したコラボレーション)

PHPアプリケーションは、イベントに焦点が当てられている場合にのみ、要求駆動型よりもイベント駆動型に近いと思います。それらのアプリケーション/フレームワークが横断的関心事(AOP)のイベントのみを使用している場合、それはイベント駆動型ではありません。同様に、いくつかのドメインオブジェクトと単体テストがあるという理由だけで、テスト駆動またはドメイン駆動とは呼ばないでしょう。

実例

これらのフレームワークが完全にイベント駆動型ではない理由を示すために、いくつかの例を選びました。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);
}

これはイベント駆動型です。どのルーター、ディスパッチャー、ビューレンダラーが使用されるかを確認する手がかりはなく、これらのイベントがトリガーされることだけがわかります。互換性のあるほとんどすべてのコンポーネントをフックして、イベントをリッスンして処理できます。コンポーネント間の直接通信はありません。

ただし、注意すべき重要な点が1つあります。これは、プレゼンテーション層(Controller + View)です。ドメイン層は確かにイベント駆動型にすることができますが、そこにあるほとんどすべてのアプリケーションには当てはまりません。**イベント駆動型とリクエスト駆動型の間には混合があります:

// albums controller
public function indexAction()
{
    return new ViewModel(array(
        'albums' => $this->albumsService->getAlbumsFromArtist('Joy Division'),
    ));
}

コントローラコンポーネントはイベント駆動型ではありません。サービスコンポーネントと直接通信します。代わりに、サービスは、プレゼンテーション層の一部であるコントローラーによって発生したイベントをサブスクライブする必要があります。(この回答の最後に、イベント駆動型ドメインモデルとなるものについての参照を指摘します)。

Symfony 2

それでは、Symfony2アプリケーション/フロントコントローラーで同じことを調べてみましょう:\ 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の)。

代わりに、完全にイベント駆動型のコンポーネントであるためには、ZF2アプリケーションコンポーネントのようにする必要があります。

    // Trigger dispatch event
    $result = $events->trigger(MvcEvent::EVENT_DISPATCH, $event, $shortCircuit);

プラド

ソースコードを調べる時間がありませんが、最初はしっかりした方法で構築されていないようです。いずれにせよ、MVCに似たフレームワークのコントローラーとは、プラドはそれを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's 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/decouple_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

... 等々

于 2012-09-02T23:55:10.537 に答える
9

PHPはステートレスではなく、HTTPはステートレスです。簡単に言うと、ステートレステクノロジーの上に基本的にレイヤーを構築し、その上にステートフルデザインを実装できます。まとめると、PHPと選択したデータストアには、セッションのトークン化を介してイベント駆動型パターンに基づいてアプリケーション設計を構築するために必要なすべてのツールがあります。

非常に一般的な方法で、HTTPは、デスクトップコンピューティング用のBIOSと同じようにWeb用であると考えることができます。実際、これをもう少し先に進めると、Webの暗黙のイベント駆動型の性質を簡単に確認できます。「イベントではなく、まったく新しいリクエスト」とおっしゃっていましたが、「まったく新しいリクエストイベントです」という意味で、デザインパターンの意味で。これは、ユーザーのアプリケーションとの対話に関連する具体的な意味を持っています。

基本的に、MVCやフロントコントローラーなどのパターンを介して(およびHTTP CookieとPHPセッションのメカニズムによって)、セッションの状態を復元し、イベントに応答して、それに応じてその状態を変更します。

私はRESTの本質であるRepresentationalStateTransferを考えたいと思います...しかし、UIイベントが発生したときにのみ状態が転送されるという暗黙の意味を忘れてはならないことを付け加えておきます。したがって、モデルの「表現状態」(つまり、ドキュメント、JSONなど)でのみ「話す」HTTPとの契約を維持しますが、それは私たちの方言にすぎません。他のシステムは、キャンバス座標、信号デシベルなどで話すことを選択します。

編集/その他の考え

それで、私はしばらくそれを熟考してきました、そして、HTTPを介してPHPの領域でこれらのパターンを議論するときの曖昧さの少しを説明する概念があると思います:決定論。具体的には、リクエストを受信すると、PHPの実行パスが決定論的になります。そのため、PHPで「イベント駆動型」アーキテクチャを検討することは非常に困難です。私の考えでは、ユーザーとの対話のより大きな「セッション」に対して、PHPよりも1レベル高いと見なす必要があります。

デスクトップコンピューティングでは、runloopsとステートフルコンテキストを使用してイベントを「待機」します。ただし、Webは実際にはこのアーキテクチャ(ほとんどの場合)を改善したものですが、最終的には同じパターンであると主張します。実行ループと無限期間の状態の代わりに、イベントが発生したときに状態をブートストラップしてから、そのイベントを処理します。そして、その状態をメモリに保持して次のイベントを待つのではなく、その状態をアーカイブしてリソースを閉じます。ある意味では効率が低いと見なすことができますが(「イベント」ごとに状態をロードする必要がある)、理由もなくメモリにアイドル状態がないという点で効率が高いと言えます。実際に消費/操作されている状態のみをロードします

したがって、このように、HTTPを介したPHPは、マクロレベルでイベント駆動型であると考えてください。ただし、特定の実行は実際に決定論的であり、実際にはイベント駆動型ではありません。ただし、フロントコントローラーとMVCパターンを実装して、アプリケーション開発者に駆動フックの使い慣れた構造を提供できるようにします。適切なフレームワークを使用している場合は、「ユーザーがいつ登録するかを知りたいので、その時点でユーザーを変更できるようにする必要があります」と言うだけです。これはイベント駆動型の開発です。フレームワークが(ほぼ)フックを呼び出すという唯一の目的のために環境をブートストラップしたことを心配する必要はありません(環境がすでに存在し、単に存在していたというより伝統的な概念とは対照的です)イベントの通知)。これが、イベント駆動型の方法でPHPを開発することの意味です。コントローラーは(要求に基づいて)発生しているイベントを判別し、使用するように設計されたメカニズム(つまり、オブザーバーパターン、フックアーキテクチャなど)を使用して、コードがイベントを処理したり、イベントに応答したり、その他の命名法を使用できるようにします。特定のフレームワークのセマンティクスに最も適しています。

于 2012-09-02T05:11:31.443 に答える