非純粋関数型言語の純粋関数と純粋関数型言語メモ 続き
↓↓↓たぶん前日譚↓↓↓
純粋関数型言語を使ってないけど、ドメインモジュールだけは純粋関数的に設計するようにしている。効能はこの記事に書いたとおり。 zenn.dev/j5ik2o/article…
2021-12-01 09:19:06ドメインオブジェクトでI/Oを扱わないのは共通認識だけど、ドメインサービスならOKとする例もある(「実践ドメイン駆動設計」はこっち側)。僕は前述の理由でドメインサービスでも純粋にする。I/Oするならドメイン以外のレイヤーでやる。
2021-12-01 09:19:06つまりI/Fはドメイン層, 実装はインフラストラクチャ層。実装でI/OしてもI/Fから見えなければなんでもよいという設計にはしない。これをやるとドメインロジックが副作用を巻き込んでモジュールの再利用やテストが極端にやりにくくなるから。なので、ドメインサービスは純粋関数的に設計する。
2021-12-01 09:19:07私もDomain層は入出力から切り離して副作用減らしビジネスロジックだけに集中させた方が良いと思ってる。 Domain層にIF、実装はInfra層だと実装詳細を隠蔽してるだけで結局は入出力。 Domain層にIF置いてApp層でそのIFを実装し、Repository等の永続化責務関連はApp層から参照するようにしてる。 twitter.com/j5ik2o/status/…
2021-12-03 08:58:13@j5ik2o ですね。 青本でもドメインサービスもドメインモデルの一部とありますし、ドメイン層にI/Oの責務が漏れ出ると、I/O失敗時の補足とか意識することに繋がりかねないので可能な限り分離したいです。ドメイン層はビジネスルールやロジックに特化させたい。
2021-12-03 11:56:38そうなんですよ。I/Oが失敗する可能性をドメインロジックに含めたくないんですよね。例えI/Oモナドのような抽象化概念で失敗を排除できてもI/Oの知識を使っているのには変わりないので純粋なものとしたいですね。そうすることで逆にI/Oのロジックが書きやすくなると思うので twitter.com/read_po/status…
2021-12-03 12:00:16↑↑↑前日譚ここまで↑↑↑
以下本編続編
賛成。クリーンアーキテクチャは、I/Oの実装をドメインロジックから追い出そうとするが、それより先に、I/Oの存在自体をドメインロジックから消し去れないかと考えた方がいい。いつもそうできるわけではないが、出来る場合も多い。 twitter.com/j5ik2o/status/…
2021-12-05 09:44:52@cubbit2 まぁ興味を持ったのでやってみますよ。 Cubbitさんも是非CatsやZIO、関数型プログラミングの大規模プロジェクトなど試してみてください。
2021-12-05 09:53:17@cubbit2 楽しい会話に付き合ってくれたお礼に何か送りたいのでAmazonの欲しいものリストとかないですか?
2021-12-05 09:57:16@cactaceae 横から失礼します、当方Haskellのエコシステムよりもcats/cats-effectの方に長く触れているのですが、ツリーの上の方にあった > Monadの扱い方によっては純粋関数のメリットを失うことは簡単におきて という部分がよくわかりませんでした。これはより具体的にはどのような状況を指していますか?
2021-12-05 10:17:46@Kory__3 1/6純粋関数の定義を前提に ・関数の戻り値は,同一の引数に対して同一である ・関数の適用には副作用がない 純粋関数を利用することで以下のメリットが得られると考えています ・テストしやすい ・合成しやすい ・相互に依存しない関数は実行順序の自由がある ・Input等の副作用を後から注入できる
2021-12-05 11:43:33@Kory__3 2/6テストしやすさについて(以下で関数は純粋関数とする) 関数を評価して最終的な結果がでるところまで副作用がない純粋関数(haskellの定義とは違います)は、引数と結果が必ず一致するのでテストが容易です。 単一関数のテストだけでなく、多くの関数を合成した関数についても同様にテスタブルです
2021-12-05 11:43:56@Kory__3 3/6上記の前提に加え、純粋関数は依存が明確でリファクタリングしやすいため、 DDDで生まれたモデルに対応する純粋関数をつくることや、そのモデルをもとにQAが作成したテストセットを実行することも容易です。
2021-12-05 11:44:14@Kory__3 4/6例として、外部からのInputに対して、複数のデータストアやAPIにアクセス、その結果を一部計算し、また別のAPIやデータストアにアクセスし、さまざま計算をして結果を出力するようなプログラムを作るとします。
2021-12-05 11:44:27@Kory__3 5/6一般的なプログラマーはデータが必要な時に取得し、そのデータに対して実行できる処理を行った結果を次の関数に渡す、ということを繰り返します。 すると実行時に副作用ある処理と実行時にも副作用がない純粋関数が交互に実行され、上記のような柔軟なテスト構成ができません。
2021-12-05 11:45:59@Kory__3 6/6そこでEffectSystemを利用して、最終的に副作用を起こす純粋関数と、副作用に一切依存せず引数のみに依存する純粋関数を区別してそれを集め、テスタビリティを向上させるアーキテクチャを構築しています。
2021-12-05 12:09:02@cactaceae しかしながら、副作用を起こさない処理(のうちうまくいったかが検証可能な部分)を実際にテストできるように、副作用を起こす部分とそうでない部分を区別した時点で十分に純粋関数のメリットを享受していませんでしょうか?
2021-12-05 12:38:45@Kory__3 はい。そうです。 そこからもう一歩進んで純粋関数のメリットをより活かせるように考え、関数型プログラミングをしています。
2021-12-05 12:57:23@cactaceae なるほど、結局一番最初の方のツイートは、「ただ単に純粋関数を使ってMonadicなプログラミングをしたところで、ドメインモデルなどがopaqueなデータ構造(e.g. (IO a))に埋もれてしまっては結局テスト可能性が損なわれるので、純粋関数は銀の弾丸ではない」程度の意図と読み替えて大丈夫でしょうか?
2021-12-05 13:04:28