前置き
LAMP環境でフリーランスのエンジニアを数年やってきて気づいたこと。自分の場合は主にPerlとPHPだけども
サーバーサイドスクリプト言語では、言語としてオブジェクト指向をサポートしている場合であっても
JAVAなどのオブジェクト指向言語と比較して、手続き型の実装になりがちである。
自分なんかはフリーランスでやってるので、色んな現場の既成システムをみる機会がこれまであったわけですが
改修案件や追加機能案件などで、手続き型のシステムをみながら、そのメンテナンスのし辛さを目の当たりにして
途方に暮れながらも、いつかどうにかなるはず、やまない雨はないと信じて目の前のタスクをとにかく消化していくわけです。
でもある日気づいたんですね、ゴミはいつまでたってもゴミなんだと。
自分は本来なまけものなんです。だからこそ、手続き型の気怠さが許せず、体系的で整理されているものを好みます。
今日まとめるのは、この混沌とした経験の中でえた自分なりの整理、整理するまでの考え方です。
ゴミの定義
ゴミとは言ったものの、ゴミはゴミながら動いてはいるわけです。
とくにビジネス的観点からしてみれば、必要な機能を生み出している限り
ゴミがだろうが、一流だろうがあまり問題ではないわけです。
シンプルに、マンションを建築する為だけであれば、姉歯建築でも構わないし、ゼネコンでも構わないわけです。
とは言え、作る側からしてみれば、体系化されておりメンテナンスも容易な手法にこしたことはないですね。
そういうジレンマと我々戦士は戦っていかなければならない宿命にあるのだということを感じます。
じゃあどうする?
問題には大きく分けて二つの場合があります。
- これからゼロベースで新たに何かシステムを作ろうとしている場合。
- 既存のシステムに改修を加えなければならない場合。
1の場合は言うまでもなく、手続き型を脱却してシステムを実装していけばよいです。
でも、これまで手続き型の実装しかやってこなかったチームまたは個人のLAMPエンジニアが
いきなりオブジェクト指向でかっこよくやろう!というのは簡単ではありません。
2の場合は、諦めてしまいがちです。郷に入れば郷に従えというように、手続き型の中に
必要な処理を差し込んでいくという手法です。これは簡易的ですが必ず戻ってくるブーメランです。
そこに立ち止まっている限り、いつか必ず自分が被弾します。となると、すこしでも体系化されたモノにして
アドオンするという考えを元に要件を満たしていく事を考えた方が利口ですね。
となると、いずれにしても必要なのは、支えとなる考え方だということになります。
つまりシステムの構造設計です。
心がけていること
あくまで自分がこれまでの経験上心がけていることなので
完全なシステムアーキテクトとして汎用化するには三流です。
しかしその三流以下のゴミが世の中に溢れていることは捨ておけない事実。
これは構造を設計するにあたり心がけていることの抽象化。
理想的な構造と、現実問題としての実装にはおおくの場合、隔たりがあります。
ガチガチのオブジェクト指向開発をするには、そのための準備やエンジニアのナレッジが必要です。
しかしサーバーサイドスクリプト言語におけるウェブアプリケーションのような
スピード感が求められ且つ、コンパイルも必要なく実装が容易な場合には
とりあえず動くものをつくってみるという姿勢も大事であるため、
ドメイン駆動開発のような完全な構造設計とは溝ができがちな印象。
となると、とりあえず作ってはみるがいつでもリファクタリングはできるぜ。という姿勢、心意気が大切なのでは?と。
またウェブアプリケーションにおけるスピード感や早いサイクルでのPDCAは、
アジャイル開発と非常に相性がよいので、緩い構造設計における恣意的実装という短所を
レビュー・リファクタリング・テスト自動化などのサイクルを前提としてカバーしつつ
調整していくということは非常に効果的なわけです。
この定義は、いわゆるDDDでのアーキテクチャを参考にしているが、DDDの世界ではアンチパターンの一つに含まれます。
というのも、いわゆるトランザクションスクリプト。
※DDDには設計段階からDDDを意識していく必要があるがそうではないので
使えるエッセンスだけを抜き出している。ただできる限りOOPであるということは大切です。
既存の大規模モンスターシステムに対する保守・機能追加など様々な用途に適応できる形にミニマライズしたもの。
とはいえ、何か一つのレイヤーが肥大化した際には適時リファクタリングによって最適な形に保つ。
この際のバランス感覚は経験によって培うしかありませんが、常に意識しておきたいところです。
そしてその実行を手助けするテスタビリティの高い実装。テストの自動化などで、リファクタリングの障壁をあげないよう支える。
いずれにせよ新規スタートアップやシステム刷新でしか適用できないようなアーキテクトでは現実問題困りますからね。
かといって簡単なものだからレガシーでというのは、あとが恐い。
アプリケーションアーキテクトダイアグラム
考えの支えとなる構造レイアウトは、以下のような感じです。
構造における要素
ユビキタス言語 Ubiquitous Language
ユビキタス言語とは、エンジニア・企画・ステークホルダーで認識可能な言語のことです。
例えばオブジェクト指向がいいよねと、近所のおばちゃんに言っても通じないでしょうが
駅前のパン屋さんと言えば伝わりますね、それは駅前のパン屋さんという単語が、あなたと近所のおばちゃんにおける
ユビキタス言語だからです。隣町のAさんにはそれがなんだかわかりませんからね。
こういうエンジニアに関わらず、そのビジネス全体に関わる全ての人での共通言語のことを指します。
これをシステムに応用すると、ここで定められたキーワードを元にビジネスドメイン、ドメインオブジェクトの定義に役立てるということになります。
そしてこれはエンジニアが要件を深く理解する事で、正しい形のドメインオブジェクトができあがるはず。
ビジネスドメイン Business Domain
ビジネス領域における要件、機能や仕様などの諸々はここに含まれており、ドメインモデルは、これを礎に切り分けられていることが大切です。
ビジネスドメインでモデルを切り分けるということは、ビジネスとしての区分けで、実装時のモデルを切り分けるということ。
それはユビキタスな言語で構成されているとよりよいです。
例えば、「いいね」機能というのが最近はやってますが、「いいね」というのが既にユビキタス言語です。
「いいね」というのは本来、言葉として固有の意味を持っていますがシステム上違う意味がありますよね。
それは日本語的な「いいね」とは別の意味ですが、そのシステムに関わる人だけの固有の意味をもっています。
こうやってビジネスドメインを、ユビキタス言語で整理しながらクラス名にしたりコンポーネントにします。
オブジェクト指向プログラミング OOP
ぐぐって
アスペクト指向プログラミング AOP
オブジェクト指向的な考えで、ビジネスをオブジェクトとして捉える観点ではべつに、関心事をみるという観点で捉えることをいいます。
そしてこれはオブジェクト指向と共存できる考えです。ドメインオブジェクトから関心事を取り除く事で、
よりよい設計にするのに役立ちます。主にはキャッシュやトランザクション管理など横断的なロジックを管理するのに有効で、
インフラストラクチャレイヤーでの実装に役立ちます。
依存性の注入 DI
ドメインレイヤー、インフラストラクチャレイヤー、サービスレイヤーと本質的に横断するロジック
同様に、各レイヤー内におけるロジックの結合度を緩めるためにも大事な概念です。
疎結合であるということが、良いシステムにおけるキーワードの一つですが、それを促進します。
とくに手続き型言語や、オブジェクト指向で実装されていても、疎結合という概念を取り入れていない場合
多くのメリットを知らず知らずのうちに捨てているという状態に陥ります。
オブジェクト指向の皮をかぶった獣が量産されてしまうのです。
プレゼンテーションレイヤー Presentation Layer
ウェブビューなどテンプレート、MVCでいうViewにあたる階層です。
ランキングを公開するという機能をシステムが要している場合、それが
HTMLなのかRSSなのかメールなのかといった問題は、このレイヤーの責任であり
その他のレイヤーは、それぞれのアウトプット機能を用意してあるという具合です。
アプリケーションレイヤー Application Layer
アプリケーションとは言うものの、一般的にいえばコントローラであることがおおいです。
つまりMVCでいうところのCになります。アプリケーションとしてユーザーが接する層です。
表現方法はHTMLなのかRSSなのかJSONなのかといった切り分けを行うだけのごく薄い層です。
###ドメインレイヤー Domain Layer
ビジネスロジックの総体。サービスオブジェクトとドメインオブジェクトにわける。
ここがMVCでいうところのModelにあたります。
そして、その他のレイヤーに比べて肥大化しやすい傾向があります。
MVC構造においては、コントローラーの肥大化問題などもありますが、これは単に怠けた結果である事が多いです。
しかしモデルの肥大化問題は、しっかりと考えて正しく設計し、実装しようとしているからこそおきうる厄介な問題でありがちです。
これを避ける為の解決策として、前項のユビキタス言語に則ったドメインオブジェクトの切り分けというのが重要になります。
このレイヤー内で水平的に肥大化しないよう、かつ疎結合な状態で整理する、難しいですが仕方ありません。
難しいことをやろうとしてるわけですからね。オレはそろそろ心折れてきました。
サービスオブジェクト Service Object
これはドメインレイヤーに位置し、本来ここはものすごくビジネスのロジックを一切もたない薄い層であり
アプリケーションレイヤーとドメインオブジェクトを結びつけるのが主であり、ファサードとしての役割がつよいです。
依存関係の管理を担ったりします。
んだけど、チーム開発における実装上は、ここにビジネスロジックを持つ場合もあってよいとすることがおおいと思います。
そうするといわゆるドメインロジックからトランザクションスクリプトと変化していくわけですが、ビジネス的都合で
そうなってしまうというのはそれはそれで一つの解決です。
多くの手続き型実装では、アプリケーションレイヤーにあたる箇所でビジネスロジックがまとめられてしまっていますが
ここでいうトランザクションスクリプトからしてみれば、問題ないと思います。
例えば具体的に、一つのアウトプットに対して一つのモデル(サービスオブジェクト)をもち、機能はサービスが責任をもつということに
設計した場合、機能の実装がサービスオブジェクトか後述のドメインオブジェクトにあるかはあまり問題ではありません。
後々サービスオブジェクトに実装していた内容をドメインオブジェクトにうつし、サービスオブジェクトでドメインオブジェクトのロジックを
ラップするというようなリファクタリングが可能だからです。
###ドメインオブジェクト Domain Object
ビジネス領域におけるロジック、ユビキタスな言語で構成されるロジック群です。
本来的にはここに全てのビジネス要件がまとめられている状態が理想です。
ここが処理の心臓部。母艦。本丸。ホワイトハウスです。
ビジネスロジックの主たるものであり、理想的には
インフラストラクチャレイヤーなどに依存せず他のレイヤーからできる限り独立していることが望ましく
依存関係は、DIやAOPによって管理されることで抜本的な変更などにも対応できる仕組みとなります。
が、現実問題としてそこまで仕組化できないこともおおい。その場合はいわゆる共通処理として位置されていることも多々。
データアクセスレイヤー Data Access Layer
DAOなどのデータベースにアクセスするオブジェクトです。
インフラストラクチャレイヤー Infrastructure Layer
キャッシュやトランザクション、ロギングなど、横断的に利用されるライブラリ的性質をもつロジックです。
AOPやDIによってサービスレイヤーで利用される事がおおい。
他にもサービスレイヤーにおけるクラスの継承関係により、依存関係を持つ場合もある。
```
### アノテーション Annotation
```
DIやAOPといった疎結合を保ちながら依存関係をあらわすにあたり、
依存関係をまとめるのにロジック内にハードコードするのではなく、
アノテーションに記述するという形で使われる。
これによって依存関係が緩くなります。
```
### 仕様 Specification
```
Specificationパターン。
何らかの条件を満たすオブジェクトの状態を仕様オブジェクトとしてえぐりだす。
具体的に以下のような目的がオブジェクトに対してある場合、仕様オブジェクトとして定義することが有効。
1. オブジェクトを検証して、何らかの要求を満たしているか、何らかの目的のための用意ができているかを調べる。
2. コレクションからオブジェクトを選択する(期限が超過した請求書を問い合わせる場合など)。
3. 何かの要求に適合する新しいオブジェクトの生成を定義する。(DDD本から引用)
何故これが有効かというと、このパターンは応用がききやすいです。
スクリプト言語では、明確に一つのオブジェクトをクラスオブジェクトするよりも
ハッシュ構造として管理する事がおおいですが、ハッシュ構造は操作が簡易的なので信頼のおけるものではなく
信頼性が必要なデータとするにあたって、このパターンを有効活用できます。
もちろんハッシュ構造ではなくクラスオブジェクトだった場合にも有効活用できます。
```
## こころえ
- レイヤーの切り分けが仮にうまくてできなくても、サブルーチン単位での切り分けは行っておく。
- ドメインロジックではコンポーネント毎にデザインパターンを有効に利用するとよい。
- オブジェクトを量産した場合には、パフォーマンスに大しても充分に配慮する。
- CommonやUtilなど汎用的な名前のモジュールをつくらない。つくりたい場合はディレクトリにしてもっと狭い名前のモジュールにする。
- 依存関係の管理に苦労した場合はデザインパターンをあてはめて考えてみる。
書くの疲れたので終了。