
今回はフレームワーク作成のコツということでお話ししたいと思います。
最近ではいろんなシーンでフレームワークという言葉が使われていますが、ここでお話しするフレームワークはプログラム開発言語のフレームワーク(以降、フレームワーク)になりますので、一般的なフレームワークの知識や概念などについて知りたい方は、別のサイトをご参照ください。
また、ここで紹介するフレームワーク構築については、一般公開できるような汎用的なものではなく、特定の会社や組織、特定の案件のためのフレームワーク構築を対象としています。
一般公開されているフレームワークをベースに、機能の補完や強化、機能追加、機能制限などを行って、案件開発に必要な開発基盤を提供するためのノウハウになります。
特にWEBアプリケーション開発をベースにお話ししますが、クラサバなどでも適応できることは多いと思います。
今まで、さまざまな言語でフレームワークを構築してきましたが、言語の違いによる影響はあまり感じたことはありませんでしたので、言語に関係なく対応できる内容となると思います。
今回の内容はあくまでも「コツ」の説明のみとなります。
前述の通り、ここで説明するフレームワークは利用シーンが特定されたものを対象としています。
特定の作り方を説明したとしても役に立つことはないと思いますので、実際の構築手順などの説明は行わないようにしたいと思います。
フレームワークに求められること
一般公開されているフレームワークは(最近のものは改善されつつありますが)基本的にプログラマビリティを重点に置いていません。
どちらかというとプログラマーの自由を奪うことで、プログラムの均一化や品質の担保を行うようにしています。
(中には概念的な思想を押し付けた結果、開発効率や可読性が低下してしまうフレームワークもあります)
フレームワーク開発者(基盤チーム・アーキ担当者)は、こういった一般的なフレームワーク(以降、ベースフレームワーク)の機能をうまく使うか、場合によっては手作りして、フレームワークに求められることを実現することがフレームワーク開発者の役割となります。
手間を減らす
では、開発現場でフレームワークに求められることはどういった内容でしょう。
私が最初にフレームワーク(当時はフレームワークという言葉もありませんでしたが)を作成したときに要求されたのは以下のことでした。
- コスト削減
- 品質の向上
今、考えると、一般的なフレームワークが目指すものと全く違うことを要求されていたと思います。
しかしながら、この2つについては今でもほとんど(すべてといってもいいぐらい)の開発現場で要求される内容です。
20年以上、フレームワーク構築をしていても、この2点を最重要とすることは変わっていません。
この2点は実は同義です。つまり「コスト削減=品質の向上」です。
コストを下げるということはプログラマの作業(手間)を減らす、つまりプログラム量を減らします。
作成する機能は変わりませんので、プログラムが減るということは共通化する部分が増えるということです。
品質を担保しなければいけないのはプログラマが作成するプログラムですが、プログラムそのものを減らしているので品質が劣化する可能性そのものを減らしていることになります。
結果として、「コスト削減=品質の向上」となるのです。
迷いを減らす
フレームワーク作成者は実装者の細かい手間をなくすために、ありえないぐらいのコストをかけます。
具体的な例を挙げてみたいと思います。
一つのシステムを構築するとき、画面テンプレート処理、画面スクリプト処理、リクエスト処理、トランザクション処理、DBアクセス処理、バッチ処理など、それぞれの実装箇所にプログラムを作成することになりますが、場合によっては実装箇所ごとに言語が違うこともあります。
例えば、セッションデータからユーザIDを取得する場合、
画面テンプレート処理:
<input type="hidden" name="user_id" value ='${requestObject.sessionData["user_info"]["user_id"]}'>
// 画面スクリプト処理:
<br>let userId = USER_INFO["user_id"];
// リクエスト処理:
String userId = getHttpRequest().getSession().get("user_info").getUserId();
// DBアクセス処理:
String userId = getIntegrationData.getSessionData().getUserInfo.getUserId();
と、いうように同じ内容のプログラムにもかかわらず実装箇所によって書き方が違っています。
こういうフレームワークは意外と多いのですが、これでは直感的に実装できません。
また、似たようなプログラムを書きたいときでもどう書けばいいか迷いが出てきます。
画面テンプレート処理:
<input type="hidden" name="user_id" value ='${request.sessionData.userInfo["user_id"]}'>
// 画面スクリプト処理:
let userId = request.sessionData.userInfo["user_id"];
// リクエスト処理:
String userId = getRequest().getSessionData().getUserInfo["user_id"];
// DBアクセス処理:
String userId = getRequest().getSessionData().getUserInfo["user_id"];
のようにすると、
「”userInfo”で取得できるものが、ユーザ情報が入った連想配列だろう。だから、”userInfo[“user_name”]”って書けば、ユーザ名が取れるはず。」
という感じで直感的な実装ができるようになります。
これを実現するには、(UI層、インテグレーション層、DB層など)各層で”request”オブジェクトを取得できるようにする必要があります。
そのため、フレームワーク内ではかなり無理な実装が必要となります。
フレームワーク開発者はこのような、ちょっとしたプログラマビリティを上げるために膨大な時間(コスト)をかけます。
しかし、作成した基盤の利用が多ければ多いほどフレームワーク開発にかかるコストは実装コスト削減で十分に取り返すことができます。
「直感的」・「わかりやすい」とは?
ここまで「直感的」、「わかりやすい」が大切と説明してきましたが、どうすれば「直感的」、「わかりやすい」と、なるのでしょうか?
主観的、感覚的なものとしてしまうと「人によって違う」ということになってしまうので、ここで改めて分析、定義してみたいと思います。
例えば、以下のような物事の変化を説明するとき、変更前と変更後をこのように表現したとします。


何がどう変わったのか、そもそも何を表現したいのか、非常にわかりにくいですね。


このように表現するとわかりやすいです。
「わかりやすい」とは「理解に時間がかからない」=「ほしい情報を取得するためのコストが低い」と言うことができます。
上記の例では、前後が同じ形となったことで違いが明確となっています。
この「同じ形」というところが重要です。
人が何かを理解するとき、過去、経験したことと比較し、そのとき理解した内容から連想して理解しています。
上記の例では、「片方で得た情報(経験)をもう片方を理解するために利用できる」かどうかがわかりやすさの違いとなっています。
素人が作ったYouTube動画などで違和感を覚えることがありますよね。
(最近は減ったかもしれませんが)一般的に動画を見るのはテレビです。
実はテレビで放映される動画には、おおよそのパターン(暗黙ルール)が存在します。
それを知らない人が作った動画を見ると、見慣れているパターンと違うため違和感を感じて、内容が頭に入ってこないと感じてしまうのです。
しかし、ほとんどの人は「編集の暗黙ルール」みたいなものがあるとは知りません。
それどころか、テレビ放送と比較していることすら気づかずに、違和感だけを感じ、「内容が頭に入ってこない」(わかりにくい)という結果になっているのです。
このように、「わかりにくい」と感じるのは「無意識に過去の経験と比較し、一致しないことによる混乱」、「直感的」とは「過去の似た経験があるため連想しやすいこと」と言うことができます。
そのため、他のもの「同じ形」、一般的に知られているものと「同じ形」であることが「直感的」、「わかりやすい」を実現する近道なるのです。
(これはシステムのユーザーインターフェースを考えるときにも言えることですが、一般的なアプリなどに設計思想を合わせるだけで「今どき」だとか「わかりやすい」という評価を得られます。)
プログラマの手間やストレスを減らすには、前述の例のように、各機能を「同じ形(思想)」で作ったり、他のフレームワークの「真似」をしたりすることでプログラマビリティはかなり向上します。
「一般的なアプリの思想に準じる」ということも直感的な実装を実現するために重要な考えとなります。
単純な構造
「わかりやすい」フレームワークとするためのもう一つの要素として「単純さ」があります。
フレームワークそのものの構造を簡素にすることも大事ですが、各機能の構造が「単純」、または、「単純なように見える」ことが大事です。
プログラマーが実装で使うユーティリティやabstractクラスが、「メソッドや引数や戻り値を見るだけで、中で何をやっているかがなんとなくわかる」という作りになっていることが理想です。
例えばこんな感じです。
- 内部仕様が想像できるインターフェース(メソッド名、引数名など)
- 探しやすいデータ構造(階層が浅いなど)
- 内容を的確に表現したキーや名称
こうすることで、初めて使う機能でもマニュアルやソースを見なくても作業することができます。
楽に開発できるようにする
便利なツール(ユーティリティ)があるとプログラマビリティを上げることができます。
後で事例として紹介するポップアップ画面呼び出しをjavascript一行で行う機能と言った機能を用意すると実装がかなり楽になります。
運用しやすい機能を提供する
プログラマビリティだけでなく、マネージャビリティもフレームワークに求められる要素です。
フレームワークを作ることで、システムの課題解決がかなりらくになりますが、システムの設定を行う機能において以下のことができるようにしています。
- 設定箇所の集約
- (サーバー再起動などをしないで)動的に設定変更
- メンテナンスモード
- ユーザ告知機能
- アクセスログ、アクセス数管理機能
UIフレームワークも必要
もちろん、ユーザビリティも重要です。
特に画面UIはデザイン性と開発効率を両立させようと思うとかなり無理な実装をすることが多いです。
ただ、考え方は今まで説明してきたフレームワーク構築と同じです。
ユーザーが要求する機能やデザインを、ベースとなるUIフレームワークを使って実装できるように、「わかりやすさ」や「楽に作れる」ということを意識して基盤を作成するようにしましょう。
フレームワーク構造と機能
ここで、過去作ってきたフレームワークの機能を紹介したいと思います。

コア部分
- リクエスト&レスポンス事前、事後処理
- 各イベント実装基底クラス
- リクエストパラメータ処理
- レスポンス生成処理
- 画面テンプレート処理
DBアクセス部分
- テーブルエンティティ基底クラス(補完、項目チェック、エンティティ全体の整合性チェック)
- ORマッピング
- トランザクション管理
ユーザ認証
- 認証情報基底クラス
- ユーザ情報保持、参照機能
- 権限管理機能
セキュリティ
- Javascriptインジェクション対策
- SQLインジェクション対策)
- アクセス履歴管理(ヒストリバックイベント、CSRF対策)
UI部分
- ユーザー固有情報保持(クッキーの代替え機能、ユーザ固有の設定情報保持など)
- ポップアップ画面規定クラス
- Ajaxアクセス基底クラス
- マスター名称表示機能
ユーティリティ
- 各層(UI層、インテグレーション層、DB層など)間のデータ受渡しオブジェクト
ベースフレームワークの選定
フレームワーク作成は最初に、言語、DBの選定と、それに対応したベースフレームワークの選定を行います。
選定の基準はこんな感じです。
- ライセンスに抵触しないか
- シェア(ユーザー)は多いか
- 実装したい機能を持っている、または作りやすい
- 実現したいことの妨げになる制約はないか
- 不要な機能を使用できないようにできるか
いよいよ実装
フレームワークは試行錯誤しながら作成するため設計というフェーズはほとんど行いません。
(簡単な基本設計ぐらい)
ひとつひとつの機能を作りたい形になるように試行錯誤しながら実装していきます。
実際に作ってみると実現がむずかいものもいくつも出てきます。(道なき道を行く感じです)
フレームワーク実装においては、基本的に理想を諦めないことが大事です。
フレームワーク(基盤)レベルでちょっとしたことでもシステム構築となると膨大なコストとして跳ね返ってきます。
そのため、フレームワーク構築においてはちょっとしたことでも諦めず、出来るだけ理想に近い形にします。
通常のプログラムであれば諦めたり妥協したりする内容であっても、基本的に諦めないようにしてください。
どうしても諦める必要が出てきた時は、その影響が実装工数(手間)や実装結果(ユーザビリティなど)にどの程度か、影響をなくすことが出来るかを検討し、回避できないなら諦めるべきではないと判断するようにしましょう。
技術面やプログラマビリティで難しい問題にぶつかったときは、前回ご紹介した「問題解決のコツ」の手法を使って解決します。
実際にこの手法を使って解決することがほとんどですので、こういったスキルについても身に着けておくといいでしょう。
実装例
過去、実装した機能を紹介します。(前述の通り、使えるサンプルはあまりないので1つだけです)
ポップアップ画面機能
多くのシステムでマスターを参照するポップアップ画面の実装が必要になります。
共通部品とするべき機能ですが、意外と面倒なことが多いです。
そこで以下のようなフレームワーク機能を提供し、共通部品の実装と呼び出し部分の実装を行うとかなりコストの削減になります。
ポップアップ呼び出し部分は1行だけ
<INPUT type="text" name="xxx_code" id="xxx_code_id">
<SPAN id="xxx_name_id"></SPAN>
<INPUT type="button" value="マスター参照ポップアップ" onclick="xxxMstPopup({XXXCD:$('#xxx_code_id')}, {XXXNAME:${'#xxx_name_id'}})">
共通部品は受け取ったパラメータを使ってフレームワーク機能を呼び出します。
レスポンスで受け取ったJSONを使って呼び出し元から受け取った画面オブジェクトに値をセットします。
function xxxMstPopup(params, fields) {
responseJson = popupRequest("XXXマスタ参照", "/popup/xxxRequest", 800, 500, params, fields);
fields['XXXNAME'].val(responseJson['XXXNAME']);
}
言語によって実装方法が異なるので処理部分は割愛しますが、受け取ったパラメータを使ってポップアップ画面を開き、URLにリクエストを送ります。
画面側で作成したJSONを受け取って共通部品に返します。
※ポップアップで選択されたマスタをJSON化して戻す必要がありますが、うまくやるとJavascriptだけで実現できます。
function PopupRequest(title, url, width, height, params, fields){
// Windowをwidth, heightの大きさで開き、パラメータとしてtitle, paramsを使用してurlにリクエストする
...
// レスポンスで受け取ったJsonをそのままリターン
return ...;
}
まとめ
今回はフレームワーク構築のコツということで説明させていただきました。
フレームワークを構築する機会は多くないかもしれません。
ベースフレームワークを使えば基盤を作らなくてもシステム構築はできると考える人も多いでしょう。
複数のプログラマで作業するようなプロジェクトであれば作業効率やコードの均一化のためにも共通部品を作る必要が出てきますので、結局、フレームワークの構築をすることになります。
フレームワーク構築でかかったコストが実装コスト削減で取り返せなかったことは私の経験上、一度もありません。
フレームワーク構築コストを上回る削減ができることがほとんどですし、プロジェクト中や運用開始後に発生した問題の対策もしやすくなるため、デメリットよりメリットはかなり大きいと思います。
システム構築する時にはフレームワーク構築について検討して損はないと思いますので、ぜひ、チャレンジしてスキルを上げていただければと思います。