るいもの戯れ言

関数型でドメインモデリングというお話。

第2章「ドメインの理解」

実際のドメインの理解について実例を示しながら解説している。ドメインエキスパートへのインタビュー、こんなに何でも知っているお客様はいないっすよとは思いつつ、まぁそれを言い出すとキリが無いので仕方無い。そして、いきなりER作り出すなよと釘を刺す。これは、おじいちゃんへの良いアドバイス。そしてクラス図もダメだからなと釘を刺す。今時もクラス図って生きているの?じゃどうやって実装に落としていくのかというと、テキストベースのメモに留めておけと。そしてドメインの理解が完全になってから実装に進むのだ。ただこのテキストベースのメモに出てくる型の記述って結局は(Javaで言うならAbstcatr Classを排してInterfaceだけを使った)クラス図じゃないのかなぁ。

インタビューは更に進む。「数量は整数ですか浮動小数ですか?」に対しドメインエキスパートにプログラミングの専門用語使っちゃダメですよ。「数量は整数ですか小数ですか?」と聞きましょうと。それはそうなんだけど。分析系以外で浮動小数点数の利用は常識として無いだろうと思う。この手の書籍で気軽に浮動小数点数を持ち出す例を見かけるのだが、やめた方が良いと思う。そもそもこの著者って本当に実務でプログラミングしているの?という疑問が沸いてしまうので。

制約条件、ライフサイクル(状態マシーン)を書き上げる。まぁそうですよね。

完全に新規の場合は本書の通り「ドメインの理解」から始まるのだろうけど、そうでなければ通例は「システムの理解」的なものがあるのではないかな?特に外部システムとか。これはドメインエキスパートはあまり知らないので、お客さんのIT部門の人にも聞かないといけないし、ITの人も外部システムについては実は良く知らなかったりで大変なわけで、とはいえ「関数型」とは離れてしまうので省略されたのかもしれない。

第3章「関数型アーキテクチャ」

処理はイベントソーシングでキューイングする。このあたりはマイクロ・サービスのコレオグラフィと同じ考え方なのかな。

これまでのようなサービスレイヤ、ドメイン、永続化層みたいなのは良くないよね。なぜなら横につながっているのである機能の修正のために他の機能への影響が出やすいからと。機能ごとに分離しましょう。ただ、それだたレイヤーが増えすぎて良くないからオニオンアーキテクチャにしましょう。I/Oは副作用なのでドメインには置かずに外側に置きましょうと。う〜んそれはまぁ分からないでもないけどドメインロジックで、ビジネスルールなどのチェックが必要だとI/O必要ないのかなぁ。そういうのは先に取得しておいて渡せってことかな。でもそうするとビジネスルールが複雑な場合って、最終的には不要なものも含めて必要になりそうな可能性のあるもの全てを先に読んでおけってことになるのかな。

第1部はここまで。第2部は基本はF#の話。

静的型の活用。値型(単純型)。不変条件。このあたりは別に「関数型」でなくてもImmutable指向のOO設計でも共通の話題かな。

ワークフローはイベントをキューで流せと。Validationとかの粒度でイベントとして扱うとされているが、そんなレベルでやるの? あとはフラグやめてステートマシンにしようね。このあたりは昔から変わらない。

第3部もF#というか関数型の基本の話で始まる。

エラー処理。F#ではResultを使う。まぁイベントをキューイングというアーキテクチャでは例外を使ってもご利益ないよね。そしてシリアライズの話。

12章で永続化。ここで3章で抱いた疑問への回答が示される。

まず、前の章の方ではかなり粒度の小さなレベルでイベントとキューイングで実装していくように読めたのだが(誤読か?)、実際はI/OがOKな層からドメインロジックを「呼び出し」て良く、ドメインロジック中で追加でデータが必要になったら一旦その旨を戻値でドメインロジックから返し、その戻値をもとにI/O層が追加でデータを取得してまたドメインロジックを呼ぶような造りで良いとのこと。まぁこれなら良くみる「サービスレイヤ」なのでそんなに異和感は無いよね。

本書での主張は自分は、要件定義ですぐにER図とかクラス図などの実装詳細を書こうとするな、ドメインロジックからはI/Oを無くせ。あたりが本質かなと感じた。具体的なアプリケーションを題材にしているので、アプリケーションを実装している人には示唆に富んでいると思う。

ただ盲従しない方が良いかなと。基本CRUDで画面を数100枚作るようなプロジェクトでこの手法はover killだと思うし、基幹業務で一旦作ったら5年、10年塩漬けというようなアプリケーションでもメリットを出しづらいだろう。

なら一日に何度もデプロイするような戦略アプリケーションならどうか? 少なくとも3-5年前の自分ならこういう設計(ドメインロジックからはI/Oを排する)に諸手を上げるかもしれない。ただし、これはトレードオフで、やはりドメインロジックからI/Oを排するために複雑になることは受容しなければならない。こういうのは一旦流行しても5年くらいするとPlain Oldなんとかみたいな揺り戻しが起きて「単にサービスレイヤで永続化層とやりとりするだけの単純なコードの方が実装ははるかに楽だよね?」みたいになりそうな予感もする。

最近はRustでWebアプリケーションを実装してるが、結局永続化に関する部分はsqlxやSeaORMのようなライブラリで吸収できてしまうし、複雑なバリデーションはBuilderに入力をつっこんでbuild()メソッドで検証みたいな素朴な作りで十分で、あまり大層な仕組みの必要性を感じない。フレームワークもテスト支援機能が手厚く、DBありでも無理なくテストできる。もちろんその分、I/Oが入るとテストが遅くなるとか、単体テストはテストごとにPostgresのDB名にUUID入れたものを新規生成してぶつからないようにするなどの工夫が必要などデメリットもある。その分、ドメインからDB切り離して得意になっていたら、SQLがバグっていて痛い目にあったなんてことが起きないというメリットもある。結局はトレードオフなのだ。自分のようにDB苦手な人間はDBも巻き込んだテストを地道に回す方が安心できる。