技術の話をするところ2016-09-09FluxとDDDの統合方法おはこんばんにちは、かとじゅんです。久しぶりにブログを書く...。最近、趣味でAngular2やらReactやらやっています。Webpackになれました...。今回のお題は「FluxとDDDの統合方法」について。Angular2を先に触っていましたが、FluxといえばやはりReactだろうということで途中で浮気してReactで考えています。Angular2でもできるはずですが、今回はReactで統合方法*1について考えてみたいと思います。一つ断っておくと、FluxはDDDと統合することを想定していない設計パターンなんで云々とかはここでは考えていません。それはこのブログ記事を読む読まないに関わらずご自身で判断されてください。ソースコードについては、Githubへのリンクを一番下に書いてあるので興味がある人は参考にしてみてください。本家曰く、クライアントサイドアプリケーションを構築するためにFacebookが使っているアプリケーションアーキテクチャ。図を見ればわかるように一方向のデータフローを提供するのが特徴です。Facebookが開発しているので、Reactと一緒に使われることが多いと思いますが、フレームワークというより設計パターンのようなものです。Fluxの各コンポーネントの僕の解釈からということで以下を参照。Storeをみていきましょう。Storeってなんぞやって話が多いので整理します。Fluxについて解釈が間違っているところがあれば指摘してもらえるとうれしいです。Creatorはメソッドのパラメータからアクションを生成する、ヘルパーメソッドの集合。Actionにはタイプがアサインされ、それをDispatcherに提供する。DispatcherすべてのActionは、StoreがDispathcerに登録したコールバックを経由してすべてのStoreに送られる。すべてのActionはStoreがDispatcherに登録したコールバックを経由してStoreに送られる。Dispatcherは、Fluxアプリケーションのためのデータフローを管理する中央ハブ的な存在で、アプリケーション固有の要件を含まないシンプルな配送システム。Creatorは新しいActionをDispatcherに提供する。技術的な要件しか含まなそう。わかんねーと言われるもの。ストアがアクションへのレスポンス処理でストア自身を更新した後、変更イベントを送信する。Storeはアプリケーション状態とアプリケーションロジックを表すもの。MVCのモデル相当だが、たくさんのオブジェクト状態を管理する。ORMモデルような単一のモデルを表現しない。TodoStoreはTodoアイテムのコレクションを管理するものに似ている。Storeはモデルのコレクションと論理領域のシングルトンなモデルの両方の表現する。StoreはDispatcherを使って自分自身の登録とコールバックを提供する。コールバックはパラメータとしてActionを受け取る。Storeに登録されたコールバックの内部にある、アクションタイプに基づくswitch文がActionを解釈するために使われ、適切なフックを提供する。これによって、Dispatcher経由でStoreが持つアプリケーション状態を更新できるようになる。Storeが更新された後は、更新イベントがブロードキャストされ、ビューが新しい状態をStoreに問い合わせたり、ビュー自体を更新するかもしれない。ここでわかることは、StoreはMVCのモデル相当ということと、アプリケーション状態とアプリケーションロジックを含むということです。このモデルの振る舞いはコールバックの中にあるということになります。まとめると、ActionはメッセージとしてDispatcherに渡すと、ActionはDispatcher経由でStoreに送られる。Storeはアプリケーション状態を持っていて、Actionに応じた振る舞いを起こし状態を変化させる。状態変化が起こるとViewに通知される。更新の通知を受け取ったViewはStoreから状態を取り出したり、自分自身の状態を更新する可能性がある。いうことになると思います。ビジネスの概念と、ビジネスが置かれた状況に関する情報、およびビジネスルールを表す責務を負う。ビジネスの状況を反映する状態はここで制御され使用されるが、それを格納するという技術的な詳細は、インフラストラクチャに委譲される。この層がビジネスソフトウェアの核心である。ビジネスという言葉は少し大げさに聞こえるかもしれません。簡単に言い換えるならば、そのソフトウェアで解決すべき関心事とか知識という意味です。当てはまるかもしれません。自分が担当しているTodoが今どれだけあるか、期限切れしているTodoは何かなどの状態を保持しています(状態の保持についての技術的都合はこの層の責務外)。いう意味では、ソフトウェアのコアはドメインです。詳しくいうと、他のレイヤーのすべてのコンポーネントが直接的か間接的かによらずドメイン層に依存します。AngularやReactなどのフレームワークによって実装されるアプリケーションであっても、ドメイン層に従います。ソフトウェアが行うことになっている仕事を定義し、表現力豊かなドメインオブジェクトが問題を解決するように導く。とって意味があるものか、あるいは他システムのアプリケーション層と相互作用するのに必要なものである。このレイヤは薄く保たれる。ビジネスルールや知識を含まず、やるべき作業を調整するだけで、実際の処理は、ドメインオブジェクトによって直下のレイヤで実行される共同作業に委譲する。ビジネスの状況を反映する状態は持たないが、ユーザやプログラムが行う作業の進捗を反映する状態を持つことはできる。アプリケーション層もわかりにくいものの一つかもしれませんが、ドメインモデルの状態や振る舞いを協調動作させてアプリケーション上の一つのユースケースを実現するものです。チャットの部屋で、誰かにメンションすると、チャットというドメインモデルを更新するだけではなく、To先のユーザアカウントのデバイスセッションを探し、通知しなければならないかもしれません。これがアプリケーションロジックと言われるもので、多くの場合はユースケースを担うアプリケーションサービスの責務となります。DDDではアプリケーション状態はドメイン層が握っていますが、もう少し詳細に述べると、集約とリポジトリというものが担っています。集約は内部にドメインモデルが複数格納されていて、それらの複数の関連をひとまとまりとして表現しています。集約を永続化する際はリポジトリに依頼します。対応する永続化デバイスもRDB版、KVS版、API版など様々ですが、外からみるとMapのように見えるインターフェイスを持っています。リポジトリが存在する理由としては、ドメインモデルはソフトウェアで解決する関心事をそのまま表現するため、永続化などの技術都合はリポジトリに移譲すべきという考えからきています。FluxへのDDDの統合方法では、Fluxとの統合について話しを先に進めます。FluxのStoreはリポジトリに似たような責務を持っているのは上記で述べた通りです。どちらもアプリケーション状態を保持する責務を担います。FluxのStoreにはアプリケーションロジックも含まれています。リポジトリには集約の永続化責務しかありません。DDDではアプリケーションロジックはドメインモデルを協調動作させるアプリケーションサービスの役割でした。集約の協調動作(=アプリケーションサービス)と考えることができます。この時点でStoreはアプリケーション層にあると考えられます。ビジネスロジック付きの永続化アプリケーションサービスのようなものとして解釈することができます。単純に統合するならば、FluxのStore内部でリポジトリと集約を使って、アプリケーション状態とアプリケーションロジックを実装すれば、FluxやDDDの考え方から逸脱せずに統合することが可能です。以下が統合イメージです。最初に考えたものから少し変わっていますが、根本は変わっていません。CreatorとViewを、アプリケーション層にはDispatcherとStoreを、ドメイン層にはビジネス上の概念を表す集約(ドメインモデルをカプセル化したもの)とリポジトリを、それ以外の汎用的なものはインフラストラクチャ層に配置します。アプリケーション状態を更新したり、取得したりする際に、Store経由でリポジトリと集約を利用します。それ以外の要件(たとえばドメインモデルとは関係がないRPC呼び出しなど)は別の方法で実現することを考えていますが、長くなるので後日にします。Fluxを踏まえて各層をどのように実装するか提案します。ドメイン層まず最初に集約を定義します。この集約は、ビジネス上の利害関係者の頭の中にある概念なので、実現方法である技術要素からできる限り独立している方がよいです。ここではインフラストラクチャ(要件の影響を受けない汎用的技術基盤)となる言語機能にしか依存しないように設計します。今回のドメインモデルはただのデータの入れ物と化していて貧血症になっています。本来はビジネス上の豊富な知識を投影したドメインモデルとなるようにしなければいけませんが、今回の趣旨とは外れるのでご容赦くださし...。}}次にこの集約の永続化を担うリポジトリを実装します。リポジトリは集約のインスタンスを保存したり検索したりすることができます*2。インターフェイスだけを見ると集約のコレクションのように見え、内部の実装はMapであったり、ローカルストレージであったり、APIサーバであったり様々なものが実装できます。APIサーバから集約をI/Oしたいなら、内部の実装をHTTPクライアントに切り替える必要があるでしょう。}}アプリケーション層Storeの前にStateについて説明します。Stateには画面の入力状態以外にビューに出力するための状態として、先ほど定義したリポジトリを含めるようにします*3。};}次はStoreです。今回はFluxUtilsのReduceStoreで実装します。肝心な部分はreduceメソッドとcreateTodoメソッドです。このコードから、アプリケーション状態はリポジトリが担っていることがわかります。今回のTodoは振る舞いがないので微妙ですが、複雑な要件であればこのドメインモデルに設計の意図を十分に表せる振る舞いが実装されるはずです。TodoStore(todoDispatcher);Dispatcherが抜けていた...。Flux.Dispatcher<TodoAction>();UI層いよいよ、Reactのコンポーネントの結合ですが、最初にビューモデルを作ります。このモデルはビューに関連する知識を表しています。集約などのドメインモデルは、実際のビジネスなどの問題領域の概念と対応付きますが、ビューでは異なる表現形式が必要になる場合があります。集約のモデルを画面によって形式を変えて出力する場合などです。ビューモデルの生成はいろいろやり方がありますが、ここではConverterを使って集約から変換して導出します。}}Converterのコードは以下。リポジトリが持つ集約の集合をビューの要件に合わせてmapしているだけです。};}最後に、React.Componentです。CreatorとStore(リポジトリ含む)にしか依存していません。Creatorを使ってActionをDispatcherに送信します。すると、Storeで振る舞いを起こし状態変化が起こり、ViewがStoreにあらかじめ登録したハンドラが呼ばれてView自身の状態を書き換えます。}}コードはGithubにあります。リポジトリがメモリ版なのでそれをAPI版に返れば複数ユーザで特定のアプリケーション状態を共有できるようになるはずです。以上、長くなりましたが、ReactでのFluxとDDDの統合方法の解説でした!統合方法は他にもいろいろあると思いますが、僕が考える一例を示してみました。次はAngular2版をまとめてみます。Sourcingではありません。Sourcingです。機会があれば実装例を作ります*2:ActiveRecordのようにドメインモデルが永続化機能を持たないのはなぜか?という疑問があるかもしれません。ドメインモデルはビジネス上の概念と紐付くものなので、なるべく固有の技術から依存性を排除しビジネス上の知識を表現します*3:Scalaのケースクラスのcopyメソッドがほしい...。Sourcingを学ぶための教材ServiceとDCIについて月別アーカイブはてなブログをはじめよう!j5ik2oさんは、はてなブログを使っています。ブログを報告するスターをつけました引用をストックしましたストック一覧を見る閉じる引用するにはまずログインしてくださいログイン閉じる引用をストックできませんでした。再度お試しください閉じる限定公開記事のため引用できません。