情報がリッチなプログラムをモジュール化する一般的な方法は、大きく3つのレイヤーに分割することです。 その3つとは、プレゼンテーション(UI)、ドメインロジック(ビジネスロジック)、データアクセスです。 ウェブアプリケーションであれば、HTTPリクエストをハンドリングしてHTMLをレンダリングするウェブレイヤー、バリデーションや計算が含まれるビジネスロジックレイヤー、データをデータベースやリモートサービスに永続化する方法を管理するデータアクセスレイヤーに分けられます。

これは多くのアプリケーションに使えるモジュール化の形式です。 また、私が定期的に使用したり推奨したりするものです。 (私にとっての)最大の利点は、意識のスコープを狭められるところです。3つのトピックを比較的独立して考えることができるからです。 ドメインロジックのコードに取り組んでいるときは、UIをほぼ無視したり、データソースを必要なデータをくれる、あるいは必要に応じてデータを更新してくれる抽象的な関数のセットとして扱ったりすることができます。 データアクセスレイヤーに取り組んでいるときは、インターフェイスで必要な形式になるようにデータを扱うことに集中できます。 プレゼンテーションに取り組んでいるときは、UIの振る舞いに集中しながら、関数の呼び出しに合わせて魔法のようにデータを表示・更新できます。 これらの要素に分割することで、思考のスコープを狭くなり、やるべきことに集中しやすくなります。

スコープが狭くなるというのは、プログラミングの順番を意味するものではありません。 通常、レイヤー間で何度も行き来する必要があります。 たとえば、UXの理解とは関係なく、データやドメインのレイヤーを構築することがありますが、UXを改良するときは、ドメインを変更する必要がありますし、それによってデータレイヤーも変更する必要があります。 ただし、レイヤー間を何度も行き来するとしても、変更を加えるときにはひとつのレイヤーに集中するほうが簡単です。 リファクタリングの2つの帽子を使った思考モードの切り替えとよく似ています。

モジュール化をするもうひとつの理由は、モジュールの実装の置き換えが可能になることです。 モジュールを分割することで、ドメインロジックを複製することなく、複数のプレゼンテーションを構築できます。 複数のプレゼンテーションとは、ウェブアプリの別のページ、ウェブアプリとモバイルネイティブアプリ、スクリプト作成用のAPI、古のコマンドラインインターフェイスなどです。 データソースをモジュール化すれば、データベース技術の変更に対応したり、予告なしに変更される永続化サービスをサポートしたりすることが可能です。 ただし、データアクセスが代替可能になることがデータソースレイヤーを分離する原動力だという話はよく耳にしますが、実際にそれをやっている人は知りません。

モジュール化によって、テスト容易性がサポートされます。 自己テストコードの大ファンである私にとっては非常に魅力的です。 モジュールの境界は、テストのつなぎ目になります。 UIのコードはテストが難しいので、テストしやすくするためにロジックはできるだけドメインレイヤーに入れましょう。 そうすれば、技巧を凝らしてUI経由でプログラムにアクセスしてテストする必要がありません1。 データアクセスは遅くて扱いにくいため、データレイヤーにテストダブルを使うことで、ドメインロジックのテストが簡単で高速になります。

こうした代替可能性やテスト可能性もレイヤー化の魅力ではありますが、これらがなくてもレイヤーに分割すべきであることを強調しておきたいと思います。 意識のスコープを狭めることができるだけで、レイヤー化をする十分な理由になるのです。

プレゼンテーション・ドメイン・データは、1つのパターン(プレゼンテーション・ドメイン・データ)もしくは2つのパターン(プレゼンテーション・ドメインとドメイン・データ)として扱うことができます。 どちらの見方も有益です。 ちなみに私は、プレゼンテーション・ドメインとドメイン・データであると考えています。

私はこれらのレイヤーをモジュールの一種であると考えています。 モジュールはソフトウェアを比較的独立した部分にまとめる一般的な言葉です。 これがコードにどのように対応するかは、使用しているプログラミング環境によって違います。 通常、最下位のレベルは何らかのサブルーチンや関数になります。 オブジェクト指向言語には、関数とデータ構造をまとめるクラスの概念があります。 ほとんどの言語には、階層構造が作れるパッケージまたは名前空間と呼ばれる上位のレベルがあります。 モジュールは、デプロイ可能な単位(ライブラリやサービス)に対応することもありますが、必ずしもそうである必要はありません。

レイヤー化はこれらのどのレベルでも発生します。 小規模なプログラムでは、レイヤーに含まれる関数を別々のファイルに入れるだけかもしれません。 大規模なシステムでは、複数のクラスが含まれる名前空間に対応したレイヤーを作ることもあるでしょう。

ここでは3つのレイヤーを説明しましたが、3つより多いレイヤーを持つアーキテクチャもあります。 よくあるのが、ドメインとプレゼンテーションの間にサービスレイヤーを入れたものです。 あるいは、プレゼンテーションレイヤーをプレゼンテーションモデルなどで分割したものもあります。 レイヤーを追加しても、中心となる分割が維持されるのであれば、基本的なパターンが壊れることはありません。

レイヤーの数が多いアーキテクチャも、
基本的なドメイン・データパターンに従っている

依存関係は上から下に向かいます。 つまり、プレゼンテーションがドメインに依存し、ドメインがデータソースに依存します。 一般的には、ドメインがデータソースに依存しないように、間にマッパーを導入します。 このアプローチは「ヘクサゴナルアーキテクチャ」と呼ばれます。

これは物理層ではなく論理レイヤーです。 3つのレイヤーは1台のノートパソコンでも実現できます。 あるいは、デスクトップでプレゼンテーションとドメインモデルを実行して、サーバーでデータベースを実行することもできます。 プレゼンテーションをブラウザのリッチクライアントとサーバーのBacked For Frontendに分割することもできます。 なお、BFFは特定のプレゼンテーションをサポートするものなので、プレゼンテーションレイヤーとして扱います。

プレゼンテーション・ドメイン・データの分割は一般的なアプローチですが、 比較的小さな粒度で適用する必要があります。 アプリケーションが成長すると、各レイヤーも複雑になるので、さらにモジュール化する必要があります。 このような場合、上位のレベルでプレゼンテーション・ドメイン・データを使用することは最善の方法ではありません。 多くのフレームワークは、トップレベルの名前空間にビュー・モデル・データなどを設定することを推奨しています。小規模なシステムであれば、これで問題ないでしょう。 しかし、レイヤーが大きくなっていくと、トップレベルをドメイン別のモジュール(内部がレイヤー化されたもの)に分割する必要があります。

複雑なアプリケーションでは、トップレベルのモジュールにレイヤーを使用しない...
...代わりに、トップレベルのモジュールをフルスタックにする

レイヤー化で組織が迷走するのは、開発チームをレイヤーに分割するというアンチパターンです。 フロントエンドとバックエンドでは、使用するフレームワーク(や言語)が違うので、開発者がいずれかに特化できるという意味では、魅力的に思えます。 共通のスキルを持つ人たちを集めると、スキルの共有が可能になり、組織は特定の仕事ができる人たちとして扱うことができます。 同様に、データベースの専門家を集めることは、共通のデータベースやスキーマには適しているでしょう。 しかし、レイヤー間で頻繁にやり取りをするためには、チームを何度も行き来する必要があります。 同じチームに専門家がいて、気軽に話しかけることができるのなら問題ありませんが、チームの境界があると、大きな摩擦が発生するだけでなく、レイヤーを横断するシステムを理解したいという個人のモチベーションが低下します。 さらに悪いことに、レイヤーをチームごとに分割すると、開発者とユーザーの間に距離が生まれます。 開発者はフルスタックである必要はありませんが(フルスタックであれば素晴らしいですが)、チームはフルスタックであるべきです。

開発者はフルスタックである必要はありませんが、チームはフルスタックであるべきです。

さらに詳しく知るために

こうした分割については、別のところでさまざまな角度から書いています。 レイヤー化はPofEAA本の構成にもなっています。 また、PofEAAの第1章では、これらのレイヤーについて詳しく説明しています。 PofEAAではレイヤー化をパターンにしていませんでしたが、Separated Presentationプレゼンテーションとドメインの分離などを考えています。

大規模なシステムの最上位モジュールをプレゼンテーション・ドメイン・データにすべきではない理由については、Simon Brownの著書と講演を参照してください。 また、ソフトウェアアーキテクチャをコードに組み込むべきだという彼の意見にも同意します。

同僚のBadri Janakiramanとヘキサゴナルアーキテクチャについて興味深い議論を交わしました。Ruby on Railsを使用したアプリケーションに関するものでしたが、このアプローチを検討している他のケースにも当てはまると思います。

謝辞

James Lewis、Jeroen Soeters、Marcos Brizeno、Rouan Wilsenach、Sean Newhamが、この投稿の草稿について議論してくれました。

脚注

  1. ページオブジェクトは、UIのテストに役立つ重要なツールでもあります。