実践 ICONIXプロセス : アーキテクチャ

ICONIXプロセスでは、ロバストネス分析が終わったら、実装の設計(シーケンス図作成)に入ります。

ただし、ロバストネス図は、概念設計なので、実装に進むには、実装手段を決めなければいけません。

最も単純なのは、プログラミング言語ですね。大昔であれば決定事項はOSと言語くらいです。あとは、とにかく自分たちでコードを書く。

最近は、特にWebアプリケーションの分野では、大きく状況がかわりました。サーバーとフレームワークを選ぶことで、ソフトウェアの骨格を用意でき、便利な部品がいっぱい手に入ります。

私たちの場合だと、

・Apache
・Tomcat
・Spring MVC
・Web Flow
・Velocity
・iBatis
・PostgreSQL
・Spring Security

などを使っています。

開発ツール類は、Enterprise Architect, Eclipse, Subversion, JIRA, など。

Webアプリケーションを作るために、良く出てくるパターンは、部品が揃っていますから、コードを書く量は激減しました。

例えば、Java では、 for 分を書くことは、ほとんどなくなりました。
データベースから取得したレコードセットを Collection で扱う処理は、iBatis で済ませるので、for 文はいりませんから。 メモリ上のコレクションを for 文で処理することは、ほとんどないですね。

表示の時に、Velecity のテンプレートファイルで、ループさせるくらいかな?

開発は、基本的に以下の作業だけです。

・ドメイン層のクラスの実装
・アプリケーション層のクラスの実装

・iBatis SQLマップファイルで、SQLの定義

・Velocity テンプレートファイル
・Web Flow 定義ファイル

どのクラス名、メソッド名、ID名も、基本的にドメイン(問題領域)の用語だけです。
Spring の定義ファイル内で、フレームワークのクラスやメソッド(つまり技術用語)を一部使いますが、中心ではない。

Spring は、ドメインオブジェクトを活用することを重視しているので、ドメインの分析・設計・実装だけやれば、あとは、お約束に従ってコーディングしておしまい、という感じです。

もちろん、そうなるまでには、フレームワークの選定や習得にだいぶ回り道をしました。
また、今でも、フレームワークのバージョンアップなどは、本番環境も含めると、かなり頭の痛い問題です。

ただ、フレームワークの選定・習得をがんばれば、開発が楽になることは身にしみてわかっていますから、がんばりどころだと思っています。

ICONIXプロセスのガイド本「ユースケース駆動開発実践ガイド」は、アーキテクチャは、Java + Spring なので、私たちにはとても分かりやすかった。

DAO や O-R マッピングのところは考え方が違っていたり、本は jsp で、私たちは velocity とかありますが、たいした問題ではありませんでした。

本や私たちとは違ったフレームワークを使っている人でも、基本はいっしょだと思います。

いずれにしても、シーケンス図(実装設計)の前に、アーキテクチャを確立しておきましょう。
確立するということは、チームで「習熟」しているし、トラブルシューティングの経験・ノウハウもある、ということです。


実践 ソフトェア・アーキテクチャ : 楽をしたい

トップダウン

考え方とか概念から、だんだん具体化していくやり方。

余計なことを捨て去った(抽象化した)基本事項を押さえたところから始めるので、効率的といえば効率的。でも、具体化の途中で、どこかに迷い込んじゃいそう。

ボトムアップ

実際に作って、問題にぶちあたり、問題と格闘しながら、覚えていく。
抽象化したアーキテクチャ論も、もともとは、こういう現場レベル、実践レベルの格闘から生まれたものなんだと思う。

世間では解決済みの問題と格闘するのは非効率ですね。自分たちが始めて出会う世紀の発見なんて、まず、ないでしょうね。学ぶにしかず。

ミドルアウト

中核になるもの、骨格になるものをまず作って、それを膨らませていく。

トップダウンだと、抽象的な概念からスタートする。
ボトムアップだと、具体的な実体、コードでスタートする。

ミドルアウトは、フレームワークや(実践的な)アーキテクチャパターンのように、ある程度、具体的な形はあるが、詳細は未実装、というところから出発する。

トップダウンの具体化の過程の前半を省略したやり方といえる。
ボトムアップの設計の改良(リファクタリング)の成果への先回りともいえる。

純粋なトップダウンやボトムアップよりは、楽ができることはまちがいない。

ミドルアウトをもっと生かすために、トップダウンの勉強や、ボトムアップの経験が大切。

実践 ソフトウェア・アーキテクチャ : 結合度を簡単に判定する

レイヤやパーティンションは、コードレベルでは、パッケージの設計と実装になりますね。

パッケージ間の参照関係 ( import 関係 ) が複雑だと、ソフトウェアは理解が難しく、変更もたいへんになる。

結合度を低く保つことも、設計の重要テーマですね。

結合度の実践的なチェックポイントは、import 文の数。

import 文が多い=依存するパッケージが多い=結合度が密
import 文が少ない=依存するパッケージが少ない=結合度が疎

ということですね。

もう一つのチェック方法は、単体テストの難易度。

単体テストが難しいモジュールは、なにかと密結合しているから。
単体テストが易しいモジュールは、疎結合。結合度が低い。

単体テストをやるのに、山ほど依存コンポーネントを集めたり、モックコードを書くようではだめ、ということですね。

単体テストの障害になる箇所を、特定し、それを改善することで、設計の質を向上できる。

Spring は「テスト容易性」を重視したフレームワークです。Springが提供するさまざまなメカニズムやその背景にある考え方は、とても参考になります。

例えば、Spring では、SQL 例外の扱いで、2つの利便性を提供します。

a. ベンダー依存の SQL 例外を、一般化した SQL 例外モデルにマッピングする
b. 一般化した SQL 例外は、runtime 例外 ( コード内で try / catch は不要 )

Spring を使うと、結合度を低くする設計方法とそのメリットを具体的に知ることができます。

実践 ソフトウェア・アーキテクチャ : パッケージに凝集する

レイヤー構造にして、パーティショニングして、それでも要素が膨らむ場所は、サブパッケージつくって、コードの置き場所を明確にする。

関心事ごとに、入れ物が決まっていると、ソフトウェアが作りやすいし、理解がしやすく、変更も容易になる。

でも、実際には、こういう構造になっているのに、理解が難しく、変更が容易でないソフトウェアはたくさんあります。

よくあるアンチパターン

・ある入れ物に集めた要素(クラス)を眺めても、共通点がぴんとこない
・入れ物との名前と中身が一致しない(ものがある)
・関連するクラスに思えるのに別の入れ物に分かれている。

典型的なの悪い例が、 java.util パッケージ。 List と StringTokenizer と Timer が同じパッケージなのは、私にはわけがわかりません。

Timer は、関連項目 Thread と同じパッケージか、関連を明示したパッケージに置くべきだと思う。 

StringTokenizer は String に関連づけるべき。 まあ、String#split が追加されたので、状況は改善されましたが、今度は、String クラスが入れ物として膨らみすぎて、怪しくなってきている。

こういう「関連したものを集める」ことを凝集度を高める、と言いますね。

一番いいのは、事前の説明やドキュメントやコメントなしに、パッケージ名だけど判断でき、実際の中身も、名前と一致していること。(名は体をあらわす)

実際には、レイヤ、パーティション、パッケージ、それぞれ、どういう視点で関連するものを集めるかが、チームメンバー間で理解が違っていることが多い。

この状況の検知と改善には、パッケージ図が良いと思っている。

パッケージ図をちょっと描けば、すぐに理解の違いが明らかになるので、それをネタにいろいろ話をする。相手の考えを聞いたり、こちらの考えを伝えるには手軽で確実な手段だと思います。

実践 ソフトウェア・アーキテクチャ : パーティショニング

レイヤ構造にして、レイヤごとに関心を分離できた。
実際のアプリケーションだと、横の分割だけでは不十分。

それぞれのレイヤごとに大量のクラス、ファイルを作ることになる。ひとつのレイヤ内で、さらに関心を分離して整理をして、理解しやすく、変更を容易にする。

具体的には、物理的にはファイル構成とディレクトリ構成。Java のクラスに限れば、パッケージ構成ですね。

RUP では「パーティショニング」という用語(概念)で、この関心の分離を説明していた。

どういう視点で分離するのかは、私たちも正直、試行錯誤です。
上のほうのレイヤ ( UI/Web層 、アプリケーション層 ) は、アクターとユースケースのパッケージ設計を使うようにしています。

ドメイン層は、主要なドメインクラスは、だいたい集約のルートになるので、その集約関係の単位でモジュール化する。
モジュールをまたがるようなクラスや、共通コンポーネントみたいなものは、モジュール化(グルーピング)の単位やパッケージ名が悩ましいことが多い。

下のデータアクセス層は、DBアクセスについては、ドメイン層のモジュール化に近い。
その他のデータアクセス処理は、mail とか file とか message とかのシステム機能の単位になる。

分割の単位

「論理的に意味がある単位」といっても、わからないので、単純なルールを作った。

・要素が7個超えたら、分割検討サイン
・要素が10個超えたら、注意警報
・要素が20個超えたら、レッドカード

という感じ。人間が一度に考えられる要素として「最大7」というのは、いつも意識するようにしている。

現実には、いろいろ議論がでてくるケースもあるけど、まず「分割ありき」という価値感を徹底することが大切だと思っている。
放置すると、すぐに大きな一枚岩、エアーズロックパッケージを作っちゃうので。

名前

パッケージの名前をつけるのも結構悩ましい。

上の層は、ユースケースモデリングとドメインモデリングのパッケージ図(構造と名前)を基本にする。

下の層は、詳細は、フレームワークにまかせている部分が多い。フレームワークと強く関連する場所は、フレームワークや Java の API パッケージの名前も参照にするようにしている。 Java のパッケージ名は、それほど良い名前とも思えないので、Spring のパッケージ名のほうを重視している。

放置すると、util とか io とか意味があいまいな名前がはびこるので、もっと、説明的な名前をつけるように指導している。

また、インタフェースと実装クラスとか、実装方式の視点でパッケージ化したがる開発者も多い。 下のレイヤでも、ドメインモデルの構造に関連するところは、可能な限り、ドメインモデルの構造と名前を持ち込ませるようにしている。

パーティショニング(縦の分割)は、アクター、ユースケース、ドメインが、全体構造を決める。
技術方式や技術的な特性により構造を決めるのでない、という点を何度も話すんだけど、なかなか伝わらない。

レイヤ分割(横の分割)は、技術方式の特性で関心を分離しているので、比較的、すんなり聞いてくれるんですけどね。

開発者に「ドメイン駆動」を浸透させるのは、なかなかたいへんです。

実践 ソフトウェア・アーキテクチャ : アリモノを活用する

私たちが使っている主なフレームワーク

・Spring Web Flow ( 2.0 )
・Spring MVC
・Velocity

・Spring DAO
・iBatis

・Spring Security

Model と View

Web アプリケーションの画面の制御やセッション変数の管理はかなり面倒。入力フォームでブラウザのバックボタンの処理も悩ましい。
Spring Web Flow を使えば、ここらへんは全部、約束事と道具が揃っているので、楽ができる。

ドメインオブジェクト( Model ) の画面への表示 ( View ) は同じことをしょっちゅう書くし、細かい変更が多い。
Spring MVC と Velocity を使えば、画面へのレンダリングの書き方が定型化、単純化できる。
入力フォームをドメインオブジェクトにバインドする、入力データの妥当性検証も、Spring MVC の Data Bind の仕組み、Validator インタフェースを使えば、コードの見通しが良くなる。

Control

画面制御( Control ) は、Spring MVC だと、いろいろな書き方ができてしまい、コードが読みにくく、変更しにくい。
Spring Web Flow 2.0 だと、簡単な XML でコードをパターン化して整理できる。

O-R マッピング

ドメインオブジェクトの値を取り出し SQL を生成し実行。 SQL の処理結果をドメインオブジェクトに詰め込む。同じパターンだけど、読みにくいコードがあちこちに散在する。

Spring DAO と iBatis を使うと、この O-R マッピングの部分を完全に分離して記述できる。読みやすく、変更しやすい。

セキュリティ

ユーザ認証や権限の管理は、アプリケーションを横断する別の関心ごと。
Spring Security を使えば、アプリケーションのコードから、認証・権限部分を完全に分離できる。読みやすく、変更しやすくなる。

---

私たちが、レイアアーキテクチャを採用しているのは、教科書的な話ではなく、定評のあるアリモノ(フレームワーク)を使って、開発で楽をしようとした結果といえる。

それらのフレームワークが想定し、実践しているアーキテクチャがレイヤアーキテクチャなので、フレームワークを活用しようすれば、それにあわせたほうがやりやすいから。

実践 ソフトウェア・アーキテクチャ : Spring DAO + iBatis

Web 側は、Spring MVC で基本構造を決めたとしよう。

DB 側は、 Spring DAO で基本構造を決める。 Data Access を抽象化したインタフェースで、データアクセスの実装は O-R マッパーを使う。私たちの場合は、 iBatis を使っています。

データアクセスは、 MVC に比べれば、画面遷移とかセッション管理とかの問題がないので、ソフトウェアの構造としては単純。 また、 MVC の Contoroller のように、コードをごりごり書くような場所はないので、比較的安心。

もっとも、テーブル設計によっては、O-R マッピングがたいへんなことがある。
既存システムのテーブル設計だけでなく、新規に作成したテーブルも、問題のあるケースが多い。

原因としては、
・問題領域の分析が未熟
・オブジェクトの設計が脆弱
・データモデルとテーブル実装の知識・経験が不足
などが重なった結果でしょうね。

結果として、SQL 文でごりごりがんばったり、データアクセスオブジェクト内のコードでごりごりがんばる。

こういうごりごりコードがはびこりだすと、もう、理解不能で変更不能のソフトウェアにまっしぐら、ですね。

これは、レイヤ構造のアーキテクチャの問題ではありません。クラスの分析・設計の問題ですね。

実践 ソフトウェア・アーキテクチャ : Spring MVC

Web アプリケーションのアーキテクチャの勉強には、Spring MVC が参考になると思う。

どのフレームワークも良い点も悪い点もあるので、フレームワークの優劣は簡単に決まりません。
でも、MVC パターンによる関心の分離という視点で見れば、 Spring MVC は、他のフレームワークよりよくできていると思う。

Spring フレームワークは、ドメイン層のオブジェクト、つまりドメインモデルを実装した POJO を他の層で活用するという考え方です。

ドメインモデルは自分で設計・実装してね。それ以外のレイヤは、私たち Spring コミュニティ が設計・実装の best practice を提供します、という思想のフレームワークですからね。

その思想の一部として提供されている、Web アプリケーション API が Spring MVC です。 ( Servlet をさらに抽象化して、 best practice を実装したもの、だそうです)

Model

Spring MVC は M つまり Model は、ドメイン層の POJO をそのまま使うことを想定している。トランスファーオブジェクト (DTO) に変換する発想はないです。

View

さまざまな View テクノロジーを選択可能です。私たちは、テンプレート方式の Velocity を使っています。 View テクノロジーが選択可能になっている、ということは、 View についての関心をうまく分離して、インターフェース定義ができている。そのインタフェースを使って、JSP, JSF, Velocity などを View の実装技術として選択できるということです。 ここらへんの設計思想と実装の具体例は、アーキテクチャ、ソフトウェアの基本設計の参考になります。

Controller

Controller は、なんでもここにかけてしまう場所ではあります。 ドメイン層の POJO が単なるデータの入れ物にしてしまうと、ここに大量の手続きをコーディングすることになる。

ドメイン層のオブジェクトが豊かな振る舞い(メソッド群)を持っていても、アプリケーションロジック(サービス手順)にあたるよロジックは、ここにかけてしまう。

私たちは、これは問題だと思っているので、 このコントロールの部分を抽象化した Spring Web Flow を使っています。 Spring Web Flow を使うと、アプリケーションサービスは、アプリケーション層のオブジェクト( POJO ) として分離して記述してするスタイルが自然にできるようになります。

Spring Web Flow は、XML 定義だけなので、そもそも、ごりごりロジックは書けない。だから、必然的にアプリケーションロジックは、POJO bean として設計・実装することになる。

書こうとしてもごりごり書けないというの、ソフトウェア・アーキテクチャの実装方式として良いことだと思います。

たとえば、Java Servlet を直接コーディングすればなんでもごりごり書けてしまう。 JSP もそうですよね。 自由にできることを規約やセンスでしばってみても、なかなかうまくいきませんから。

Spring Web Flow 2.0 は、1.0 に比べ、かなり簡潔に記述できるようになりました。 MVC のコントロール層の記述として、理解しやすく、変更が容易になったと思います。

実践 ソフトウェア・アーキテクチャ : レイヤ構造と import 文

ソフトウェアをレイヤ構造にすると、クラスファイル内の import 文が減る、という話。

関心を分離すると、あるクラスの役割・責任はとても単純になります。

ソースコードで具体的に考えると、

・ コードの行数が減る
・ public メソッドが減る
・ import 文が減る

ようになります。

レイヤ構造、という視点で、私がコードレビューで注目するのは、import 文 の数と import するパッケージの種類

レイヤ構造のソフトウェアでは、いろいろな用途のパッケージを大量に import することはありえません。 

ドメイン層のクラスでは、 servlet や sql に関連するパッケージは絶対にでてこない。
デフォルトの lang と collection くらいで記述していないとおかしい。

UI層のクラスは、フレームワーク使ったりすると、いろいろ import が必要になることがあるけど、ちょっと待ってほしい。 さらなる階層化や、クラスの役割の分割が必要なのでは?

データアクセス層でも同じことですね。

import 文が多いクラス。例えば5つ以上の import 文が書かれたクラスは、たくさん責任を背負い込みすぎている可能性が高い。 

レイヤ構造、というソフトウェア・アーキテクチャを採用したら、徹底的に import 文のチェックをやる。 SQL 例外をドメイン層で扱うために、 java.sql をインポートする、なんていう、「アーキテクチャ(レイヤ構造)くずし」が、普通にあったりしますから。

実践 ソフトウェア・アーキテクチャ : アンチパターンが実は正しい?

Eric Evans の Domain-Driven Design ( DDD : ドメイン駆動設計 ) は、アーキテクチャのアンチパターンとして、 Smart UI を説明している。

画面ごとのプログラムにドメインロジック(ビジネスロジック)も、データアクセスも書く、というやり方。

マーチンファウラーは「リファクタリング」の中で、大きなリファクタリングとして、
・UI と ロジックの分離
・手続き型の設計からオブジェクトへ
を説明している。

わざわざ書いている、ということは、アメリカでも、現場のプログラムは
・ UI、ロジック、データアクセスが一体になった設計
・ 手続き型の設計
が多いということなんでしょう。

一つの機能(ボタンアクション)のコーディングに、UI・ロジック・データアクセスの処理手順を手続き的にコーディングしていくというやり方も、必然性というか、なんらかの真理がある、ということでしょうね。

でも、それが、理解が難しく、変更が難しいソフトェアになってしまう、ことも明白な現実。

そうなりたくないから、理解しやすく、変更がしやすいソフトウェアを書きたいからこそ、ソフトウェア・アーキテクチャが大切になる。

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