実践 ICONIXプロセス : 詳細設計のまとめ

詳細クラス図を作る

ICNONIXプロセスの詳細設計は、詳細クラス図を作る作業です。

ユースケースを絵にする

ロバストネス分析で、ユースケースを絵にしてユースケースを検証しました。(ロバストネス分析)
詳細に設計するために、今度も、ユースケースを絵にしてみます。(シーケンス図)
ロバストネス分析では概念クラスで絵にしました。
詳細設計では、実装クラスを追加して、ユースケースの実現手段を絵にします。

シーケンス図

詳細クラス図を作るために、ユースケース記述のステップを追いながら、メッセージを追加していきます。
シーケンス図作成は目的ではなく、詳細クラス図を作るための手段です。

特に、クラスに操作を割り当てるために、シーケンス図を使います。

シーケンス図は、目的ではなく手段です。
目的は、詳細クラス図の完成です。

詳細クラス図を検討する

詳細設計は、詳細クラス図から始まり、詳細クラス図で終わります。

シーケンス図を書き始める前に、詳細クラス図を念入りに検討し、その時点で発見・追加できるクラスは追加しておくべきです。

アプリケーションのデザインパターンがあれば、それを、シーケンス図を描き始める前にクラス図に追加してもかまいません。

大半の時間を、詳細クラス図(静的モデル)の検証と改良に使ってもかまいません。
ただし、ユースケースを実現していることを保障するために、かならず、シーケンス図で、ユースケースのそれぞれのステップをどう実現するかを確認すること。

レビューの準備

詳細設計のレビューに進む前に、詳細クラス図を時間をかけて念入りに検討します。
コードに落とし込める状態、コードを自動生成できる状態になっているか?

別のやり方

単純なシーケンス図を作成して、コーディング環境でクラス図を完成させる、というやり方もあり(だそうです)。
私は、このやり方は進めません。設計とコーディングの分離があいまいで、結局、設計にもコーディングにもバグが紛れ込みやすい、やり方だと考えるからです。


実践 ICONIXプロセス : シーケンス図アンチパターン

ICONIXプロセスでは、シーケンス図は、ユースケースを実現する実装クラス図を完成させるための手段です。
一般的なUMLシーケンス図の描き方からは逸脱するところがある。
重要なのは、手段より目的。

シーケンス図を描く時に犯しがちな誤り。

活性区間

オブジェクトの活性区間は、重要な概念です。
しかし、問題は、活性区間を思うように描くのはかなりたいへん。Enterprise Architect は良く出来ているモデリングツールだと思いますが、それでも、活性区間は思うように描けない。特に修正がたいへん。

活性区間にこだわるのは、やめましょう。
ICONIXプロセスのシーケンス図は、クラス図に操作を定義するための手段です。このためには活性区間は何も意味を持ちません。

最初から、活性区間の表示をオフにしてシーケンス図を描けばそれで十分です。

内部の動作

シーケンス図を描き出すと、フレームワークのオブジェクトまで描き出す人がよくいます。あるユースケースを実現するためのオブジェクトの協調動作を正確に描こうとすれば、そこまで描くことは必要。

しかし、ICONIXプロセスのシーケンス図の目的には、そこまで描くことは何の役にも立ちません。
むしろノイズが多くなって邪魔になる。 内部の深くまで踏み込み過ぎですね。

重要なのは、自分たちが設計するクラスに操作を割り当てることです。ただ、これだけが目的です。
ですから、内部のオブジェクトに深入りはしないこと。

フレームワークの内部の動作をシーケンス図で描いてみることは、勉強としては、とても良いことだと思います。
でも、ICONIXプロセスのシーケンス図作成(詳細設計)の作業とは全く別です。

ICONIXプロセスで言えば「テクニカルアーキテクチャ」の作業の一部として、フレームワークの動作を描いてみることは役に立つかもしれません。

順番の整合性

シーケンス図は、名前の通りオブジェクトの動作の順番(シーケンス)通りに描いていくのが原則です。

ICONIXプロセスでは、ユースケース記述の実現を描くことが目的。ユースケース記述には、基本コースと(複数の)代替コースがあります。ひとつひとつのコースは順番に描くことができますが、全体としては、一つのシーケンスにはなりません。

元もとのシーケンス図の記法からすれば、コースごとに別のシーケンス図を描くのが正しい。

しかし、ICONIXプロセスでは、代替コースを含めて、一枚にシーケンス図にひとつのユースケース記述を絵にすることを強調します。

シーケンス図を描くことは、ユースケースの実現方法を検討することが目的。そして、代替コースだけ独立させて検討すると、ユースケース全体の整合性が崩れても気が付かない。何か代替コースについて問題が起きたり、修正が必要になった時の、他のコースとの関連や影響を見落とすことになる。

ユースケースを小さな単位に分割することは良いことですが、ユースケースを基本コースと代替コースという個別のコースにばらばらにすることは、良いことではありません。

実践 ICONIXプロセス : 設計の腕を上げる

シーケンス図を描きながら、詳細な実装クラス図を完成させるのが、詳細設計作業。

この設計作業の腕を上げるには?

幅広い知識と経験が必要だと思う。

・問題領域、業務の知識
・ドメインモデリングのパターン
・UIのデザインパターン、特に、エラー時の画面の振る舞い
・アプリケーションアーキテクチャのパターン
・フレームワークのメカニズム

私の勉強方法や参考になった情報は以下の通り。

問題領域・業務の知識

・その会社の事業概要や組織図。採用ホームページとか新人研修用のテキストが結構、参考になる。
・業界の解説本。これも就職用の解説本が役に立つ。
・業務の解説本。販売とか、在庫管理とか、顧客サポートとか、採用とか、いろいろある。

インターネットでも参考になるページもあるけど、基本は書籍。

書籍のほうが「問題領域(ドメイン)」の用語を体系的に整理してあるから。

ドメインモデリングのパターン

Eric Evans の Domain-Driven Design
Pavel Hruby の ビジネスパターンによるモデル駆動設計
David Hay の Data Model Patterns

UI のデザインパターン

ソシオメディアがインターネットに公開している UIデザインパターン

アプリケーションアーキテクチャ

ファウラーの エンタープライズ・アプリケーション・アーキテクチャ・パターン

フレームワークのメカニズム

Spring のドキュメントと解説本

---

私は、ドメイン駆動派(?)なので、この順番で重要だと思っている。
もちろん、実装よりの知識も重要だけど、まあ、ここは腕自慢の人がいることが多いので、そういう人にまかせることが多くなってきた。

実践 ICONIXプロセス : 設計と実装の分離

ICONIXでは詳細設計は、シーケンス図を描きながら詳細クラス図を完成させる作業です。
シーケンス図は、ユースケース記述を実現することを考えながら描きます。

詳細設計で、すべてのクラス、すべてのパブリックメソッド、主要なプライベートメソッドは、シーケンス図のメッセージとして描き、同時に、詳細クラス図の操作として追加します。

私もそうでしたが、かつては、こういう作業の多くは、ソースコードを書きながら、いろいろやっていたような気がする。
呼び出し側のクラスと呼ばれる側のクラスのソースファイルを行ったり来たりしながら、メソッド名を考え、コードに追加し、コンパイル&ゴーを繰り返す。

オブジェクト間の連携、メソッド呼出を、すべてシーケンス図と詳細クラス図で定義するのは、設計と実装(コーディング)の作業を完全に分離することですね。

実装(コーディング)は、(自動生成した)クラスファイルの空っぽのメソッドの中身を書く作業だけになる。
この分離の効果は大きいです。

設計、つまりシーケンス図とクラス図で考えているときは、メソッドの中身は一切考えません。
実装、つまりメソッドの中身を書いているときは、そのロジックだけに集中できます。

Do one thing, do it well !

という言葉がぴったりです。

エディタで設計しながら実装していた時には気が付かなかったのですが、クラスへの操作の割当作業が、いかにたいへんで、また、設計で良い仕事をすることが、理解しやすく、変更が安全なコードを書くコツだということを実感しました。

ユースケース駆動開発実践ガイドでは、このことを「プレファクタリング」と説明していますね。
メソッドの名前変更や、メソッドの移動は、リファクタリングの基本テクニックです。
しかし、実装の前に、シーケンス図とクラス図で操作の割当をしっかり検討しておくと、この種のリファクタリングの必要性は激減します。

設計作業はたいへんですが、コーディングの前に、じっくり考えたほうが、結局は、読みやすいコード、変更が安全なコードを楽に書くことができますね。

実践 ICONIXプロセス : 静と動、要求と実装

詳細設計は、さまざまな要素が交差する、設計の難所であり、ハイライトです。
ICONIXプロセスでは詳細設計は、シーケンス図を描きながら、詳細クラス図を完成させる作業です。

静と動の交差

ICONIXプロセスは、静的モデルと動的モデルを同期させることを重視します。
特に、動的モデルを作成しながら、静的モデルを更新することがポイント。

画面の紙芝居や、要求のメモ書きから、業務の用語(問題領域の用語)を抜き出して、ドメインモデル図(概念クラス図)を描きます。2時間くらいでざっと描くことを推奨。最初の静的モデルですね。

次に、ユースケースを記述します。最初の動的モデルですね。
ユースケースを書く時に、必ず、ドメインモデル図のクラス名(業務の用語)を使うこと、つまり、静的モデルと動的モデルが同期していることを徹底します。もちろん、必要なら、ドメインモデル図にクラスを追加します。

ユースケース記述は、次のステップで、ロバストネス図として絵にしてみます。絵にしながら、ユースケースの問題点を発見・修正します。同時に、静的モデルのクラス図に属性を追加したり、改良を続けます。

動的モデルの最後がシーケンス図です。ロバストネス図を元にして、シーケンス図で、ユースケースの実現方法を、実装クラスとその操作として定義していきます。静的モデルのクラス図で詳細に完成させます。

シーケンス図とクラス図の同期は、Enterprise Architect などのモデリングツールを使えば、簡単にできます。

動的モデルは、

1.画面の紙芝居
2.ユースケース記述
3.ロバストネス図
4.シーケンス図

として発展させていきます。
この動的モデルの洗練作業と並行して、静的モデルも概念クラス図を、詳細化し、実装用のクラスを追加し、詳細クラス図を完成させ、コードを生成します。

シーケンス図の作成は、静的モデルと動的モデルの交差の最終段階ですね。

要求と実装の交差

ロバストネス分析までは、要求の確認・整理の作業です。
ロバストネス図を描き、そのレビュー(予備設計レビュー)を終えた時点では、ユースケース記述はロバスト(
強靭)になり、画面のモックアップが完成し、ビジネスルールなどの補足仕様も明確になっています。

ドメインオブジェクトも、業務に必要な属性は、この時点までに定義済みです。

並行して、予備設計レビューの完了までには、実装の最初の作業であるアーキテクチャの検討し確立しておきます。
Web アプリケーションであれば、実装言語を決め、フレームワークを選定し、リファレンス実装をすませておく。

そして、シーケンス図の作成が、要求と実装の最初の交差点になります。
シーケンス図を描きながら、詳細クラス図にさまざまな実装クラスを追加し、ユースケースを実現するためのオブジェクトの協調動作を設計していきます。

具体的には、シーケンス図にメッセージを追加、つまり、操作をクラスに割り当てる作業を続けます。

実際にやってみると、このシーケンス図の作成がヘビーな作業になりすぎている感じがします。
もう少し、この要求と実装の交差を楽に確実にできないか、やり方をいろいろ検討しているところです。

実践 ICONIXプロセス : 問題空間から解決空間へ

ICONIXプロセスでは、ロバストネス分析(予備設計)までは、問題空間( Problem Space )だけに集中します。解決空間( Solution Space ) つまり実装は、詳細設計(シーケンス図作成)から始まります。

問題領域(ドメイン)は、ドメインモデル(概念クラス)とユースケースモデルで記述します。
ICONIXプロセスでは、ドメインモデルは、詳細クラス図に発展し、そのままコードになります。
ユースケースモデルは、ロバストネス分析を経て、シーケンス図になり、実装設計に反映します。

インフラの足場クラスの登場

解決空間には、さまざまな実装用のオブジェクトが登場します。永続化メカニズムの実装、Web アプリケーションの MVC の実装などのオブジェクトですね。採用したフレームワークと、アプリケーションアーキテクチャパターンによって、大半のオブジェクトは決まります。

多重度

ICONIXプロセスでは集約の多重度は、ようやくこの段階で登場します。
ようするに、Book book ; で実装するのか、 List<Book> books ; で実装するのか決めなければいけないので、この時点では、多重度は重要になる。

ここまでは、多重度はそれほど重要でないし、クラス図に描くべきではない、というのがICONIXプロセスの主張。
データモデルでは、多重度の明示と検証は重要だが、クラス図では、オプションである。 Java で実装するときに、検討・決定すれば、十分、ということのようです。

まじめな(?)モデル論から見れば、かなり乱暴なやり方ですが、実践的には、この程度の位置づけで十分な気もします。

書籍一覧と実装

概念クラスが実装では消える(!)ことがあります。典型的なのが書籍一覧( BookList ) が実装クラスでは、List になって、独立したクラスではなくなるパターンです。

「ユースケース駆動開発実践ガイド」では、シーケンス図作成の過程で、永続化メカニズムの BookDao オブジェクトを導入し、List<Book> getBookListByCategory( int categoryID ) メソッドを割り当てた段階で、BookList は、消えるといっていますね。
図から消すだけなのか、モデルから削除すべきことを言っているのか、意図は不明です。

概念モデルで発見したクラスを、実装方法の決定時に削除というのは、おかしいので、詳細クラス図からは消して、概念モデル図には残す、というのが良いように思う。

実践 ICONIXプロセス : シーケンス図とクラス図

ICONIXプロセスは、動的モデルと静的モデルの二つの流れが基本構造。

動的モデル

画面の紙芝居→ユースケース→ロバストネス図→シーケンス図

静的モデル

ドメインモデル(概念クラス図)→実装モデル(詳細クラス図)→コード

動的モデルと静的モデルの同期

コードは(静的モデルの)詳細クラス図から作成する。(できれば自動生成する)
しかし、実装の設計は、(動的モデル)のシーケンス図で行う。シーケンス図を描きながら、操作(メソッド)をクラスに割り当てる決定をしていく。

実際には、モデリングツールを使って、シーケンス図を描く作業をしながら、詳細クラス図も同時に更新してしまうところがポイント。

動的なシーケンス図で考えながら、静的なクラス図も更新する。

シーケンス図でクラス設計

デザインパターンと呼ばれるものは(静的な)クラス図での説明を良く見かける。
Eric Evans のドメイン駆動設計(DDD)でも、中心は、クラス図である。
クラス図があって、サンプルコードがある、という説明が多い。

クラス設計というと、クラス図つまり静的モデルのイメージが強く、シーケンス図でクラス設計、というのは、違和感があるかもしれない。

しかし、ICONIXプロセスでは、シーケンス図で詳細設計することを、強調している。その意図は「ユースケース記述」の実現を考えながら、詳細設計をすべきである、ということ。
ICONIXプロセスのシーケンス図の描き方は、ユースケース記述の一行一行を、オブジェクトのメッセージに割り当てていく作業。ユースケースと実装を密接に関連づけることを重視している。

詳細クラス図とクロスチェック

もっとも、ガイドブック「ユースケース駆動開発実践ガイド」を良く読むと、シーケンス図を描きながら、詳細クラス図を更新し、詳細クラス図から、設計上の問題の発見や解決策を検討することをちゃんと説明している。

詳細クラス図は、そのまま(できれば自動的に)コードにするので、シーケンス図で実装を設計するという意味は、シーケンス図を描きながら詳細クラス図を完成させる、という意味。

ドメイン駆動設計では、あまりシーケンス図はでてこないが、文中には、コード例や、実装時の動作を追いかけながらモデルを検討し、改良する箇所が良くでてくる。
つまり、シーケンス図で実装時の動作を絵にしながら、詳細クラス図を検討し改良するICONIXプロセスのやり方と同じですね。

実践 ICONIXプロセス : メソッドの割り当て先のクラス

ユースケース記述を読みながら、ロバストネス図のコントロールをひとつずつ、シーケンス図のメッセージに割り当ていく。(クラスの操作に割り当てていく)

レイヤアーキテクチャと設計方針が明確であれば、この作業は比較的容易です。

私たちの場合

・画面(バウダリ)から出発する最初のコントロールは、サービスクラスのメインメソッドになる。
OrderService の submit()
 サービスクラスは、ドメインオブジェクトとリポジトリクラスに役割を委譲する。

・検証コントルールは、ドメインオブジェクトに割り当てる。
 Spring の validator インタフェースの実装ですね。

・エンティティの保存は、リポジトリクラスの save() メソッド

という感じです。

検索

検索のリクエストは、検索条件オブジェクトをつくる場合と、ドメインオブジェクトをそのまま設定条件につかう場合があります。 
find By ID のような機能は、ドメインオブジェクトをそのまま使う。
複合条件や範囲指定は、searchCriteria オブジェクトを別に用意する。
Eric Evans が DDD で説明している Specification パターンも興味があるけど、今のところ研究対象。

検索のメソッドは、リポジトリクラスに割り当てます。
findByID()
findByName()
findByDate()
...

ICONIX プロセスでは、それぞれの検索パターンごとにユースケースを記述します。(汎化はしません)
ですから、ユースケースをひとつ実装するたびに、リポジトリクラスに、findXXX メソッドが増えていくことになります。

ドメインの知識

設計方針としては、業務の知識はできるだけ、ドメインオブジェクトに集約することにしています。

会員情報に関する知識は、できるだけ MemberProfile クラスに集めるようにしている。
入力の妥当性検証ロジックとか、テキスト形式のフォーマッティングとかは、会員情報を扱う業務の基本知識ですので、これらに関する知識は、ドメインオブジェクトの MemberProfile に集めるようにする。

検証ロジックは、Validator オブジェクト、フォーマッティングは、FormatUtil という設計も過去に試したことがあります。
現在は、ドメイン(問題領域)の知識はドメイン層のクラスに集める。この方針を徹底しています。

理由は、単純に、コードの調査や変更が楽だから、ということにつきます。
ロジックと情報を一か所に集めたほうが、扱いやすい。

かっこよく言えば、オブジェクト指向らしく、ドメイン駆動っぽく、ということになるのかな?

実践 ICONIXプロセス : メッソドはどこのクラスに?

クラスは一通り発見できました。
あとは、クラスが協調して、ユースケースを実現できるように、クラス間のコミュニケーションを設計しなければいけません。

シーケンス図でいえば、メッセージの定義、クラス図だと、操作をクラスへ割当てる、コードへの実装だと、メソッドの定義ですね。

作業は簡単

Enterprise Architect のようなモデリングツールを使えば、これは、一つの作業としてやることができます。
シーケンス図上でメッセージを追加すると、自動的にクラスの操作として追加でき、コードの自動生成機能で Java コードのメソッド宣言のコードを作れます。

最難関

図を描く作業は簡単ですが、設計としては、最も難所にさしかかっています。

どのクラスにどの操作(メソッド)を割り当てるかは、概念ではなく、現実の意思決定だし、その決定がそのまま実装クラスの構造になります。

コードが理解しやすく変更も安全で容易になるか、理解不能で副作用が怖くて変更ができないスパゲティになるかは、ここでの意思決定が分岐点になります。

設計の神髄だし、上級の技術者とかけだしの技術者の腕がもっとも違うところですね。

基本パターンの勉強

操作の割当(メソッドの設計)のパターンは、最近は、勉強すればたくさんの良い情報が手に入ります。

特に、以下の6冊はお薦めです。

レベッカワーフスブラックの オブジェクトデザイン
Eric Evans の Domain-Driven Design (DDD)
Pavel Hruby の ビジネスパターンによるモデル駆動設計
マーチンファウラーのエンタープライズ・アプリケーション・アーキテクチャ・パターン (PoEAA)
同じく リファクタリング
Kent Beckの Implementation Patterns

私は、これらの本で、どういうクラスを、どういう操作(メソッド)を割り当てるかの考え方と基本パターンを勉強しました。

特に、なぜ、こういうパターンが良いか、あるいは、なぜ、こういうパターンは悪いかというという、考え方・判断材料をいろいろ勉強しました。

共通していることは、唯一のベストパターンはない、ということです。何を重視し、何をあきらめるかによって、いろいろパターンがある。

重視すること

私は、基本的には、
・読みやすいコード
を最も重視しています。

・大きなクラス、大量のメソッドを持つクラスは読みにくい。
・長いメソッドは読みにくい
・名前の省略形は読みにくい
・問題領域の言葉(業務用語)中心だとコードの意図が分かりやすい
...

まあ、これだけで意思決定できないケースが多いんですけど、コードの基本価値として、ここにはこだわっている。
実際に、それが、分かりやすく、変更が安全で容易なコードを作ることに効果があがっていると思う。




実践 ICONIXプロセス : サービスクラス

サービスクラスは、アプリケーションサービスを抽象化したもの。

例えば、
・書籍をキーワードで検索する SerarchByWordService
・注文する OrderService
・ログインする LoginService
など。

ユースケースとサービスクラス

ICONIXプロセスでは、ユースケースを比較的、小さい単位に分割するので、一つのユースケースが、一つのサービスクラスになることが多い。

ユースケースの precedes, invokes の関係も、サービスクラスを順番に実行する、サービスクラスをサブルーチンとして呼び出す、という実装の設計と直結している。

モデルと実装

ICONIXプロセスでは、初期のドメインモデルは、実装クラス設計に直結するし、ユースケース記述は、サービスクラスの設計に直結する。 モデルをそのままコードまで落としていくことを重視している方法論ですね。
やってみると、実践的でやりやすい。

サービスクラスの発見

サービスクラスを、どの時点でクラス図に加えるべきか? (どの時点で発見するか?)

A.ユースケースパッケージ図でユースケースを定義した時?
B.初期ドメインモデルで、発見してしまう。(ユースケース候補にしてしまう)
C.シーケンス図を描くとき (ロバストネス図のコントロールの受け皿)

最初はC.かと思っていた。B.もかなり有望な気がする。実際に、中核のユースケース(=サービスクラス)は初期ドメインモデリングの段階で、発見していることが多いから。

今後の検討課題ですね。

calendar
     12
3456789
10111213141516
17181920212223
24252627282930
31      
<< March 2024 >>
システム設計日記を検索
プロフィール
リンク
システム開発日記(実装編)
有限会社 システム設計
twitter @masuda220
selected entries
recent comment
recent trackback
categories
archives
others
mobile
qrcode
powered
無料ブログ作成サービス JUGEM