yoskhdia’s diary

DDDとかプログラミングとかアーキテクチャとか雑多に

UseCaseの再利用性

Clean ArchitectureにはUseCase層が定義されていますが、このUseCaseが一体どういうものなのか度々わからなくなるので、自分の考えをまとめてみるエントリです。

Clean Architectureについてはこちら

8thlight.com

日本語訳:クリーンアーキテクチャ(The Clean Architecture翻訳)

以降、概念を”ユースケース”、実装されるモノを”UseCase”と表記することにします。 (同じっちゃ同じなんですが、指してるものがところどころ変わるので表記分けをしています。) また、Webアプリケーションを想定しています。

ユースケースとは何なのか

Clean Architectureから抜粋します。

Use Cases

The software in this layer contains application specific business rules. It encapsulates and implements all of the use cases of the system. These use cases orchestrate the flow of data to and from the entities, and direct those entities to use their enterprise wide business rules to achieve the goals of the use case.

We do not expect changes in this layer to affect the entities. We also do not expect this layer to be affected by changes to externalities such as the database, the UI, or any of the common frameworks. This layer is isolated from such concerns.

We do, however, expect that changes to the operation of the application will affect the use-cases and therefore the software in this layer. If the details of a use-case change, then some code in this layer will certainly be affected.

ユースケースはアプリケーション固有のビジネスルールをカプセル化したものです。 エンティティ(ドメインオブジェクト)の持つ(企業全体の)ビジネスルールを使って、それらの調整(データの受け渡し等)を行うことでユースケースの達成を目指します。
ユースケースはあくまでエンティティを(一方的に)利用する側であるため、この層での変更(アプリケーションの変更)はエンティティに影響を与えないことが望ましいです。 また、より外層のDBやUIなどからの影響からも隔離されていることが望ましいです。

みたいなことがClean Architectureでは書かれています。

ガイドラインからユースケースを考える

以前、ユースケース難民になってから読んだ書籍「ユースケース駆動開発実践ガイド」では、以下の様なガイドラインが述べられています。

  1. (画面のような)バウンダリクラスの名前を使いなさい。
  2. ドメインクラスの名前を使いなさい。
  3. 「名詞(主語) - 動詞 - 名詞(目的語)」(日本語でなら「名詞(主語) - 名詞(目的語) - 動詞」)という文の構造に従ってユースケースを書きなさい。
  4. オブジェクトモデルの言葉を使ってユースケースを書きなさい。
  5. ユースケースは実行時の振る舞いの仕様であるということを忘れないように。
  6. GUIプロトタイプや画面モックアップを使いなさい。
  7. イベントとその応答の流れとしてユースケースを書き、ユーザとシステムの対話の両側を記述しなさい。
  8. ユースケースは叙述的に書きなさい。
  9. アクターとユースケース図を使ってユースケースを組織化しなさい。
  10. 2段落ルールに従いなさい。

一点、注意が必要なのは、ユースケース記述とユースケース図は異なるということです。 このガイドラインユースケース記述のガイドラインです。 ですが、ユースケース図の粒度(一言で表現するもの)は全然違うかというと、気をつけることは同じです。ユースケース図はユースケース記述の目次です。
UseCaseはこのユースケース記述をもとに実装します。*1 言い換えると、UseCaseに何を含めれば良いかは、ユースケース記述を書いてみるとわかりやすい、ということですね。

ユースケース記述は、「ユーザがAを行うと、システムはXを応答する。ユーザはXを確認してBを行う。システムはBを確認してYを応答する。......」のようにユーザ(アクター)とシステムの相互作用を記述するものです。 これがユースケースを実装する際の難しさなのではないかと思っています。 システムが応答する時点でHTTPのようなステートレスなプロトコルでは区切りとなるため、一つのUseCaseとして実装しづらいためです。

一方で、ユースケース記述の文量はあまり多くならないようにします。(2段落ルール) これに従う限りはユースケースの粒度はアクターがシステムを使うシンプルな操作にとどまることになるでしょう。 すると、システムを使って何かの目的を達成するためにユースケースの前後関係があらわれ、ユースケース図によってその関係性を図示したりします。

f:id:yoskhdia:20161018092821p:plain

ここから分かるのは、ユースケースと一口に言っても「ユースケースを呼び出すユースケース」がある、ということです。 ユースケース図では invokes として関係を表します。
UseCaseで実装する対象は、これらの大なり小なりのユースケースになると考えると、UseCase層のなかでも他のUseCaseを呼び出すことは有り得そうです。

※勿論、あるユースケースはあるユースケースの後におこる、というものもあります。典型的なものはログインなどですね。ユースケース駆動開発実践ガイドでは <<precedes>><<invokes>>ユースケースの関係は十分としています。

良いユースケースとは

基本的にはガイドラインの通りなのですが、ポイントになる視点だけ整理しておきます。

参考:良いユースケースを書くための発想法

  • 利用者(アクター)からの視点で記述すること
  • ユースケースとは、アクターがシステムを使って何ができるか、を表したものであること

ユースケースの目的は、システムを使って何ができないのかを決めることにあります。 どのようなアクションがあったとき、システムはどのような応答をするのか、という境界を決めることにあります。
書籍「ユースケース駆動開発実践ガイド」でも代替コース(想定されるユーザの違反な操作など)を可能な限り拾うことが推奨されています。 それは線引を明確にするために必要な分析です。

ユースケースのレベル

書籍「UMLモデリングのエッセンス」ではCockburnの"ユースケースのレベル"を取り上げています。

  • 海面レベル
    主アクターとシステム間の個々の相互作用を表します。このようなユースケースは主アクターになんらかの価値を与えるもので、主アクターは一般的に数分から30分程度かけてユースケースを実行します。
  • 海中レベル
    海面レベルのユースケースに包含されているという理由だけで存在するユースケースです。
  • 上空レベル
    海面レベルのユースケースがさらに広範囲なビジネス上の相互作用の中で占める位置を示します。

上空レベルのユースケースは、通常ビジネスユースケース*2ですが、海面および海中レベルはシステムユースケース*3です。ソフトウェアの観点ではユースケースは海面レベルにします。

余談)includes

ユースケースの中に複雑なステップがある場合などは「包含(include)」とも言いますが、ユースケース駆動開発実践ガイドでは不要として切り捨ててます。*4 また、書籍「UMLモデリングのエッセンス」でも、以下のように述べられています。

ユースケースの包含は、そのままではメインシナリオがわかりにくくなるような複雑なステップや、複数のユースケースで繰り返されるステップに使用すると便利です。しかし、機能の分割によってユースケースをサブユースケースやサブサブユースケースに分けるといったことはしないでください。このような分割は多大な時間の浪費です。

再利用性はあるのか

ユースケースは、ユーザ(アクター)とシステムの対話がベースにあります。

ユースケースは基本的には再利用されるものではないと考えています。 なぜなら、場面がすべて違うものであるはずだからです。
ただし、前述のようにユースケース図を書いたとき、invokesの関係(とprecedesの関係)が現れることがあります。 再利用したいという要求は前述の invokes を受ける側のユースケースに対して起こるもののように思います。

ユースケースのレベル」節の話を使えば、前者は海面レベル、後者は海中レベル、となるでしょう。 海面レベルのユースケースは再利用するものではなく、海中レベルのユースケースは他のユースケースから利用される、と言えるのではないかなと思います。

ユースケースの独立性

一個のユースケースとして独立性は保つことが重要と考えています。 例えば、元々AというユースケースがあってB, Cのユースケースからinvokesされるとします。 ここでアプリケーションへの要求が変わり、Aを修正する必要が出たとき、あるワークフロー(B→A)では修正が必要でもう一方のワークフロー(C→A)では以前の動作である必要がある、という場合、Aは異なるユースケースとした方が良いかを一考する必要があると思います。 なぜなら、ユースケースはステップの集合であり、そのステップに差があるなら異なる目的をもったユースケースであると思われるからです。その差が、ユースケースの"拡張"として許容できるくらい小さく妥当なものなら良いですが、油断すると"拡張"ステップが増大することになりかねません。

現実問題、ユースケースが膨らんで困る

ということもあるんじゃないかなーと思います。

ユースケースはシステム内外の相互作用を表すものであるので、ある一つの操作で叶えたいシステムの振る舞いが大きければ、ユースケースも大きくなるような気がします。 まずは、ユースケースの目的が多すぎることを疑うべきとは思いますが、昨今のプロダクトは魔法のワンクリック*5が持てはやされてもいるので、ユースケースに含まれるステップも多くはなってしまうのかもしれません。

では、このときどうするかですが、「アクターはユーザである必要はない」という点がヒントになるかもしれません。 ユースケースにおけるアクターは役割と呼ぶ方がより正しいです。 ですので、例えば、ユーザの代わりにシステムと相互作用を行うアクターを用意して、ユースケースを小さくしていく、というモデリングが使える可能性があります。

コードにすると、どうなるのだろう

何かしらサンプルコードを出すつもりだったんですが力尽きました......。 続く(かもしれません)

まとめ

ユースケースにはレベルがある。 海中レベルのユースケースは再利用される可能性がある。 だけど、ユースケースは極力独立している方が良いと思うので、再利用ではなく目的や責務が保たれることを目指そう。

参考

8thlight.com

www.shoeisha.co.jp

www.shoeisha.co.jp

*1:ただし、ユースケース駆動開発実践ガイドで述べられるICONIXではユースケース記述からロバストネス分析を行い、シーケンス図に書き出して実装につなげます。

*2:顧客やイベントに対するビジネス上の対応を問題にする。

*3:ソフトウェアとのやり取りに関わる。

*4:一方でUMLモデリングのエッセンスでも、extendは無視せよと切り捨てている。invokesも出てこないので、ユースケース図においては厳密な関係定義よりも前後が分かることの方が重要です。

*5:少ない操作で多くの効果を得るナニカ