知識問題と思考問題

最近、高校の先生が「知識問題」「思考問題」という言葉を使ってらっしゃるのを聞いて、「なるほど、面白い分類だな」と思った。それ以来「知識問題と思考問題」という視点で物事を眺めるのが自分の中でブームとなっている。

もちろん、知識問題と思考問題というのはきれいにスパッとわかれるものではなく、グラデーション状になっていると思う。この問題はやや思考問題よりだね、とか、かなり知識問題に振れてるね、とか、かなり思考問題寄りだね、とか。

ソフトウェアの設計について考えてみると、じつは、ソフトウェアをどのようなレイヤーに分割するか、というような話はかなり知識問題寄りに見える。この問題には先人の知恵がたくさんあって、それらが「傾向と対策」として未知の問題にも流用しやすい。

一方で、レイヤーの内部をどのような責務でどのようクラス、コンポーネントに分割していくのかというのは、かなり思考問題よりに見える。このとき、設計原則などは役にたつが、それは「検算」的な部分で役に立つ感じで、問題とがっぷり四つに組んで頭に汗をかいて設計しなければならないように感じる。

これがマイクロサービシーズになってくると、どのようなマイクロサービスにどのような責務を負わせ、どのように分割するのかがかなり思考問題寄りで、サービス間の連携についてはけっこう知識問題に寄っているように思えるのがおもしろいところだ。

そんな視点で眺めてみると、去年ぼくがbuildersconで話した開発現場で役立たせるための設計原則とパターンという発表は、設計原則やパターンは、このように活用すると思考問題を解く際の武器にできるよ、という話をしているというように理解ができそうだ。一方、今年のbuilderscon に応募している(たのむ!採択されてくれ!)、Ruby (off|with) the Railsという発表は、かなり知識問題よりの領域を扱っていると思う。まあこの記事で言いたかったのはそういう予告です……。

ところで、知識問題を解くための武器とも思考問題を解くための武器だったら、なんだか思考問題を解くための武器のほうが「ありがたがられ」そうな気がするけど、これらに優劣はない、とぼくは思っている。思考問題を解くための武器は錆びつくのが遅いけど、一方で「すぐに役立てる」のが難しい。知識問題を解くための武器はすぐに陳腐化するけど、明日からすぐに役立てやすい。これらは優劣ではなく、性質の違いだ。

自分が問題に向き合ってる時に、いま知識問題に向かい合っているのか思考問題に向き合っているのかは少し意識してみると、自分がいま向き合うべき問題なのかを考えられたりとか、「知識問題だけど知識が足りないから、すぐ使えるやつを仕入れてこよーっと」みたいなムーブができたりとかできて良いかもしれない。いや、しかしこれは「知識問題だとわかるためには知識が必要」という問題があるか?

あー、でも、少なくとも、ジュニアエンジニアが知識問題に戸惑ってたらシュッと知識をインストールしてあげる、思考問題に戸惑ってたら本人の気づきをナビゲートしてあげる、みたいなことはできそうですよね。知識問題と思考問題という視点はけっこう便利であるなあと思います。

なんかとりとめない話になっちゃったけどブログだからいいよね。

rubyのString#splitのおもしろ挙動

RubyのString#splitは、下記のようなおもしろ挙動を持っている

"  a\nb \t c   \rd  ".split(' ') # => ["a", "b", "c", "d"]

これは(とうぜんのことながら)ちゃんとDocumentedである

docs.ruby-lang.org

1 バイトの空白文字 ' '
先頭と末尾の空白を除いたうえで、空白文字列で分割する。

便利なんだけど知らないでいると意図せぬバグを埋め込んでしまうかもしれないので、知っておいて損はないかもしれない(ぼくは今日知った)

RailsのControllerにApplicationService相当のロジックを書くのはありなしや?

誤解を産んでいそうだったので追記します。ここでいうApplicationServiceというのは、いわゆるレイヤードアーキテクチャのアプリケーション層のApplicationServiceレイヤの話です。別の言葉だと、「Usecase層」とか言う言葉で呼ばれたりするアレのことです。追記おしまい。

これについてです。

結論から先にいうと、ぼくは「正しい」と思っています(ただ、自分ではあまりやらず、ApplicationServiceに切り出しちゃいますが、これは好みの問題だと思っています)。

なぜ正しいと思うのか。それを考える際に、まずは「一般的に、なぜControllerとApplicationServiceを分けるべきなのか」について考えてみたいと思います。ControllerとApplicationServiceを分けるのは、PDSの文脈において、ControllerがPresentation(ユーザーとのインタラクションに関わる部分)のレイヤーに属すものであり、ApplicationServiceはDomainに属すものである、という理解がまずあるからだと思います。

では、なぜPDSが重要なのでしょうか。原典を引きましょう

  • プレゼンテーションロジックとドメインロジックが分かれていると、理解しやすい
  • 同じ基本プログラムを、重複コードなしに、複数のプレゼンテーションに対応させることができる
  • ユーザーインターフェイスはテストがしにくいため、それを分離することにより、テスト可能なロジック部分に集中できる
  • スクリプト用のAPIやサービスとして外部化するためのAPIを楽に追加できる(選択可能なプレゼンテーション部分で見かける)
  • プレゼンテーション部分のコードは、ドメイン部分のコードと違ったスキルと知識が必要

Martin Fowler's Bliki (ja)より

ぼくのことばで言い換えると、

  • プレゼンテーションはテストしにくいから、ドメインに分けておくとテストがしやすくなる
  • 責務が明確に分離されてわかりやすくなる
  • 他のプレゼンテーションレイヤーに差し替えが可能(Railsの例で言えば、ApplicationServiceに分離しておけば、たとえばTaskからも呼べる)

の三点が、PresentationとDomainを分離する際の嬉しいポイントです。ここで、RailsにおいてControllerにApplicationServiceを書いたときにどれだけこの問題が顕在化するかについて考えてみます。

まずは「プレゼンテーションはテストしにくいから、ドメインに分けておくとテストがしやすくなる」についてです。これ実はRails使ってるとあんま問題じゃなくて、というのもRailsってControllerのテストめっちゃふつうにしやすいじゃないですか。だからこれはあんまり問題にならないと思っています。

続いて、「責務が明確に分離されてわかりやすくなる」です。これについてはちょっと丁寧に見ていきましょう。おそらく、RailsでControllerとApplicationServiceを分離すると、Controllerの各メソッドは「1,2行書いておしまい」というメソッドになるでしょう(そうじゃなかったらちゃんと分離できてないと思ったほうがよさそう)。こうなると、「Controllerいる?」って感じになってきますよね。これが静的型付け言語の場合で、しかもVannila DIだったりCakePatternしてる場合なんかは、Controller部分でDIしてたりするので、Controllerに責務が残りますよね。あと、MVVMで作ってるGUIアプリケーションの場合、ローカルステートの管理などがVMの責務として残ります。けど、Railsの場合、ApplicationServiceをControllerから切り出すとそこに責務ってほとんど残んないんですよね……。あるとしたら認証とかかな。けどこれもControllerのテスト対象メソッドでやるんじゃなくて普通はレイヤスーパータイプとかでやってますよね。というわけで、なんか特殊なHTTPの言葉の部分触ってるとかそういうのがないのであれば(あーセッション周りとかはなにかしらあるかもですね)(しかしセッションって本来Infrastructureだよな〜)、「RailsにおいてはApplicationService相当のロジックをControllerに書くとする!」ってするのは十分にメイクがセンスするし、パルスのファルシのルシがパージでコクーンなのではないかとぼくは思います。

最後に、「他のプレゼンテーションレイヤーに差し替えが可能(Railsの例で言えば、ApplicationServiceに分離しておけば、たとえばTaskからも呼べる)」についてです。ここについては実はControllerにApplicationService相当のコードを書いてしまうと問題が顕在化します。TaskからApplicationServiceを呼び出すのはとても簡単ですが、TaskからControllerを呼び出すのは簡単ではありません。しかし、ちょっと立ち止まって考えてみると、ほんとうにそんな柔軟さはRailsアプリに必要なのでしょうか???今まで、「あー、TaskからController呼び出せたら最高なのにな〜」って思ったこと、あります? ぼくはほとんどないです。そのために新たな層を導入して複雑さを引き受けるの、コストがペイするのでしょうか?パルスのファルシのルシがパージでコクーンなのでしょうか?

しかし、じつは、ここがぼくの「趣味」の部分で、ぼくはこれのためにApplicationServiceを分離してんですね。なぜか。そうすることで、たとえばテストの、しかも「前提データ」を用意するときに嬉しいことが起こります。Rails文化ではなぜか(なぜかと言ってしまおう)FactoryBotが人気で、FactoryBotを利用して前提データ(たとえば、ログイン可能なユーザーアカウントとかね)を作るプラクティスが横行していますが、ぼくは以前から、なぜここでみんな(たとえば)「UserSignupApplicationService」を作ってそれを呼び出すだけにしないんだろう、と不思議に思っています。適切にApplicationServiceが書かれていれば、UserSignupApplicationServiceを呼び出すだけで前提データは作れるはずです。FactoryBotを利用して前提データを作る場合、「サインアップ」の仕様が変わると「UserSignupApplicationService」と、「サインアップ済みユーザー」を作り出すFactoryBotを使った部分両方のメンテが必要になってきます。一方、FactoryBotではなくてApplicationServiceを利用すれば、ApplicationServiceのメンテのみで済みます。なぜ「テスト済みの、前提データを作れる部品」があるのに、FactoryBotを使う必要があるのでしょうか?

という観点に立つと、意外とApplicationServiceを分離することには価値がありそうですが、世の中的にはFacyoryBotが主流だし、「Railsにおいて」という前提に乗っかるなら、これはかなり「異端児の意見」であろうな、というのもわかるので、アンケートの答えとしては「正しい」を選ぶ、けど自分はあまりやらない。という感じになるわけでした!

以上、ぼくの意見です。みなさんの意見もぜひ聞かせてください。

Sounds Around (My Life) has released!

まずこちらをお聞きください。つるえさんというボーカリストの方に楽曲提供をさせていただきました。ぼくは作詞・作曲・編曲・コーラス・ギター・ベース・打ち込みを担当しています。

soundcloud.com

つるえさんと知り合ったのは荻窪にあるアルカフェというアコースティックライブバーで、初めてお会いした時にその歌のうまさに衝撃を受けたのをとてもよく覚えています(この音源も、ボーカル補正なしですよ。めっちゃうまくない?)。

歌がうまい、というのには、いくつかの要素があるとぼくは思っています。まず、ピッチが正確であること。自分の歌を棚に上げて言うと、これが大前提。そこから先は個性や好みの話が関わって来て、たとえば一聴してそのひとの歌だとわかる強い個性、あるいはクセを持ったボーカル、というのがひとつあると思います。日本のポップシンガーで言うと、たとえば山崎まさよしさんとか奥田民生さんをイメージしていただくとわかりやすいのではないでしょうか。

実は、つるえさんの歌唱にはそういう「一聴してこのひとだとわかるクセや個性」はあまりありません。ただ、誤解して欲しくないのは、ここで言っているのは、アーティキュレーションのクセや発音のクセの話であって、声そのものの話ではないです。つるえさんの声は、地の声に芯があり、その芯の周りにツヤのある響きを纏わせていて、とても魅力的な個性があると感じます。そして、おそらく意図的に、歌唱法で「ここはこういう表情」「ここはこういう表情」というのを非常に幅広く、自覚的に使い分けています。アーティキュレーションや発音に、強烈な個性がないからこそ、とても幅広く表情をつけていて、変幻自在ですごい。

そして、そういうボーカリストに出会ってしまった、自分自身は歌が下手なコンポーザーが思うことと言ったらひとつしかないですよね。「このボーカリストに歌ってもらう曲を書きたいなあ」。その夢がかないました。書きました。歌ってもらっちゃいました。それが冒頭に貼った「Sounds Around (My Life)」という曲です。

このコラボレーションは、スタジオ録音などをしているわけではなく、リモートでコラボしていて、なんとボーカル録りは「iPhoneのボイスメモ」でやっていただくという荒業を見せています。このあたりにもっとリソース使えれば、もっともっとダイレクトにつるえさんの歌の素晴らしさを音源に残すことができたのですが、それができなかったことだけがとても残念です(ていうか、その録音環境でこの歌のクオリティなの、いい意味でヤバすぎませんか?大事なことだから二回言うけど補正もほとんどしてないんですよこれ)。

そんな心残りはあれど、ぼくが思うつるえさんのボーカルの「美味しいところ」のためにメロディを作り、つるえさんの声の魅力の邪魔しないように控えめな、しかし丁寧な編曲を心がけて、つるえさんの声で聴いたらとても綺麗にイメージが届くであろう歌詞を頑張って書いて、つるえさんの歌が主役になるようにミックスをしました。ソングライター、アレンジャー、DTMerとしての「このボーカリストに歌ってもらいたい」というエゴにめちゃめちゃに素直になった結果、全ての要素がつるえさんのボーカルに奉仕するようなつくりになっています。これでつるえさんの歌の魅力が伝わらなかったとしたら、それはもう完全にぼくの力不足以外のなにものでもありません。ぜひ多くの方に聴いていただきたいと思います。よろしくどうぞ。

2019年のwebAPIの設計を取り巻く問題と技術シリーズ そのに 「ビジネスロジック」は誰が持つべき?

前回の記事の続きです。

前回は、「時代が変わるとサーバーアプリケーションの役割も変わるよね。そうすると必要な要素技術も変わっていくよね」という話でした。今回は、じゃあ「サーバーアプリケーションがJSON喋るマンになって、クライアントアプリケーションとの協働でユーザー体験が実現されるようになってきた今、"ビジネスロジック"はだれが持つべきなの?」って話をしたいと思います。

基本的にはサーバーでもってあげたほうが楽

さて、サーバーサイドでなんでもやる牧歌的な時代は過ぎ去り、クライアントサイドとサーバーサイドがコラボレーションしてひとつの体験を提供するようになった昨今、「そのアプリケーションの体験を成り立たせるための"ロジック"をどっちに書くのか」という問題が立ち上がってきます。わたしの私見ですが、これはなるべくならサーバーサイドで請け負ってあげるのがよいのではないでしょうか。その理由は、クライアントサイドにはクライアントサイド特有の複雑な問題を抱えているため、そのほかの問題はサーバーサイドで抱えてあげたほうが全体の複雑さが減るからです。

HTTPというのは基本的にステートレスなプロトコルです。そのため、基本的にはサーバーサイドの状態は1リクエストごとに閉じています。リクエストを受け付ける時にはまっさらな状態で、レスポンスを返すまでには様々な状態をもちそれらに対して様々な計算をして、レスポンスをかえします。このとき状態は一度破棄され(あるいはRDBなどの外部に書き出され)、次のリクエストのときにはまたまっさらの状態でそのリクエストを処理し始めます。

一方、クライアントサイドでは、ブラウザSPAアプリならばブラウザをリロードするか別のサービスに遷移するまで、メモリ上に状態を持ってしまいます。とうぜん、これはネイティブアプリでも事情は同じです。やったことのあるひとにはわかると思いますが、この状態の管理は、それだけでけっこうめんどくさいものです。

また、その「状態」から複雑な画面を構築しなければいけないというめんどくさい問題も、クライアントサイドは抱えています。画面作るのほんとに面倒で、そこにユーザーインタラクションがからむともっともっと面倒です。そういうめんどくさい問題を抱えているのに、さらに複雑なビジネスロジックまで押し付けられるのはあまりに大変ですので、困難を分割するためにも、サーバが出来る限り複雑さを引き受けてあげるのがよいのではないかと思います。

また、サーバーサイドに複雑なロジックを持っておくことで、複数のプラットフォームでのアプリ展開がやりやすくなる、という面もあります。たとえばiOSアプリもAndroidアプリもwebアプリも作る、というような場合に、クライアントサイドにビジネスロジックを持っている場合、それぞれのアプリで毎度実装する必要が出て来ますが、サーバーサイドでロジックをもってあげればサーバーサイド一回の実装ですみます。

また、クライアントサイドで一生懸命複雑なことをやったとしても、サーバーにとってリクエストは「だれから送られてきたかわからないもの」なので、結局サーバーサイドでデータの不整合が起こらないように検証はしなくてはいけません。そういった検証はまさにビジネスロジックの一部であり、クライアントサイドでビジネスロジックを持った場合、サーバーサイドでも重複したロジックを書かなければならない、というようなことは容易に起こり得ます。

ファーストパーティに閉じたサービスとサードパーティに開かれたプラットフォームの違い

しかし、これらの状況は、そのサービスが、ファーストパーティに閉じたサービスなのか、それともサードパーティに対して開かれたプラットフォームを目指すのかによって、事情が変わってきます。

ファーストパーティに閉じたサービスの場合、APIは「このアプリ専用」に作ることができます。いわゆる「顧客-供給者の関係」ですね。アプリ側の都合に合わせてAPIを作ることで、「複雑なビジネスロジック」をサーバー側が引き受けることになります。

一方で、サードパーティに開かれたプラットフォームの場合、不特定多数のクライアントに対して応答する必要があります。そのときに、サーバーサイドは、あるクライアントが解こうとしている問題に特化したAPIを提供するわけにはいきません。そんなことをしたら、クライアントの数だけAPIの種類が増えてしまいます。そのため、サードパーティに開かれたプラットフォームでは、サーバーサイド側になるべくロジックをもってあげることが難しくなります。プラットフォーム側の「ビジネスロジック」は薄くなる傾向があるでしょう。

しかしこれは、考えてみると当然のことです。というのも、プラットフォームというのは、「ぼくたちのデータや資源を使って、きみたちの問題を解決してね」というサービスなわけで、そうなってくるとどうしても問題を解く主体は、プラットフォームではなくてそのユーザーとなるでしょう。つまり、ユーザーは、ユーザーごとに異なる問題と向き合うことになるわけです。そのときに「ドメインロジック」がクライアント側にもたれるようになることは、ある意味当然となります。もちろん、このときプラットフォームにとっての「クライアント」が、webブラウザであるとは限りません。プラットフォームのユーザの選択によっては、webブラウザだけで完結するかもしれませんが、あるいはプラットフォーム利用者が自前でサーバーを立てて、そのサーバーサイドアプリケーションがプラットフォームにとってのクライアントとなるかもしれません。しかし、プラットフォーマーが持つサーバーアプリケーションは、特定の問題のための「ビジネスロジック」を持たない、ということには変わりがないでしょう。

まとめ

というわけで、まとめると、

  • 基本的には複雑なビジネスロジックはサーバーにもってあげるのが筋がよいと思う
  • 一方で、サードパーティに広く開かれたプラットフォームの場合はクライアント側にビジネスロジックを持ってもらい、サーバーはファンダメンタルな操作を提供するほうがよい

という感じになります。

前者の場合、最近だとリソース指向なAPI(いわゆるRESTfulなAPI)よりも「ユースケース指向」なAPIが増えてきていると感じます。ユースケースと1:1になるようなエンドポイントが生えてるようなAPIですね。

一方で後者の場合はユースケースはクライアントに依るので、mutationに関してはリソース指向のAPIがマッチしそうです。一方、リソースの取得についてはクライアントによってほしいデータが違うという問題があり、そのあたりにはGraphQLの恩恵がききやすいのではないか、という理解をわたしはしています。

次回書く気になれば、次回は

  • サーバーサイドに寄せられた、複雑なロジックやミドルウェアとのやり取りと向き合うための技術
  • クライアントサイドに残された「状態管理」と「複雑な画面」と「ユーザーインタラクション」と向き合うための技術

あたりを書けたらいいなと思います。が、もしかしたらtwitterでバーっと書いて満足してしまうかも……。

2019年のwebAPIの設計を取り巻く問題と技術シリーズ そのいち 導入

最近、2019年のwebAPIの設計まわりの問題について考えていて、問題とその周辺技術の整理を自分の頭の中でしています。 で、その内容をたぶん何回かに分けて書きますが、初回の今日はちょっと導入として浅めの部分を整理してみようと思います。

昨今、サーバーサイドの仕事はもっぱらJSONなどをしゃべることに終始して、エンドユーザーが触る画面をサーバーがレンダリングする事例は減ってきているのではないでしょうか。これはぼくが考えるに不可逆な事態で、今後もユーザーが使うインターネットにつながるデバイスは多様化していくし、それぞれがそれぞれにネイティブな環境を持ち、それらがHTTPSを喋ってインターネットとつながる、という世界はしばらく変わらないでしょう。言葉を変えれば、サーバーサイドでHTTPをしゃべるお客さんが、エンドユーザーからクライアントアプリケーションというプログラムに移行してきている、ということです。

その結果、サーバーサイドからは「複雑な画面を作る」という責務が奪われます。その結果がサーバーサイドアプリケーションになにをもたらすのかを考えてみたいと思います。

まず、Railsなどでよく使われる文脈でいう「decorator」は、昨今においてはその存在感を少しづつ失っていくでしょう(Pythonとかのデコレータとは文脈が違うので注意)。そもそも、この文脈におけるdecoratorは、「画面が要求する様々な"ちょっとした計算"」というプレゼンテーションの関心がモデル(ここでいう「モデル」というのはRailsの文脈でいうような「モデル」です)に混じってしまうことを避けるためのソリューションでした。しかし、昨今ではサーバーサイドアプリケーションがそのような複雑な画面をレンダリングすることはなく、jsonをペロッと吐けばいいだけです。そこには「画面が要求する様々な"ちょっとした計算"」は必要なく、むしろそれは画面を構築するクライアントサイドのプログラムでやってもらうべき仕事となるでしょう。

この「デコレータいらなくなったよね」という話は、牧歌的な「サーバーで全部やります」な時代が終わり、解くべき問題が変化した結果、必要な要素技術が変化していく1例として考えられるのではないでしょうか。ここで確認したかったのは、「デコレータがいらない」という話ではなく、 むしろ「問題がかわったら必要な技術が変化する」ということです。

さて、サーバーサイドからは「複雑な画面を作る」という責務が奪われたわけですが、昨今新たに出てきた新しい問題として、「じゃあサーバーとクライアントの間でどういう責務分割をするべきなの?」「サーバー側はサーバー側で昔と比べて複雑さ増してきてるよね?それをどうする?」というものがあります。次回以降、そのあたりの問題を整理しながら、どういう問題に対してどういう技術が効いてくるのかを書いていこうと思います。

まだ詳細未定ですが、予告としては、

  • webAPIとクライアントのどちらに「ロジック」を寄せるべきかという問いを整理
    • ファーストパーティに閉じたサービスとサードパーティに開かれたプラットフォームでの違い
  • ファーストパーティに閉じた場合
    • サーバーサイドに寄せられた複雑なロジックやミドルウェアとのやり取りとどう向き合うか、その解決としての設計パターンとmicroservices
    • クライアントサイドに残された「状態管理」と「複雑な画面」と「ユーザーインタラクション」とどう向き合うか、そのための技術、その解決としてのFlux系技術
  • サードパーティに閉じた場合
    • クライアントサイドにさらに積まれる責務とどう向き合うか、そのための技術(「いわゆる」DDD的な手法)
    • 多種多様なクライアントに対してどのようにレスポンスを返すのか、そのための技術(GraphQLあたりの話につながる気がしている)
  • コネクションにぎりっぱなし問題
    • なんか書くことあるかな。2019年においてはもうそろそろ書くことなくなってきてる気はする。

あたりを書きたい。本当に書くのかは不明。@qsonaさんあたりとtwitterで喋ったら満足して書かない可能性もあります。

FizzBuzzを題材にユースケース層についてを考えるのはおそらく無意味な気がする

やんざむ先生のこのツイートを見て考えたことをまとめます。

結論を先に書くと、「これはそもそも問い自体が不適切である」(しかし強いて言えばEntity)という立場をわたしは取ります。以下、まじめにFizzBuzzを設計しながら考えてみます。

ひとまず、出発点は上にあるツイートの疑問から出発するとしましょう。

まず前提として、ユースケースってどういう役割か

まず前提として、ぼくはユースケースを「ドメインモデル(このツイートで言うところのEntity)やインフラストラクチャのオーケストレーションをする部分」だと思っています。

たとえば、「あるボタンをクリックされたら、複数のドメインモデルを利用して生まれた結果を、データベースに保存する」というのは、複数のドメインモデルとインフラストラクチャ層にあるリポジトリ組み合わせて 、システムのユーザーのアクションに1対1で対応する「ユースケース」を実現しています。

つまり、ユースケースというのは、

ものだ、と考えています。

FizzBuzzについて考えてみる

さて、翻って、FizzBuzzについて考えてみましょう。まず、FizzBuzzを無理やりアプリケーション層とドメイン層に分けて考えるとしましょう。その場合、

  • 3で割れる場合はFizz
  • 5で割れる場合はBuzz
  • 3でも5でも割れる場合はFizzBuzz
  • それ以外の場合は数字をそのまま表示する
  • これを1から指定の数字まで繰り返す

というのは、FizzBuzzという問題領域を定式化、つまりまさにモデル化したものだと言えるでしょう。なので、これをドメインレイヤーのものであると考えるのは妥当だとすることにそれほどの無理はないでしょう。

ドメインレイヤーでこの定式化された知識をどのように分割するか、という点では、いくつかの判断がありえます。ぼくがぱっと思いついたのは以下のような感じです。

  • 数をValueObjectとみなして、それを利用してDomainServiceがループで文字列を返す
  • もう全部DomainServiceに書いちゃう

前者の発想だと、以下のような実装になります。

case class FizzBuzzNum(i: Int) {
  def asFizzBuzzFormat = i match {
    case i if i % 15 == 0 => "FizzBuzz"
    case i if i % 5 == 0 => "Buzz"
    case i if i % 3 == 0 => "Fizz"
    case i => i.toString
  }
}

object FizzBuzzService{
  def doFizzBuzz(max: Int): Unit = {
    (1 to max)
      .map(i => FizzBuzzNum(i).asFizzBuzzFormat)
      .foreach(println)
  }
}

一方で後者の発想だと以下のような実装になります。

object FizzBuzzService{
  def doFizzBuzz(max: Int): Unit = {
    (1 to max)
      .map{
        case i if i % 15 == 0 => "FizzBuzz"
        case i if i % 5 == 0 => "Buzz"
        case i if i % 3 == 0 => "Fizz"
        case i => i.toString
      }
      .foreach(println)
  }
}

これは、どちらのほうが「良い設計」でしょうか。設計のレビューをするときに参考にすべきなのが各種設計原則です。そして、設計原則を適用した際の結果というのは、「問題がどのようなものか」に依存するのでした。このあたりの詳しい話は実況中継シリーズ 「開発現場で役立たせるための設計原則とパターン」 #builderscon 2018を参照してください。

さて、FizzBuzzについて、「どの部分に今後の変更がありえて、どの部分に今後の変更がないのか」を考えてみましょう。それを考えると、実は「FizzBuzzは完成された問題であり、今後の変更はない」という答えが見えてきます(!)。そうである以上、上のふたつの設計のどちらが正しいかなんてありません。問題が十分に小さいため、どちらのコードもそれなりに可読性があるし、あとはもう趣味の問題です。好きな方にすればいいと思います。

さて、ドメインレイヤーの設計が終わったところで、ユースケースのことを考えてみましょう。ユースケース

ものなのでした。しかし、今回の例に「複数のドメインモデルやインフラストラクチャ層のオブジェクトをオーケストレーション」する必要はあるでしょうか?「ない」というのがその答えではないでしょうか。そうなると、FizzBuzzという問題領域において、ユースケース層の役割は「無い」ということになります。プレゼンテーション層から直接このドメインサービスを叩くだけで良いでしょう。つまり、そもそもFizzBuzzを真面目に設計すると、多層からなる設計は不要というか、多層からなる設計をしようとしたら「役割のない層」が生まれてしまうという、考えてみれば当たり前のところに落ち着いてしまうわけです。

そして、ユースケース層の役割が「無い」ような問題領域を例にとって「ユースケースとはなにか?」を考えても、おそらく意味のある答えは期待できないでしょう。それを考えると、どうしても「これはそもそも問い自体が不適切である」という結論が出てきてしまうのです。

まとめ

このエントリの内容を要約すると、以下のようになります。

追記: