お勉強でTodoListをDDD的流れで作成していたらReactivePropertyが良かったので紹介して見ようと思いました。
以下、作成記録です。
TL;DR
- ドメインイベントのメッセージ基盤にはMessageBrokerで必要十分。
- CQRSのコマンド部はReactiveCommandを継承して実装すれば便利なのでは。
どちらも.NET Standardでも使えるからドメイン層を使い回し可能。
オレオレで作るよりかは良いかと。
ステークホルダーからの要件っぽい内容を定義しておく
- TodoListを作成したい。
- ひとつのTodoをタスクとして管理する。
- 管理する内容はタイトルと説明とそのTodoの状態。
- 状態は2値ではなく実行中を含めた3値としたい。
- タスクのリストを表示して、ひとつを選択すると詳細な情報が表示されるようにしたい。
- タスクを新規に作成するとそのタスクの詳細な情報を入力できるようにしたい。
要件っぽい内容を基にユビキタス言語の抽出とワイヤーフレームを提示してみる
ユビキタス言語一覧
項目 | 英語 | 説明 |
---|---|---|
タスク | Task | Todo1件を表す |
タイトル | Title | タスクのタイトル |
説明 | Explain | タスクの内容説明 |
状態 | Status | タスクの状態を表す。3値。Ready, Doing, Done とする |
タスクリスト | Tasks | タスクを束ねたリスト |
(タスク)ひとつを選択すると | SelectedTask | ドメインイベント?画面上の話だからアプリケーション層で取り扱うかも |
タスクを新規に作成すると | AddedTask | ドメインイベント。他にも拡張ポイントとして編集されたときとか、削除されたときとかのドメインイベントを用意する必要あるかも。 |
タスクを新規に作成する | AddTaskCommand | コマンド。他にも編集とか削除とかのコマンドが増えるはず。 |
入力画面 | Input View | タスクを新規に作成するときにここにタイトルを入力する。ボタンでタスク作成にするか、Enter検知でタスク作成にするか |
リスト画面 | List View | タスクのリストを列挙する。新規に作成されたタスクはリストの下部に追加?上部に追加? |
詳細画面 | Detail View | タスクの内容を表示する。その場で編集もできるようにしたい。 |
ワイヤーフレーム
実際の開発だとここをステークホルダーとがっつり詰めないといけない気がする。
文章をUML化
とりあえず上記内容でなんとなく固まったとして、文章だと解りづらい点をユビキタス言語にのっとって図解しとく。
クラス図
ドメインはこんなもん。Taskを集約ルートとして識別するためにTaskIDを用意した。Tasksはファーストクラスコレクションとして用意。その他は値オブジェクト。必要になれば副作用のないメソッドを増やしていく。
パッケージ図
.NET的なプロジェクトの分割も図解。
Domainだけを参照するように依存関係逆転の原則(DIP)で作っておく。
ListViewにタスク一覧を表示するときにはStatusとTitleしか必要ないのでCQRS的にDTOとクエリを用意しておく。
画面に表示する内容はアプリケーションの事情なので置き場所はAppServiceにしてみた。
画面上でタスクが選択されたというイベントもアプリケーションの事情だと思ったのでAppServiceに置いた。
シーケンス図
Taskを追加するときのシーケンスを作成。
ステートレスなインスタンスはIoCコンテナを通してコンストラクタで注入しておく。
ここでやっとタイトルの話。
AddTaskCommandはReactiveCommandを継承し、Executeメソッドを作成して最後にbase.Execute(T parameter)を実行する。
こうすれば戻り値をSubscribeできるし、必要であれば複数コマンドの直列処理も並列処理も楽。
ドメインイベントに関してはIMessageBrokerを使った単純なPubSub。
購読解除も他のIDisposableと一緒にCompositeDisposableに追加しておけば解除漏れの心配もなし。
実装
成果物
使用したライブラリ/フレームワーク
- Prism 7.1.0.431
- ReactiveProperty 5.3.0
感想
- ReactivePropertyについてはTL;DRに書いた。
- そもそもDDD的になっているか?今後新たな要件が増えたとしても変更は局所的になる気はする。
- CQRSもこんな感じで良いのかしらん。クエリの方はさっくり理解できた気がするけど、コマンドの方はコマンドバス(コマンドハンドラ?)を用意してそこにコマンドを蓄えてから実行するのが正解なのだろうか。
- イベントソーシングはパス。
- ドメイン層に置くべき処理(イベント)なのかアプリケーション層に置くべき処理(イベント)なのかの判断が難しい。ドメイン層より外側の層が全て入れ替わっても業務知識がこぼれなければ良いと思っているが。