実践 ソフトウェア・アーキテクチャ : 関心の分離

レイヤー分割は「関心の分離」 (Separation of Concerns:SoC) 原則の具体例ですね。

関心の分離(SoC)は、私が思うに、ソフトウェアアーキテクチャの基本原理(公理?)です。

ソフトウェア設計とは、いつも「関心の分離」を考え続けることなんだと思う。人間、一度に多くの関心を持つものです。そこに逆らって、一度には一つのことだけに関心を集中するようにする。ソフトウェアの複雑さと戦う鉄則。

関心の持ち方も、人によってだいぶ違う。 UIとロジックが別の関心ごと、という感覚は、誰でも共有しているわけではない。 ほんとうの初心者のころ、なんとか自分で動かせたサンプルが、UIとロジックは分離していないだろうし、それをそのまま発展させて、実用アプリとして動いているコードもたくさんある。

UI,ドメインロジック(ビジネスロジック)、データアクセスを3層に分離する、という発想・感覚は、自然に身につくものではないんでしょうね。

私は、レイヤアーキテクチャを知る前に、ソフトウェア設計とは「関心の分離」だということを、駆け出し時代に師匠にたたきこまれていたので、3層に分離という話も自然に受け入れましたが。

実践 ソフトウェア基本設計 : レイヤの設計

レイヤの設計のテーマは三つですね。
<階数> 何階建てにするか?
<用途> それぞれの階の用途は?
<連絡> 上下の階の連絡手段は?

基本といわれる3層アーキテクチャの場合、

階数=3階建て
用途=3階はユーザインタフェース、2階はビジネスロジック、3階はデータアクセス
連絡=隣り合った階だけ連絡できる。連絡手段もできるだけ狭める。

とういう約束事を決める。

これで、これから設計・開発していくクラスの置き場所が決まるわけです。

アーキテクチャをこう決めたら、一つのクラスにユーザインタフェースに関する記述と、ビジネスロジックの知識を混在させない。 一つのクラスにビジネスロジックの知識とデータベースのアクセスの記述を混在させない、という約束事になる。

理解しやすく、変更が容易なソフトウェアの作り方の、ひとつの考え方です。

実践 ソフトウェア・アーキテクチャ : サンプルと参考書

私がソフトウェアアーキテクチャを興味を持ち始めた時、ソースコードを読んで、参考書を読みあさりました。

ソースコード

現場の業務アプリケーションやオープンソースのソースコードを読んで学んだことはたくさんあります。ただ、全体像をつかむのが大変、どちらかというとアンチパターンが多い、など最初の勉強の材料としては適切ではないと思います。

規模は小さいけど、しっかりした構造のアーキテクチャの勉強になるサンプルソースコードを2点、あげておきます。
基本通りの3層構造で、また、どうやって作っていくかのプロセスの説明付きです。

Spring MVC step by step

3層(Web,Domain,Repository)構造の Web アプリケーションのフルセットのサンプル。
Spring MVC のわかりやすいガイドでもあります。

Internet Bookstore

ICONIXプロセスの解説本 ユースケース駆動開発実践ガイド のサンプルのソースコード一式です。
3層 ( Web,Domain,DAO )構造の Web アプリケーションのフルセットのサンプル。
JUnit のテストコードもセットなので、この点でも参考になります。
解説本と一緒に読めば、要件定義から実装までの一連の作業手順を勉強できます。

参考書籍

エンタープライズアプリケーションアーキテクチャ (PoEAA)

第1章 レイヤ化が、全体的な話。もちろん、そのほかの個別パターンも勉強になります。

Domain-Driven Design (DDD)

ドメイン駆動設計の本ですが、ドメイン層を独立させるアーキテクチャについての説明が参考になります。 [ Layered Architecture パターン ]

オブジェクトデザイン

「第1章 17節 アーキテクチャスタイル」 が特に参考になります。
レイヤ構造のほか、制御スタイル(集中型、分散型、委譲型)の説明が勉強になります。

ソフトウェアアーキテクチャ全体をもっと詳しく勉強したい人には

ソフトウェアアーキテクチャ (POSA)

2.2 混沌から構造へ Layers
さまざまなアーキテクチャパターンが勉強になります。設計の原則の説明も参考になる。

アーキテクト、アーキテクチャ

アーキテクトやアーキテクチャは、建築用語からの借り物。

アーキテクトは、「職人の第一人者」というような意味らしい。
「主任技術者」とか「大工の棟梁」という感じですね。

アーキテクチャは、Architect に '-ure' という接尾辞がついたもの。 '-ure' は、行動とかやり方という意味らしい。

アーキテクトは大工の棟梁、アーキテクチャは「棟梁のやり方」か。こりゃ駆け出しの職人にはムリですね。でも、いっしょに仕事をしながら、棟梁のやり方を盗むことはできそう。

ソフトウェアの基本設計(アーキテクチャ)の勉強の仕方は「棟梁のやり方を盗め」ということか。

「アーキテクチャ」は、そのやり方の「結果」を意味することもあるらしい。どうやってそこに至ったかのやり方はわからないまでも、結果である建物をじっくり観察すれば、得るもの多そう。

最後の宮大工といわれる西岡棟梁は、昔の建物の解体修理を通じて、古(いにしえ)の工人の技術を学んだそうです。

アーキテクチャの勉強には、良いアーキテクチャのソフトウェアの中身を分解して勉強することが役に立ちそう。

ソフトウェア基本設計 : かたまりの大きさ 組み立て方

巨大な一枚岩

理論的にはすべての役割を一つのクラスに押し込んだ巨大な一枚岩ソフトウェアもありです。「エアーズロック」ソフトウェアと呼びましょう。

砂粒

理論的には、コードの一行を1クラスに細分化した大量のクラスをかき寄せて固めるやり方もありです。「砂遊び」ソフトウェアと呼びましょう。

どちらも極端すぎて「理解しやすい」「変更が容易」ソフトウェアではありませんよね。
時々「エアーズロック」クラスや「砂粒」クラスを見かけることがありますけどね。

レンガブロック

レンガ造りはどうでしょう。
クレーンがなくても人間の手で運べる大きさで組み立てることができる。建物の形も比較的、自由に作れる。レンガの大きさや形が標準化されている。
悪くはなさそうです。

石垣

日本の城の石垣は、さまざまな大きさと形状の石を組み合わせて作っている。大きな石の間にうまく小さな石をはめ込んで、がっちりとした構造にしている。
レンガのように標準化された大きさ・形状より、実際のソフトウェアの構造に近い気がする。

でも、石垣づくりには良いけど、例えば橋なんか作る時は、このやり方ではムリですね。

石造りの大聖堂

ヨーロッパには、石造りの大聖堂や、石造りのアーチ橋が多いですよね。
石を役割に応じて、大きさや形状を変えて加工する。そういうビルディングブロックをうまく組み立てると、大きなアーチ構造を作れる。

「アーキテクチャ」という言葉からは、これが一番ぴったりきそう。

鉄筋コンクリート

鉄筋を通して、型枠を作って、セメントと砂利を混ぜたものを流し込んで、乾くのを待つ。

伝統的なソフトウェア実装方式は、これに近い気がする。
鉄筋や型枠で、全体の形を決める。 (概要設計?)
あとは、形状がどろどろのセメントと砂利を流し込む。 作っているときは形状が流動的だけど、乾いて固まると、もう変更ができない。 (プログラミング?)

壁とか、あとからうかつに壊せない。壁をぶち抜くと全体の強度にも影響しそう。
最初の設計した形のまま使うしかなく、後から変更はムリ。
増設は一応できるかな?

なんか、ソフトウェア開発に似ていませんか?

プレキャスト

コンクリートづくりなんだけど、柱や壁などのブロックは一定の規格を元に、工場で作ってしまう。
現場では、土台工事をしっかりやったら、あとは、クレーンでブロックを組み立ててあっというまにできあがり。

それぞれの住戸は、この時点では一つの空間。そこに間仕切りを入れたり、ユニットバスやユニットキッチンを入れる。この内装は、あとから好きなようにリフォームも可能。

水回りの位置などは、そう簡単には変更できない。

私は、フレームワークを活用したソフトウェア開発は、このプレキャストがイメージとして近いと思っています。

ソフトウェア基本設計 : かたまり・名前・置き場所

理解しやすく、変更が容易なソフトウェアの作り方の決めごとが、ソフトウェア基本設計(ソフトウェアアーキテクチャ)

基本設計の手順は、
・かたまりを見つけ
・名前をつけて
・置き場所を決める
ことだと思います。

実装レベルだと、3行のコードをひとかたまりとして、メソッドとして抽出して名前をつけ、しかるべ場所(クラス)に置くのがコードの設計。
これをうまくやれば、コードは理解しやすく、変更が容易になる。

ソフトウェア・アーキテクチャは、ソフトウェア全体を見通して、大きな視点・単位でこれをやる。

具体的には、コードを集めてクラス、クラスを集めてパッケージ、パッケージをさらに集めて上位のパッケージ、というパッケージの階層構造の設計の「方針」「決めごと」がソフトウェア・アーキテクチャだと思います。

どんなコードのかたまりをクラスとするか?
どんなクラスのかたまりをパッケージとするか?
どんなパッケージのかたまりを上位のパッケージとするか?

それぞれのかたまりに、どんな名前をつけるか?

それぞれのかたまりを、どこに置くか? 論理的はどこか? 物理的にはどこか?

混沌から秩序へ!

基本設計 : ソフトウェアの基本設計

ソフトウェアの基本設計(ソフトウェア・アーキテクチャ)は、どうやったら理解しやすく、変更が容易なソフトウェアを作れるか、その作り方を考え、約束事を決め、関係者で共通理解にする活動ですね。

ソフトウェアの基本設計は、最近は
・ソフトウェアアーキテクチャのパターン
・フレームワーク
が簡単に利用できます。
この検討が、基本設計の重要課題の一つですね。

もうひとつの重要課題は「パッケージ」構成です。

アーキテクチャパターンとフレームワークは、問題領域やアプリケーションの分割の方針は何も触れていません。

実際のソフトウェアは、ドメイン層だけでも大量のクラスを開発する。ソフトウェアを理解しやすく、変更しやすく保つには、パッケージを使って整理することが必須。

もちろん、他のレイヤでも、クラスの数は多いので、パッケージで整理が重要。

このパッケージ構成の設計が、もう一つの基本設計の重要課題です。

私たちは、レイヤに対して垂直の分割という意味で、パーティショニングという言葉を使っています。 Rational Unified Process (RUP) でも確か、アーキテクチャの説明でレイヤとパーティショニングの分割のガイドラインがあったと思います。

基本設計 : システムの実現方式

要件定義は、役にたつソフトウェアのイメージを関係者で共通理解にするための活動ですね。

次のステップは基本設計。
基本設計とはソフトウェアを楽に作るために、作り方を考え、共有する活動だと思います。

基本設計は、システムの実現方法を広く定義する活動だと思います。

・ハードウェア、ミドルウェア、ソフトウェアというシステムレイヤ全体の構成。
・ネットワークインフラの構成。
・WEBサーバー、APサーバー、DBサーバーの teer(段)の構成。
・ソフトウェアのパッケージ(モジュール)構成。 (レイヤ分割やアプリ分割)
・メッセージングやファイル転送などシステム間の連携方式。
・セキュリティの設計と実装技術の選定。
・開発言語やツール。
・運用監視の設計と実装技術の選定。
...

自分一人で試作する環境でも、いちおう、こういう事項に決め事は必要。まあ、マシン購入やネットワークのセットアップまで自分でやってみる、という機会はありますね。

ただ、本番用のそれなりの環境を、全体的に設計するという機会はあまりないと思います。だいたい、既存の環境や、今までの経験の範囲で流用できるところを流用する、というパターンが多いですかね。

XP や ICONIX などは、システム基本設計はまったく触れていません。ウォーターフォール型の方法論だと、システム方式設計や運用設計などもカバーすることが多いかな?

ソフトウェア開発のための、言語・ツール・フレームワーク・ミドルウェアも、プロジェクトのたびにゼロから決めなおすということはあまりないんじゃないでしょうか。

複雑さと戦う : 手続きの置き場所

一連の手続きは、「ドメイン知識のかたまり」「業務知識のかたまり」である。
この手続きをどこに置くか?

トランザクションスクリプト ( Transaction Script ) パターン

アプリケーション層の手続きオブジェクト。

OrderCalculater クラスの
・determinePrice( Order )
・determineTax( Order )
という感じ

ドメインモデル ( Domain Model ) パターン

ドメインオブジェクトに手続きをカプセル化する。

Order クラスの
・getPrice()
・getTaxes()

OrderLine クラスの
・getPrice()
・getTaxes()
という感じ

マーチンファウラーは「リファクタリング」で Transaction Script から Domain Model へのメソッドの移動(債務の移動)を 「手続き的な設計からオブジェクトに変換する」リファクタリングとして説明している。
(上記の例は、この本から流用しています)

ドメインモデルパターンのほうが、わかりやすく、保守しやすいコードである、という価値観ですね。 私は、データと手続きの関係が単純であれば、ドメインオブジェクトに手続きもカプセル化するのが扱いやすいと思っています。

でも、前の記事に書いたように、世界はそんなに単純ではない。 複数のドメインオブジェクト( データの入れ物 )と関連する手続きは、どのオブジェクトにカプセル化しても、すっきりしないケースも多い。

それに対する Eric Evans の答えが、Domain-Driven Design ( DDD : ドメイン駆動設計 ) の;

Services(サービシズ)パターン

もの(オブジェクト)として扱うのが不自然なものは、Service という形で、「ドメインモデル」に付け加える。

例として「口座」間の「振り替え」サービスをあげている。「振り替え」は、特定の「口座」オブジェクトと一体の手続きではない。独立した「振り替え」サービスとして別のドメインクラス(手続きオブジェクト)にすべき。

Services オブジェクトは状態を持たない。つまり、フィールド(データの入れ物)を持たない、手続きだけをカプセル化したオブジェクト。

Transaction Script との違いは、アプリケーション層にするか、ドメイン層にするか、という違いですね。

私は、アプリケーション層とドメイン層に分離するより、Eric Evans の言うように、ドメイン層に集めたほうが扱いやすいし、自然だと思う。

ドメイン層に問題領域の知識のすべてを集めて整理することが、ドメインの知識が豊かな、役に立つソフトウェアを開発し、改良を続けるためにはわかりやすい。

単体テストもアプリケーション層のオブジェクトよりも、他のレイヤとは完全に分離したドメイン層のオブジェクト ( POJO ) のほうが簡単。

ドメイン層の整理 : Modules(モジュール)パターン

ドメイン層には、データと手続きを自然にカプセル化したオブジェクトと、(複数のオブジェクトに関連した)手続きをカプセル化した Services オブジェクトがある。

ドメイン層も複雑になってくるので、パッケージを使ってモジュール化する。
モデルのリファクタリングをやる。
新しいパッケージの抽出、パッケージ間のオブジェクトの移動、メソッドの移動をしながら、さらに分かりやすい、変更やしやすいモデルに改良していく。

手続きもドメイン層の住人であるべき、ということです。データと一体のカプセル化するかどうかは別の議論。

Eric Evans は、 Services パターンの多用は危険、というようなことを言っている。とりあえずは、Services オブジェクトを作っちゃて、モデルを改良しながら、それぞれのメソッド(手続きのかたまり)の、より適切な、わかりやすく変更しやすい置き場所を模索していくのもありだと思います。
Eric Evans のいうように、最初から良いモデルにはたどり着けない。
初期バージョンを継続的に改良(リファクタリング)していきましょう。
小さくて基本的なリファクタリングを積み重ねる。ある日突然ブレークスルーが起きる。開発者が、いままでどうしても理解できなかった問題領域の常識が突然理解できる。
ドメイン駆動設計を実践すると実際に起きることです。モデリングを現場で地道にやってきた人なら、必ず経験することですよね?

複雑さと戦う : 手続きのカプセル化

ドメイン(問題領域)の知識が増えると、扱うデータやロジックが増え、ソフトウェアは複雑になる。
リファクタリングをする。データのかたまり、手続きのかたまりを抽出し、適切な名前をつける。

データのかたまりと手続きのかたまりが関連が明確なら、それはドメインクラスに集めてカプセル化すると扱いやすい。
例えば、MailAddress に関する知識は、MailAddress クラスに集まっていると分かりやすい。

でも、一連の手続きとデータの関連がそんなに単純でないケースが多い。役に立つソフトウェアにしようとしたら、もっと世界は複雑になる。

ホテルの予約だと、空き室をチェックし、宿泊費を計算し、顧客が確認し、確定したらデータベースに記録する。これれは「部屋」オブジェクトに関連はするけど「カレンダー」とか「顧客種別」とか「支払い方法」とかにも関連しそう。「オーバーブッキングポリシー」とかも必要。

このような一連の手続きを集めてひとかたまりにするのが トランザクションスクリプト ( Transaction Script ) パターン。 業務システムでは、このやり方が多いと思う。

いろいろなデータの入れ物に関連するので、データの入れ物と一連の手続きとを分けて整理したほうがすっきりする。

トランザクション(の手続き)を記述したオブジェクトは、をプレゼンテーションやデータアクセスから分離して、アプリケーション層に集めるのが一般的。

ここまでの整理(データをひとかたまりに、手続きをひとかたまりに)で、複雑なソフトウェアがわかりやすく、変更もやりやすいと判断できれば、これ以上の作業は必要ないでしょう。

でも、トランザクションスクリプトの内容に重複が目立ったり、なんとなく似た記述パターンがでてきたら、リファクタリングを続けたい。

マーチンファウラーの「リファクタリング」に書いてある「いやな臭い」
・ひとつのクラスを異なる理由であちこち何度も変更する (変更の拡散)
・ひとつの変更で、いろいろなクラスを変更する (変更の分散)
現象ですね。

変更の拡散の場合、そのクラスを複数のクラスに分割、つまり 「クラスの抽出」をしたほうが良さそう。

変更の分散の場合「メソッドの移動」で、変更箇所を一のクラスに集めることができそう。

こうやって手続きのかたまりの整理をして、わかりやすく、変更しやすいソフトウェアに改善していく。

calendar
     12
3456789
10111213141516
17181920212223
24252627282930
<< September 2017 >>
システム設計日記を検索
プロフィール
リンク
システム開発日記(実装編)
有限会社 システム設計
twitter @masuda220
selected entries
recent comment
  • 番号より名前。 ニーモニックコードより名前。 【パターン】
    師子乃 (03/10)
  • Smart UI が優れている?
    masuda220 (03/10)
  • Smart UI が優れている?
    kagehiens (03/09)
  • オブジェクト指向プログラミングの教え方?
    masuda220 (12/05)
  • オブジェクト指向プログラミングの教え方?
    ZACKY (12/04)
  • 「オブジェクトの設計力」 スキルアップ講座やります
    masuda220 (08/14)
  • 「オブジェクトの設計力」 スキルアップ講座やります
    kompiro (08/14)
  • 「オブジェクトの設計力」 スキルアップ講座やります
    masuda220 (06/13)
  • 「オブジェクトの設計力」 スキルアップ講座やります
    JHashimoto (06/13)
  • 「オブジェクトの設計力」 スキルアップ講座やります
    masuda220 (02/28)
recent trackback
categories
archives
others
mobile
qrcode
powered
無料ブログ作成サービス JUGEM