はじめに
こちらは『はじめての設計をやり抜くための本』の社内向け輪読会の資料です。
以下の記事の続きになります!
第4章 内部設計の手法
ロバストネス分析
ロバストネス分析は、ユースケース記述や概念モデルといった要件定義の成果物をもとに、オブジェクト指向分析をするための手法になります。ロバストネス分析をすることで、ユースケース記述や概念モデルからクラスを抽出できます。
手法としては下記のような方法をとる。
- クラスを下記3種類に分類する
- バウンダリ
- システムと外部とのインターフェイス
- 画面や帳票
- 外部システム
- I/F - エンティティ
- システム内部に永続化されるドメインオブジェクト
- データベースのような永続化ストレージを表すことが多い - コントロール
- バウンダリから呼び出されるそのシステムが行う処理
- バウンダリから呼び出されてエンティティを更新する
これらはMVCモデルに似ている(Model、View、Controller)が、UIとデータベースをもつシステムの一般的な設計スタイルである。
画面プログラム設計
MVCモデル画面や帳票(Model、View、Controller)
Model
ビジネスロジックとエンティティを合わせてもので、エンティティとはデータベースのデータをオブジェクトで表現したもの。ビジネスロジックとエンティティを合わせてドメインと呼んだりする。
View
GUIの画面のこと。Webでは、JSP(JavaServer Pages、Webページ内にJavaプログラムを埋め込み、これをサーバ上で実行して結果を反映したページを動的に生成することができる技術。)やThymeleafや、HTMLを表示するWebブラウザがViewにあたる。ViewではModelの情報を表示する。
Controller
ModelとViewを制御する。
画面からのイベント(リクエスト)を適切なModelに仲介し、Modelの結果をViewに表示させる。
MVCの3者が連携することで、効率よく処理が行える。
現在はWebアプリケーションを開発する際にMVCモデルを意識することは少ない。
それはフレームワークがデファクトスタンダードになっており、フレームワーク上でうまく構築してくれているから。
画面遷移図からのControllerを抽出する
画面遷移時にはControllerが呼び出され、適切な画面に仲介してやる必要がある。
その際に画面遷移の抜け漏れがないように画面誠意図を用意し、必要なControllerを洗い出す必要があります。
Controllerの設計
前段で洗い出したControllerの一覧からそれぞれどのような処理を行うかを設計します。
Controllerが行う処理は主に下記となります。
- リクエストパラメーターのバリデーション
- リクエストパラメーターの取得
- ビジネスロジックの呼び出し
- レスポンスへのデータ設計
- 画面遷移
Controller設計時にはシステムの流れが把握できるController設計書を作成する。
また、Controllerは同時に実行される為、スレッドセーフで作成する必要がある。
その為Controllerは状態を持たないよう鑿井するのが安全である。
画面共通部品の設計
画面で利用するヘッダー、フッター、メニューなどは共通で利用する為、共通部品として鑿井する。
HTTPセッション
一般的なWebアプリケーションではHTTPセッションを用いてリクエストのやり取りをする。
WEBブラウザからのリクエストごとに、セッションIDを付与しやり取りするが、その際のセッションIDを付与する方法はCookieを使うのが一般的です。
しかし、セキュリティの観点からサードパーティCookieを利用禁止にする動きがある。
こちらは埋め込みADなどのCookieがサードパーティCookieが使用されており、使用されており、広告主にクリックデータやIDが送られる。こちらは悪意のある攻撃者によってデータが抜き取られる可能性があり、廃止の方向となっている。
ビジネスロジックプログラム設計
TransactionScriptパターン
TransactionScriptではビジネスロジックをServiceクラスのメソッドにします。
Serviceクラスは、何らかの適当な業務単位に作成されるクラスです。
例えば、注文関連のビジネスロジックを提供するOrderServiceのようになります。
ユースケース単位に作成しても構いません。
TransactionScriptはビジネスロジックをメソッドにして集めているだけで、基本的に即成などの情報は持ちません。
TransactionScriptを採用する理由は、設計が簡単だからです。TransactionScriptのアプローチは、手続型言語のアプローチに似ています。
どのようにエンティティを処理するかだけに注目すれば設計できる。
その反面、TransactionScriptではビジネスロジックの処理を共通化するように設計するのは難しくなる。
DomainModelパターン
DomainModelでは、ビジネスロジックをエンティティに持たせます。
エンティティはカプセルかとポリモーフィズムを実現できます。
概念モデルから抽出されたエンティティに自然な形でビジネスロジックを配置できます。
DomainModelではエンティティ間に強い依存関係が発生します。
エンティティにシステムの重要な情報が集中するので、システムの変更要件がエンティティに局所化されています。その反面、エンティティが複雑になってしまう可能性があります。
TransactionScript VS DomainModel
DomainModelはオブジェクト指向を重視し、データとビジネスロジックをカプセル化することで、変更を隠蔽できる利点があります。
一方、TransactionScriptは処理とデータを分離する従来の手続き型に近く、ビジネスロジックが細切れになる傾向があります。
DomainModelはスキルが必要であり、ビジネスロジックの追加や巨大化、他のDomainModelとの複雑な相互作用が発生する可能性があります。
TransactionScriptは設計は簡単だが、ビジネスロジックの細分化や共通化のための設計基準が必要であり、DMよりもシンプルに保つことができますが、カプセル化が困難な側面があります。
DomainModelには変更が及ぶ可能性があるが利点もある一方で、TransactionScriptはカプセル化が難しいがシンプルさがあります。どちらのアプローチを採用するかは、プロジェクトやそのニーズに応じて判断されるべきトレードオフがあります。
データベースプログラム設計
O/Rマッピングの必要性
O/Rマッピングツールの一般的な機能
- オブジェクトとテーブルのマッピング
- 永続かオブジェクトのライフサイクル管理(コネクション管理、SQLの実行)
- トランザクション管理
- 並列生・ロック制御
DAOパターンを使う
DAOとはData Access Objectの略
永続か処理を永続化されるオブジェクトから分離し、DAOクラスに隠蔽するためのパターン
さらに、DAOインターフェイスを定義し、DAO実装クラスを分離することで、DAOの実装を切り替えることができます。
エンティティクラス図の作成
データベースプログラミング設計を行う際には、まず概念モデルをもとにしてエンティティのクラス図を作成します。
実施する主な内容は下記
- 概念モデルの日本語のクラス名を英語のクラス名にする
- 概念モデルの日本語の属性名を英語の属性名にする
- アクセサーメソッドを追加する
- クラス間の関連に方向(矢印)をつける
エンティティの関連は、処理を設計しないと検討できないので、ひとまず必要最低限の関連だけ設計し、処理の設計に合わせて追記・調整すると良いです。
DAOクラス図の作成
DAOはエンティティに対して1つ作成します。DAOにはCRUDと、findメソッドを作成します。
- CRUD
- Create,Read,Update,Deleteの頭文字をとったもので、作成、読み込み・更新・削除といったデータベースへの基本的な操作を表すもの
- findメソッド
- DAO内に作成し、エンティティの関連と同じように、ビジネスロジックの処理によって追加していきます。
これらもひとまず必要最低限の関連だけを設計し、処理の設計に合わせて追記・調整すると良いです。
CRUD設計
CRUD設計は表を使って行うと良いです。
ビジネスロジックがどのデータベースにたいsてCRUDを行うかを分析し、設計します。
CRUD分析を行うことで、ビジネスロジックが具体的にどのテーブルを操作しているのか、また逆に、どのテーブルをどのビジネスロジックが操作しているのかが明確になります。
トランザクションの制御
トランザクション制御はデータベースへの操作をまとまった単位で確定させる仕組みとなります。
トランザクションがあるおかげで、データベース操作が途中でエラーになったとしても、確定させずにロールバックできます。
これはデータベースのデータが生合成を常に保つことができる仕組みとなっています。
1つの処理に対して関連する2つのSQLを実行する必要があるとします。
1つ目のSQLの直後に何らかのエラーが発生すると、2つ目のSQLが実行されません。
1つめのSQLだけではある項目しか登録されておらず、データベースに不整合なデータが出来てしまいます。
そのため、関連する2つのSQLを1つのトランザクションで実行します。このトランザアクションの範囲をトランザクションスコープと呼びます。
2つのSQLが実行される間隔などは非常に短い時間であっても、大量に処理が動くシステムではその一瞬で障害が発生することは珍しくありません。
特にデータベース操作のような外部へのI/Oが発生する処理ではCPUの処理速度に比べて時間がかかるため障害も発生しやすいです。
トランザクションを設計するには次の2点に注意する必要があります。
- トランザクションアイソレーションレベル
- トランザクションスコープ
トランザクションアイソレーションレベルについて
レベル | 独立性 | 性能 | 説明 |
---|---|---|---|
SERIALIZABLE | 最高 | 低 | 最も独立性(安全性)が高い分離レベルで、トランザクションを順番に実行(直列化)したのと同じように、他のトランザクションのデータ更新の影響をまったく受けない。アクセスが競合すると「先客」のトランザクションの終了を待たなければならないため性能は最も低い |
REPEATABLE | 高 | 中 | 2番目に独立性が高い分離レベルで、他のトランザクションによるデータ更新の影響を受けず、トランザクション実行中は何度繰り返し対象データを読み取っても同じ値が返ってくる。しかし、他のトランザクションによるレコードの追加・削除の影響は受けるため、「ファントムリード」と呼ばれる現象が生じることがある |
READ COMMITED | 中 | 高 | 3番目に独立性が高い分離レベルで、トランザクション実行中に他のトランザクションがコミットした変更の影響を受ける。ファントムリードに加え、何度も同じデータを読み込むと他のトランザクションによる更新で値が変わってしまう「ノンリピータブルリード」現象が生じることがある。多くの著名RDBMSのデフォルトの分離レベルである。 |
READ UNCOMMITED | 低 | 最高 | 最も独立性が低い分離レベルで、トランザクション実行中に他のトランザクションが引き起こすあらゆる更新・変更の影響を受ける。ファントムリード、ノンリピータブルリードに加え、処理途上や不完全な状態のデータを読み込んでしまう「ダーティリード」現象が生じる可能性がある。処理を妨げるロックなどは最小限に抑えられるため最も高速に動作する。 |
データベースロック
アイソレーションレベルは、他のトランザクションが更新したデータをどのように見えるかを制御するためのものです。
その為、他のトランザクションが更新することを制御できません。
そこで、更新の制御を行うための仕組みがデータベースロックと呼ばれます。
ロックには主に2種類あり、排他ロックと共有ロックが存在します。
排他ロックはデータを順番に更新するための仕組みなので、複数のトランザクションが取得することはできません。一方で、共有ロックは複数のトランザクションが取得できます。
コネクションプール
サーバープログラムは大量のリクエストを処理するために、データベースコネクションを再利用します。
データベースコネクションを再利用するための仕組みをコネクションプールと呼びます。
この仕組みはデータベースコネクションを取得する場合、接続や切断の処理を行うたびに一定の負荷が生じるため、頻繁にアクセスが行われるシステムではこのオーバーヘッドのために性能が劣化する場合がある。
そこでコネクションプールを利用するとデータベースコネクションを保持・待機する為、アクセスの度に発生する接続や切断を回避し性能の向上を図れる。
マスタのキャッシュ
データベースに頻繁にReadアクセスされるようなデータはデータキャッシュが有効である。
データをキャッシュさせる事でシステム外部へのI/Oの回数を減らし、性能を向上させることができます。
データベース物理設計
物理のER図の作成
データベース物理設計では、データベース理論設計で作成した理論ER図から物理ER図を作成します。
また、テーブル定義書も作成します。
物理ER図を作成するには次のことを行います。
- テーブル名と列名を英語表記にする
- 列の方やサイズを付ける
- パフォーマンス設計を行う
パフォーマンス設計
データベース物理設計で一番重要なのが、データベースパフォーマンス設計です。
パフォーマンスは開発後に実際に動かしてみないと分からない部分もありますが、設計の段階でもパフォーマンスを向上させるためにできることはあります。
システムのパフォーマンスに影響する項目は下記があります。
項目 | 決まる工程 |
---|---|
ハードウェア(CPU、メモリ、ディスク)、ネットワーク、OS | 外部設計 |
データベース設計 | 外部設計で論理設計 内部設計で物理設計 |
プログラム設計 | 内部設計 |
データベースのデータ | 遅い場合はテストまで決まらない |
データベース設計の中でパフォーマンス検討すべき内容としては下記となります
- I/Oを減らす
- Select文で取得するデータ量を減らす
- 取得回数を減らす
- インデックスでデータを見つけやすくする
- Bツリーインデックス
- ビットマップインデックス
- 逆キーインデックス
- ファンクションインデックス
- 結合を簡単にできるようにする
- 処理はまとめて行う
インデックスの種類について
テーブル定義所の作成
物理ER図を作成したら、テーブル定義所を作成します。
テーブル定義所には、データベース論理設計とデータベース物理設計で作成したER図をもとに、テーブルごとの詳細な仕様を定義します。
- テーブルについての使用定義項目
- テーブル名
- スキーマ名
- 列ごとの使用定義項目
- 論理列名
- 物理列名
- データ型
- 長さ
- 精度
- 必須
- デフォルト
- 主キー
- 外部キー
- インデックス
テストのための設計
テストと設計
テストにもいくつかの段階があります。
その段階ごとに、テストするタイミングとテスト対象が異なります。
段階 | 説明 |
---|---|
単体テスト | システムを構成するモジュール単位のテスト。モジュールの単位としてはクラスがある。モジュールの開発が終了した時点で行う。 |
結合テスト | システムを構成するモジュールを組み合わせたテスト。組み合わせる単位は、画面遷移やユースケース。画面遷移やユースケースの開発が終了した時点で行う。 |
システムテスト | システム全体のテスト。システム全体の開発が終了した時点で行う。 |
負荷テスト | システムの処理に負荷をかけ、システムの性能や信頼性などを評価する |
運用テスト | 運用設計に基づいて正常時の運用や障害発生時の運用を行い、システムが運用上問題ないかを評価する |
受け入れテスト | システム開発の発注者であるユーザー企業において、開発されたシステムが注文通りに作成されていることを評価する |
開発プロセスによってケースは違いますが、ウォーターフォール型の開発だと下記のようなV字モデルのテスト工程になる。
このV字モデルのテスト工程の場合は、後の工程になればなるほど手戻りの工数が大きくなる。
また、テストを実施する場合、設計内容を元にテストを実施する為、設計の成果物や要件定義の成果物がなければ良いテストは行えない。
TDD
テスト駆動開発で開発と同時にテストも実装する手法の事。
こちらを読みましょう。
開発環境の構築
開発を行うためには基本的に下記を準備する必要がある
- バージョン管理システム
- ビルド環境
- テスト環境
上記を準備して、チームプレイができる開発環境を構築する必要があります。
開発標準の策定
開発の各作業を行なっていくために、開発プロジェクトチーム全員が守らなければならない規約や指針のこと。
チーム毎の開発プロセスなどで変わってくる。
おわりに
今回は内部設計の手法の章について輪読を実施しました。
次回からはアーキテクチャ編となります。
参考文献
この記事は以下の情報を参考にして執筆しました。
『はじめての設計をやり抜くための本』