ネクストデザイン有限会社  |  English  

ドメイン駆動設計の基本的な指針とドメイン中心開発の流れ

ドメイン駆動設計入門

はじめに

ドメイン駆動設計は、書籍「エリック・エヴァンスのドメイン駆動設計」(以降、DDD本) で示されたソフトウェアの設計手法です。

本記事では、まずドメイン駆動設計の要点とその必要性を共有したいと思います。

次に、ドメイン駆動設計によるアプリケーション開発の例を紹介します。

例では、ツールを使って、ドメインモデルからアプリケーションを自動生成する利点と方法について紹介します。

このツールは、本記事後半の「アプリケーション自動生成ツール」からダウンロードして無料で利用できます。

ドメイン駆動設計の基本的な指針

「ドメイン」とは、アプリケーション化したい対象領域のことです。例えば、在庫管理など。

ドメイン駆動設計では、「アプリケーションの本質はドメインである」ということを強く主張しています。

実装のためのフレームワークやドメイン外のオブジェクトなどに気を取られないで、ドメインに集中することです。

ドメイン駆動設計では、「ドメインモデル」と呼ばれるオブジェクトモデルが重要な役割を持ちます。

ドメインモデルは、ドメインに存在する概念を抽象化したものです。例えば、在庫管理に登場する「倉庫」や「移動」など。

ドメインに存在する本質的な振る舞い (ビジネスロジック) はすべて、ドメインモデルの役割としてモデリングされます。

ドメイン駆動設計において、ドメインモデルは、アプリケーション開発の起点であり中心です。

一言でいうと、ドメイン駆動設計とは、「ドメイン中心」を強く主張したオブジェクト指向開発だと言えます。


DDD本は分厚く、多くの内容が書かれています。

そのため、要点が分かり難い部分もあります。

ネットの記事や他の書籍を読んでも、ドメイン駆動設計の捉え方には様々あるようです。

受け取る側の経験やスキル、立場などによっても違ってくると思われます。

本記事では、ドメイン駆動設計を、次のような指針の集まりとして捉えています。


[基本的な指針]

・アプリケーションの本質はドメインにあります。

・ドメイン知識から導き出したユビキタス言語を使用します。

・ユビキタス言語を、チーム内のコミュニケーションやドキュメント、クラス名やメソッド名、サービス名などすべてに使用します。

・ドメインモデル、ユビキタス言語、実装の間の整合性と追跡可能を保ちながら、反復型で開発します。

いきなり、ドメイン駆動設計のキーワードを使いましたが、それらは後で説明します。


[実践的な指針]

・モデル駆動開発、オブジェクト指向技術、アジャイル手法を活用します。

ただし、「分析モデル」や「設計モデル」のように、工程によってモデルを分離しません。

1つのモデルを使います。


ドメイン駆動設計とは、このような指針を追加した、オブジェクト指向アプローチと捉えています。


[目次に戻る]

ドメイン駆動設計の始め方

始める時の前提として、オブジェクト指向モデリングの基礎知識は必要だと思います。

ドメイン駆動設計において、オブジェクト指向技術は必須の技術ではありません。

しかし、ドメインモデリングを行うための現実的な手法としては、オブジェクト指向技術になると思います。


挫折しないように、次の点に気を付けてください。

・ドメイン駆動設計を必要以上に難解に考えないこと。

・最初から多くのこと (パターンやプラクティス) を実践しようとしないこと。

・「最初の壁」(後述) に当たった時の対策を考えておくこと。

・反復型で開発すること。

・DDD本は2003年頃に書かれています。当然、実装環境はその頃よりも進化しています。従って、最近の環境を考慮しながら取り入れていくこと。


次のような始め方を推奨します。

まず、ドメイン知識を持ったメンバーが必要です。

最初は、ドメイン内の幾つかの中核オブジェクトのモデリングから始めます。

中核オブジェクトとは、例えば、対象ドメインが病院業務であれば、「医師」や「病室」です。

ポイントは、小さく始めることです。

また、比較的説明しやすいオブジェクトを選びます。

オブジェクト記述とは、そのオブジェクトを定義した短い文章です。

[例]オブジェクト名:医師

[例]オブジェクト記述:医師免許を有し、医療および保健指導を行う役割。

オブジェクト名やオブジェクト記述に含まれる単語は、「ユビキタス言語」の語彙に加えていきます。

作成したドメインモデルを検証し、さらに、リファクタリングを繰り返します。

ドメインモデルを動かすことで、検証精度が上がります。

繰り返しによって、オブジェクト記述もより明瞭にしていきます。

他のオブジェクトを発見したら、ドメインモデルに追加します。

ドメインモデル以外の、例えばプレゼンテーション層や永続化層のオブジェクトを作成して、動くソフトウェアモデルを構築します。

ドメインモデルの洗練を繰返しながら、同時に、ドメイン駆動設計スキルも向上させていきます。

本記事では、このような始め方を推奨します。


[目次に戻る]

最初の壁

ドメイン駆動設計やオブジェクト指向の導入段階で発生しやすい問題 (壁) について説明します。

ドメイン駆動設計を始めても、結局、従来のやり方に戻ってしまうというケースがあります。

その原因の1つは、下図に示すような「立ち上がり期」の停滞感です。

停滞感とは、開発チームが空回り気味で、具体的な成果物が無いというような状況です。

打開策が見つからなければ、以前のやり方に後戻りすることになります。

ドメイン駆動設計やオブジェクト指向を使った成功経験や確信が無い場合、この「最初の壁」で挫折してしまうことになります。

停滞の原因が、過剰な議論によるものなのか、時間だけの問題なのかなど、冷静に見極める必要があります。

DDD本のプラクティスを真似をするだけではうまくいかないでしょう。

例えば、プラクティスを1つだけにするとか、経験者やメンターを探す等の対策が必要です。

開発スピードの立ち上がりの比較図

※注意:上のグラフは感覚的なものです。

この最初の壁を乗り越えられなければ、ドメイン駆動設計の効果を得られる前に、従来型に戻ることになります。

[目次に戻る]

ドメイン駆動設計の必要性

ドメイン駆動設計の必要性を示すために、「利口なUI」パターンについて説明します。

これは、ドメイン駆動設計やオブジェクト指向の観点では、アンチパターンです。

つまり、よくない設計や実装のパターンです。

しかし実際には、このパターンで作られたシステムは、たくさん存在しています。

そして、このようなシステムの存在が、ドメイン駆動設計の必要性を分かり難くしているようです。


例として、Struts系のフレームワークを使ったWebアプリケーションを考えてみます。

もし、Webアプリケーションが下図のような構造になっていれば、それは「利口なUI」パターンと言えます。

トランザクションスクリプトの例

(A) や (B) では、JSPやアクションの中にSQL文が組込まれていて、そのSQL文で多くの業務ロジックが実現されています。

そして、同じようなSQL文が、複数のJSPやアクションの中に散在しています。

(C) では、Modelと称するレイヤやクラスがありますが、実態は、DAOやDTOと呼ばれるような役割 (バケツリレーのバケツ) しか持っていません。

(A) (B) と同様に(C) の場合も、SQL文が重複したり散在します。

そして、ドメインの本質的な振る舞い (ビジネスロジック) は、各画面の都合に合わせたSQL文で実現されています。

たとえ、一部のORMフレームワークなどを使って、SQL文を隠ぺいしていたとしても、問題の本質は同じです。


ドメイン駆動設計あるいはオブジェクト指向で設計されていれば、オブジェクトはそれにふさわしい名前と責務を持ちます。

そして、それらが協調することでビジネスロジックを実現します。

ふさわしい名前や適切に割り当てられた責務は、アプリケーション全体を見通しやすくします。


一方、SQLで実現されたロジックには、オブジェクトの存在や役割に相当する概念は無く、いくつかのデータエンティティに跨った処理になっています。

そのため、コードの目的が分かり難く、再利用や変更による影響も分かり難くなります。

開発の初期段階においては、大きな問題にはならないかもしれません。

しかし、ソフトウェア・エントロピー (複雑度) がすぐに増大して、手に負えなくなるはずです。

ドメイン駆動設計は、このような問題の解決に必要なのです。


しかし、利口なUIには次のような側面があり、それによって問題点が見過ごされてしまうようです。

DDD本には次のように書かれています。

※引用:ここから (DDD本の第4章 p.75)

・単純なアプリケーションの場合、生産性が高く、すぐに作れる。

・それほど有能でない開発者でも、この方法ならほとんど訓練しないで仕事ができる。

※引用:ここまで (引用元には箇条書きで他に5点示されています)


これは、多数の開発者を一時的に集めて作業をするようなプロジェクトにおいては、都合の良い面もあるかもしれません。

しかし、プロジェクトの後半や仕様変更、保守、改修といった場面では、利口なUIが利点となっているケースは少ないでしょう。


DDD本には「利口なUI」の欠点についても次のように書かれています。

※引用:ここから (DDD本の第4章 p.75)

・アプリケーションの統合は困難で、データベースを経由させるしかない。

・ふるまいが再利用されことも、ビジネスの問題が抽象化されることもない。ビジネスルールは、適用先の操作それぞれで複製されることになる。

※引用:ここまで (引用元には箇条書きで他に2点示されています)


ドメイン駆動設計は、これらの問題を解決するために必要です。

[目次に戻る]

ドメイン駆動設計のキーワード

この章では、ドメイン駆動設計の主要な概念について考えます。

ここで、留意する点は、DDD本の出版は 2003年で、15年以上も前だということです。

当然、アプリケーションの構築に使用できるフレームワークなどの要素技術は、当時よりも進化しています。

そのため、DDD本に書かれている一部の概念については、使用可能な要素技術などを踏まえて再考することも必要です。

本記事では、JPA等のORMの進化を踏まえて、ファクトリやリポジトリについては記載していません。

ソフトウェア

一般にソフトウェアとは、コードだけではなく、設計書などのドキュメントを含めたものを指します。

ドメイン駆動設計の場合は、次の「ユビキタス言語」「ドメインモデル」「ドメインモデルの実装」「ドメインモデル以外の実装」が含まれます。

アプリケーションの構成図

[目次に戻る]

開発チームと実践的モデラ

ドメイン駆動設計には、開発チームに関して、「実践的モデラ・パターン」と呼ばれるものがあります。

開発チームは、ユーザ、ドメインエキスパート (業務の専門知識をもつ人) 、ソフトウェア技術者で構成されます。

チーム内では、ユビキタス言語 (後述) を使ってコミュニケーションを行います。

実践的モデラ・パターンでは、分析、モデリング、設計、プログラミングというように、過度に役割を分離しません。

これは、チームのメンバー全員が、モデラであり、プログラマであるべき、という考え方です。

しかし、現実問題として、実践的モデラ・パターンでチームを構成することが困難な場合もあります。

ただし、そのような場合でも、全員がモデルと実装コードを理解し、関心と責任を持つことが求められます。

[目次に戻る]

ユビキタス言語

ドメイン駆動設計には、ユビキタス言語という概念があります。

どんなチームでもコミュニケーションは重要です。

しかし、どうしても、人や役割によって使用する用語の意味が、微妙に違っていることがあります。

ユビキタス言語とは、ドメインエキスパートの知識やドメインモデルをもとに、用語の微妙な違いを排除し、より正確なコミュニケーションを行うためのものです。

ドメインモデルや実装モデルの要素の名前とも正確に対応付けします。

ユビキタス言語は、モデルや実装コードのクラス名、メソッド名などと双方向に追跡可能 (紐付けられる) にします。


※DDD本 p.26 から引用:ここから

「モデルを言語の骨格として使用すること。 チーム内のすべてのコミュニケーションとコードにおいて、その言語を厳格に用いることを、チームに約束させること。 図やドキュメント、そして何より会話の中では同一の言語を使用すること。 言語を使う上で問題があれば、代わりの表現を用いて実験することで、問題を取り除くこと。 そうした表現は代りとなるモデルを反映している。 そこで、新しいモデルに合わせてコードをリファクタリングし、クラス、メソッド、モジュールの名前を変更すること。 会話の中で用語が混同されていたら、普通の単語の意味について認識を合わせるのと同じやり方で解決すること。 ユビキタス言語における変更は、モデルに対する変更であると認識すること。」

※DDD本から引用:ここまで

[目次に戻る]

ドメインモデル

ドメイン駆動設計では、アプリケーションの本質は、アプリケーション化対象領域である「ドメイン」です。

ドメインモデルとは、あるドメインに存在する概念を抽象化したものです。

Webアプリケーションでよく使われているレイヤ化アーキテクチャであれば、ドメイン層やサービス層に位置するオブジェクト群です。

ドメインモデルは、ユビキタス言語や、UMLなどのモデリング言語、プログラミング言語で定義されます。

ドメインモデルを構成するオブジェクトには、エンティティ、値オブジェクト、サービスがあります。

[例] ドメイン:書店在庫管理, エンティティ:書籍, 値オブジェクト:ISBN, サービス:在庫移動処理

[目次に戻る]

エンティティ

ドメイン駆動設計における「エンティティ」とは、ドメインモデルを構成するオブジェクトの種類です。

エンティティの他にも、「値オブジェクト」や「サービスオブジェクト」があります。

エンティティは、連続性と識別性を持ち、システムのライフサイクルを通して一意に識別されます。

ドメイン駆動設計では、多くの場合、オブジェクト指向モデリングによってオブジェクトを見つけます。

ただし、オブジェクト指向モデリングの経験者でも、オブジェクトを見つけることは簡単ではありません。

そして、答えは1つとは限られません。

そこで、オブジェクトを見つけるときのヒントを紹介します。

少々古典ですが、シュレイアー氏とメラー氏による「オブジェクト指向システム分析」という本があります。

その本には、オブジェクトの候補として以下が挙げられています。参考になると思います。

  • 有形物
    • 人、商品、伝票。
  • 役割
    • 人や構成により演じられる役割。医師、患者、顧客、従業員。医師は患者にもなる。
  • 出来事
    • 飛行、事故、故障、サービス要求。
  • 相互作用
    • 買い入れ、結婚。
  • 仕様
    • 製品などの仕様。

DOA (データ中心アプローチ) でも、上に挙げたような概念やモノは、テーブルの候補となるでしょう。

そして、それらの概念やモノは、ドメイン駆動設計のエンティティの有力な候補です。

各エンティティの責務や協調者についても、分析、設計、実装を行うことで、ドメイン駆動設計のドメインモデルとなっていきます。

少し乱暴ですが、各論理テーブルは、(かなりの確率で) 妥当なエンティティと言えます。

テーブル名や列名は、そのクラス名や属性名となります。

ただし、テーブルにはありませんが、エンティティは「メソッド」を持ちます。

主にメソッドによって、ドメインにおける責務を実現します。

これは、重要な違いです。

[目次に戻る]

値オブジェクト

ドメイン駆動設計の値オブジェクトとは、ドメインモデルを構成するオブジェクトの類型の1つです。

エンティティとは異なって、識別性を持たない、変更不可の不変 (immutable) オブジェクトで、その属性だけに関心があるようなオブジェクトです。

多くの場合、エンティティオブジェクトの状態を記述する属性として振る舞います。

例として、図形処理アプリケーションの座標点クラス (Point) があります。

多くの場合、エンティティの属性の状態を現すクラスは値オブジェクトとなります。

値オブジェクトには一意性は必要なく、同値性が重要です。


エンティティの属性の状態を示すためには、使用するプログラミング言語のプリミティブ型で十分なケースも多くあります。

例えば、Pointクラスではなくても、実数値を2つ持った2次元配列で十分と思われるケースもあります。

しかし、Pointクラスのような値オブジェクトをドメインモデルに追加することで、ドメインモデルをより洗練できることもあります。

ただし、過度に追加するとクラスの爆発と言われるような状況に陥ることもあります。

[目次に戻る]

エンティティと値オブジェクトの例

このサンプルは、正確な医療情報や知識に基づくものではありませんので注意してください。

集約のクラス図

患者オブジェクトは、血糖値とヘモグロビンA1cの値から、血糖コントロールの状況を判定して、その結果を応答します。

血糖値は、空腹時血糖値、食後血糖値など1日の中でも変動する検査値です。

ヘモグロビンA1c (HbA1c) は、おおよそ2カ月間の平均血糖値を推定できる検査値です。

血糖コントロールとは、運動や食事、薬などで、血糖値を適正範囲に保つことです。


ドメインモデリングにおいて、正解は一つとは限りません。

「血糖コントロール状況を応答する」という責務を、患者オブジェクトが持つこともあれば、持たないドメインモデルもあるでしょう。

例えば、次図のように診療記録オブジェクトが「血糖コントロール状況を応答する」というメソッドをもつドメインモデルも考えられます。

集約のクラス図2

さらに、医師オブジェクトや診断サービスなどを追加するモデリングも考えられます。


[目次に戻る]

サービス

ドメインには、エンティティや値オブジェクトにふさわしくない概念も存在します。

それらは、サービスとしてモデリングすると自然な場合があります。

サービスは基本的に状態を持ちません。

エンティティや値オブジェクトではなく、アクションや操作といった概念として存在します。

[参考] GRASPパターンの純粋人工物 (Pure Fabrication)


例えば、在庫管理業務の中に、「倉庫間移動」という業務処理があります。

倉庫間移動とは、「ある倉庫の在庫商品を別の倉庫に移動する」ことです。

ここで、この倉庫間移動は、どのオブジェクトの責務でしょうか。

単独のオブジェクトの責務とする場合もあれば、複数のオブジェクトの相互作用とする場合もあるでしょう。

例えば、

(1) 倉庫クラスの静的メソッドとします。

     Warehouse#transferStock(倉庫1, 倉庫2, 商品, 数量)

(2) 倉庫クラスのインスタンスメソッドとします。

     warehouse.transferStock(別の倉庫, 商品, 数量)

などが考えられます。

しかし、ある責務を、あるエンティティオブジェクトや値オブジェクトの責務とすると、不自然な場合があります。

このようなときには、サービスオブジェクトを検討します。

そうして、次のように設計してみます。

(3) 倉庫サービスの倉庫間移動

     StockService#transferStock(倉庫1, 倉庫2, 商品, 数量)


ドメインモデルのクラス図


サービスオブジェクトを適切に追加することで、適切な責務割当てを実現できる場合があります。

サービスオブジェクトは、デザインパターンのファサード (Facade) としての役割もあります。

但し、色々な責務をサービスに詰め込んでしまうと、アンチパターンと同類になってしまうので、注意してください。

まずは、適切なエンティティや値オブジェクトを検討します。


例として、平面に図形描画する処理系を実現する場合を考えてみます。

円や矩形の描画処理は、どのように設計・実装すべきでしょうか。


ケース(1)

DrawingService.drawCircle(x, y, radius);

DrawingService.drawRectangle(x, y, width, height, angle);


ケース(2)

Circle circle = new Circle(x, y, radius);

circle.draw();

Rectangle rectangle = new Rectangle(x, y, width, height, angle);

rectangle.draw();


ケース(1)の場合、DrawingServiceはすぐに肥大化し、役割は不明瞭になるでしょう。

ケース(2)の方が適切でしょう。


なお、ここでは、在庫管理業務や描画処理系そのものについては議論していません。

業種や組織によっても最適なドメインモデルは違うでしょう。

[目次に戻る]

モジュール

ドメイン駆動設計のモジュールという概念は、Javaのパッケージと同義です。

モジュール名もユビキタス言語に含まれる名前です。

モジュール名、モジュール構成もリファクタリングの対象です。

モジュール間は低結合、モジュール内は高凝集にします。

[目次に戻る]

集約

集約を見つけ出し適切にモデリングすることは深い問題です。

※引用:ここから (DDD本 p.123)

関係を最小限に抑えるように設計することにより、 関連を辿る処理は単純化され、 関係性の爆発的増加もある程度は制限される。 しかし、ほとんどのビジネスドメインは非常に強く相互に結びついているので、 結局はオブジェクトの参照を通じて、長くて深い経路を辿ることになる。 ある意味で、こうしたもつれはこの世界の現実を反映している。 現実には、はっきりした境界が引いてもらえることはめったにないのだ。 これはソフトウェアの設計における問題である。

※引用:ここまで (DDD本 p.123)


次図は集約の例です。ドメインとしては架空の書店の書籍情報管理を想定しています。

出版社の業務であれば、集約ルートとしては、書籍よりもISBNの方が適切かもしれません。

(DDD本の「境界づけられたコンテキスト」も参考になります)

実際の開発では、必要なユースケースを実行できるように、リファクタリングと検証を繰り返しながら、ドメインモデルを作り上げていきます。

価格やISBNは、使用するプログラミング言語の数値型や文字列型で十分な場合もありますが、ドメインモデルに価格クラスやISBNクラスを追加することで、より洗練された分かり易いモデルになることもあります。

集約のクラス図3

 ・集約ルートエンティティはグローバルな同一性を持ちます。

 ・境界内部のエンティティは境界内でのみ一意となるローカルな同一性を持ちます。

 ・境界外にあるオブジェクトは、ルートエンティティへの参照を保持できます。

 ・境界外にあるオブジェクトは、境界内部への参照を保持することはできません。集約ルートを介して参照します。

[目次に戻る]

ドメイン駆動設計と他の手法の関係

ドメイン駆動設計では、オブジェクト指向、モデル駆動開発、アジャイル開発を活用します。

また、他にも共通するコンセプトや活用できそうな手法があります。

ここでは、他の手法とドメイン駆動設計の立ち位置を考えてみます。

オブジェクト指向でドメインモデリング

ドメイン駆動設計のドメインモデリングを行うために、必ずしもオブジェクト指向モデリングを使う必要はありません。

しかし、これまでの実績やモデル駆動開発との相性を考えると、今のところ、オブジェクト指向技術が最適と言えます。

ドメインモデル以外のビュー層やインフラストラクチャ層などにも適用します。

ドメイン駆動設計では、オブジェクト指向技術は必要な技術です。

[目次に戻る]

モデル駆動で反復型開発

モデル駆動開発 (MDD) とは、OMG (Object Management Group) が提唱するモデル駆動アーキテクチャ (MDA) に則ったソフトウェア開発手法です。

モデル駆動開発では、分析・設計・実装・テストといった開発の流れの中でモデルを作成し、モデル変換を繰り返すことでアプリケーションを開発 (実装ソースコードを自動生成) する手法です。

ドメイン駆動設計の反復型開発においても、MDDの考え方に従います。

ただし、ドメイン駆動設計では、モデルを「分析モデル」や「設計モデル」等に分けません。

つまり、ドメイン駆動設計では、参照するモデルはただ一つとします。

例えば、担当者や工程でモデルを分離すると、追跡可能性が失われたり、実装と乖離してしまうことがあります。

このような問題を避けるために、ドメイン駆動設計では1つのモデルだけを作成します。

[目次に戻る]

アジャイル手法で反復型開発

ドメイン駆動設計では、アジャイル手法に則った反復型開発が基本です。

ウォータフォール式では、ドメイン駆動設計は成功しないでしょう。

また、最初から多くのアジャイルプラクティスやパターンを取り入れようとしても失敗するでしょう。

パターンやプラクティスを段階的に取り入れながら、ドメインモデルを高度化し深化させていく、アジャイルな開発スタイルが必要です。

そうすれば、ドメイン駆動設計のスキルも段階的に向上できます。

※ 本記事では、アジャイル開発とイテレーティブ開発、反復型開発を区別しないで混用します。

[目次に戻る]

アナリシスパターンはドメインモデリングのヒント

マーチン・ファウラー氏の著書「アナリシスパターン」には、再利用可能なオブジェクトモデルがパターンとして示されています。

これらのオブジェクトモデルは、例題のドメインだけでなく、他のドメインにも適用できるものです。

ドメイン駆動設計のドメインモデリングにおいて、参考になるでしょう。

アナリシスパターンに書かれているパターンは、よく洗練されたものであり、深いモデルです。

時として、深すぎて難しく感じるものもありますが、とても有益なヒントになります。

[目次に戻る]

デザインパターンは実装のヒント

書籍「オブジェクト指向における再利用のためのデザインパターン」には、オブジェクト指向設計、実装に関する23個のパターンが記載されています。

各パターンは主に次の4つに分類されます。

(1) 生成に関するパターン

(2) 構造に関するパターン

(3) 振る舞いに関するパターン

(4) マルチスレッドプログラミングに関するパターン

ドメイン駆動設計のドメインモデルを実装する際や、リファクタリングする際に参考になるでしょう。

もちろん、ドメイン層以外の層でも役立ちます。

書籍「オブジェクト指向における再利用のためのデザインパターン」は、エーリヒ・ガンマ、リチャード・ヘルム、ラルフ・ジョンソン、ジョン・ブリシディースの4共著です。この4人はGoFと呼ばれます。

[目次に戻る]

Naked Objects パターンには多くの共通点

Naked Objects パターンは、ドメイン駆動設計に通じるコンセプトを持っています。

アプリケーションを開発する時には、ドメインオブジェクトだけを作成します。

ユーザインタフェース層やデータアクセス層は、ドメインオブジェクトから自動で作成します。

優れたドメインモデルがあれば、ドメインモデルの各インタフェースをビューとしてユーザに公開するだけで、アプリケーションとして機能する、という考えです。

ドメインモデルの各インタフェースとは、サービスやクラスのメソッドやプロパティのことです。

ビューは、画面のことです。

本記事で紹介する「DDBuilder」は、 Naked Objects パターンにインスパイア (触発) されたものです。


「Naked Objects for .NET - 生産性の高い.NETフレームワーク- InfoQ」から引用します。

※引用:ここから

ユーザインタフェースのレイヤやデータアクセスのレイヤを書く必要がないだけでなく、naked objectsパターンは良いオブジェクトモデリングも促します。

なぜならドメインモデルのプロトタイプを直ちにエンドユーザが評価できるアプリケーションにすることができるからです。

これらのことを聞いた時にほとんどの人は、大規模で複雑なビジネスアプリケーションではおそらく有効でないだろう、という反応を示します。

※引用:ここまで

引用元のサイトでは、大規模アプリケーションの事例も紹介されています。

[目次に戻る]

CRCでモデリングの練習

オブジェクト指向分析ツールにCRCカード (Class Responsibility Collaborator) というものがあります。

ドメイン駆動設計で、ドメインオブジェクトの見つけ方で悩まれているならば、CRCカードを使ってみる価値があると思います。

[目次に戻る]

ドメイン駆動設計によるドメイン中心開発

ドメインモデルの役割

アプリケーションの本質はドメインです。

画面のデザインや永続化の仕組みなどは、本質ではありません。

ドメインに存在する本質的な振る舞いはすべて、ドメインモデルの役割です。

ドメイン駆動設計によるアプリケーション開発において、ドメインモデルはアプリケーションの土台であり、開発の中心軸となります。

ドメインモデル中心の反復型開発

ドメイン駆動設計では、反復型開発が基本です。

反復型開発は、ソフトウェアの完成度を段階的に上げていく開発手法です。

開発者はドメインモデルを繰り返しリファクタリングすることで、ドメインモデルの完成度を上げていきます。

反復する単位をイテレーションと呼びます。

各イテレーションの中では、分析から設計、実装、検証までを完結させます。

そのため、イテレーションは、ミニウォーターフォールとも言われます。

また、ドメイン駆動設計では、ドメインモデル、実装、ユビキタス言語の3つを整合させながら反復することが重要です。

(反復型開発は、イテレーティブ開発やインクリメンタル開発、アジャイル開発などと似ていますが、本記事では、特に区別しません)


[反復型開発の流れ]

反復型開発の流れ図

イテレーションの流れ

下図は、1回のイテレーションの流れです。

開発チームは、下図のような作業を繰り返します。

開発チームによって多少の違いはありますが、大筋は同じでしょう。

DDBuilder

まず、開発チームは、ドメインモデリングを行い、ドメインモデルを実装します。

次に、動くソフトウェアモデル (ここでは、Webアプリケーション) に必要なプレゼンテーション層や永続化層を実装します。

最後に、Webアプリケーションを起動して、ドメインモデルの振る舞いを検証します。

期待通りの振る舞いを確認できれば、次のイテレーションに着手します。

実装モデル

実装モデルとは、動くソフトウェアモデル。(ソフトウェアで表現された「動くモデル」)

ドメインモデルの実装 (例:ドメイン層) と、ドメインモデルを動かすための実装 (例: ビュー層や永続化層) を含んだものです。

ドメインモデルを動かすための方法は色々考えられます。

例えば、Webアプリケーションのドメイン層にドメインモデルを置いて動かす方法です。

あるいは、テストドライバーからドメインモデルを動かすような方法も考えられます。


実装モデルは、次の理由で必要です。

・実装モデル は、各イテレーションの成果物のひとつです。

・実際に動かすことで、ドメインモデルをより正確に検証できます。

・具体的に動かすことで、ドメインとドメインモデルについての、開発チーム内での知識や理解のバラつきを小さくします。

・ドメインモデリングに関する過剰な分析や議論の空回りを予防します。

・技術寄りではないメンバーでも検証しやすくなります。


モデル図や単体テストレベルのテストコードだけで検証しようとすると、手間がかかり、検証の精度も上がりません。

動作可能な実装モデルがあれば、UML図などに不慣れなメンバーでも、検証作業が容易になり、精度も上がります。

アプリケーション自動生成の利点

イテレーションの主要なタスクは、ドメインモデリングです。

ドメインモデリングをスムーズに繰り返すためには、ドメインモデルを素早く検証できる仕組みが必要です。

ドメインモデルを検証するためには、「動くソフトウェアモデル」を使う方法が最適です。

しかし、イテレーションを繰り返しながら、「動くソフトウェアモデル」を保守することは大変です。

それは、ドメインモデルをリファクタリングすると、メインモデル以外の実装にも変更が必要になるからです。

例えば、「動くソフトウェアモデル」が Webアプリケーション型であれば、ビュー層や永続化層にも変更が必要です。

これらの変更作業は、単純で面倒な作業です。

そのため、イテレーションサイクルは長くなり、開発者はドメインモデリングに集中できなくなります。

イテレーションの作業負荷が大きくなって、反復できなくなるケースもあります。


もし、ドメインモデルからアプリケーションを自動生成できれば、このような本質的ではない作業を軽減できます。

その結果、開発者はドメインモデリングに集中できるようになります。

ドメイン駆動設計を効果的に実践するためには、軽量なイテレーションの仕組みが必要です。

[目次に戻る]

アプリケーション自動生成ツール

DDBuilder

DDBuilderは、ドメイン駆動設計のドメインモデルからJava Webアプリケーションを自動生成します。

当社が開発し公開している無料・無保証のソフトウェアツールです。


ダウンロード...


ユーザガイド [PDF] [2.5MB]


[動作確認済みの環境]

・Java 8

・Windows7 pro 32bit/64bit, Windows10 pro 64bit

・Eclipse IDE for Java EE Developers 4.4, 4.3

・Apache Tomcat 7.0, 8.0, 8.5, 9.0


[インストールとアンインストール]

インストール/アンインストールは不要です。

ダウンロードしたzipファイルを適当な場所に解凍してください。

不要になった場合は、 それらをエクスプローラなどで削除してください。

※ ダウンロードしたファイルを解凍すると、次の説明ファイルが含まれています。

「はじめにお読みください(使い方).txt」

「DDBuilder_Guide_Ver2.pdf」


[サポート]

使いはじめ等で、ご不明な点がありましたら「お問合せフォーム」からご連絡ください。 (無料)

弊社の都合で返信が遅くなる場合がありますが、ご了承ください。

また、有料でのサポートも別途用意しております。詳しくはお問合せください。


[オープンソースで公開]

GiHubでオープンソースとして公開しています。 [Apache License Version 2.0]

- DDBuilder (ツール本体)

https://github.com/nextdesign-co-jp/DDBuilder.git

- DDBuildertemplate (テンプレート)

https://github.com/nextdesign-co-jp/DDBuilderTemplate.git


[OSSへの謝辞]

DDBuilderは次のソフトウェアを利用しています。

Apache Wicket 7.4.0

hibernate-entitymanager4.3.6.Final

Derby10.10.2.0

log4j2.5

Eclipse jdt3.9.1

[目次に戻る]

自動生成の流れ

まず、ドメインとして、簡単な「在庫管理業務」を考えます。

このドメインモデルには、4つのクラスが存在しています。

以下に、4つのクラスのクラス名、クラス図、Java実装コードを示します。


(クラス名)

倉庫クラス : Warehouse

在庫クラス : Stock

製品クラス : Product

在庫サービスクラス : StockService


(クラス図)

サンプルドメインモデルのクラス図

(Java 実装例)

Javaコードを表示する...


なお、このドメインモデルは、自動生成機能を説明するためのものです。

そのため、ドメインモデルらしい振る舞い等は含んでいません。

つまり、ドメインモデル貧血の状態であることに注意してください。

自動生成の結果

DDBuilderは、ドメインモデルのJava実装コードを読み込み、Webアプリケーションに必要な、ビュー層や永続化層を生成します。

生成結果は、1つのフォルダに出力され、そのフォルダは、そのまま Eclipse にインポートできます。

つまり、出力フォルダは「Eclipse 動的 Web プロジェクト」の構成になっています。

下図は、出力フォルダの内容です。

Eclipseプロジェクト構成

.settings, .classpath, .project は Eclipse の設定ファイルです。

src フォルダ配下に、domain, persistence, service, viewパッケージフォルダがあります。

lib フォルダの配下には、必要な OSS のライブラリが含まれています。

Webアプリケーションの画面例

EclipseにインポートしたWebアプリケーションを起動します。

以下は、そのWebアプリケーションに、Chromeブラウザからアクセスした時の画面です。


[ホーム画面]

http://localhost:8080/myapp/ にアクセスします。(myapp は DDBuilderの操作画面で指定したアプリケーションの名前です)

ホーム画面として、3つのボタンを持つ画面が表示されます。


ホーム画面

他の画面例を表示する...

なお、Eclipseへのインポートや起動方法の詳細は「開発の流れ」を参照してください。

[目次に戻る]

自動生成されるWebアプリケーションのレイヤ構成

下図は、DDBuilderが自動生成する Java Web アプリケーションの構成図です。

DDD本 (p.70) のレイヤ化アーキテクチャに準じています。


アプリケーションのレイヤ化アーキテクチャ図


次のオープンソースソフトウェアを使用しています。

・ビュー層:Apache Wicket

・永続化層 (インフラストラクチャ層):Java EE JPA / Hibernate

[目次に戻る]

自動生成されるクラス

開発者が実装するドメインクラス (エンティティ) と、DDBuilderが自動生成するクラスについて説明します。

例として、開発者が Product.javaというドメインクラス (下図の黄色の網掛けのクラス) を作成します。

次に、DDBuilderで自動生成を実行すると、下図ようなクラスとHTMLファイルが自動生成されます。(下図は、すべてではなく、主なファイルです)

view, service, domain, persistenceはパッケージ名です。

それぞれ、ビュー層、サービス層、ドメイン層、永続化層に相当します。

開発者が Product.javaを実装すれば、ビュー層、永続化層のクラスは自動的に生成されます。

自動生成されたものは、そのまま Java Webアプリケーションとして動作します。

Productの登録、更新、削除、一覧がWeb画面で操作できます。

自動生成されるファイル

ビュー層の1つの画面は、同名のHTMLファイルとJavaクラスで構成されます。

例: ProductPage.htmlとProductPage.java。

これは、ビュー層で使用している Apache Wicket フレームワークの仕様です。

Wicketは、JSP/Struts系のアーキテクチャではなく、Jave EE JSF 系のアーキテクチャです。

HTMLファイルは独立しており、通常のHTMLと同様に編集できます。

つまり、JavaやJSPなどの知識が不要なので、デザイン専門の方でも編集できます。

Wicketでは、画面は、完全に Java オブジェクト指向プログラミングで実装できます。

例えば、Productを使用するサービスが必要になった場合は、上図のSomeService.javaのように追加できます。

もちろん、サービス名は適切な名前を付けます。数は制約されません。

自動生成された画面だけでは不足する場合は、画面を追加できます。(上図では、CustomePage)

自動生成を再度実行しても、SomeServiceやCustomePageは上書きされません。

詳しくは、「DDBuilderを使った開発の流れ」を参照ください。

[目次に戻る]

使用できる関連のタイプと属性型

このセクションでは、DDBuilderがサポートする関連の種類と、インスタンス属性型を示します。

この例では、JPAアノテーションを使って、エンティティ間の関連を定義しています。

ただし、必ずしもJPAアノテーションを使って定義する必要はありません。

というのは、実際のドメインモデリングでは、決定的に関連を決めることが大変難しい場合もあるからです。

もちろん、関連を確実に定義できるところは、この例のように実装して、

あいまいさが残る部分は、JPQLやネイティブSQLを使って、関連を実装することも検討すべきです。

[サンプルコードをダウンロード]


UMLクラス図

関連の例


サンプル コードには次の5つのクラスが含まれています。

(1) 著者 @Entity

public class Author extends DdBaseEntity

(2) 書籍 @Entity

public class Book extends DdBaseEntity

(3) 版 @Entity

public class Edition extends DdBaseEntity

(4) ISBN @Entity

public class Isbn extends DdBaseEntity

(5) 書店 @Entity

public class Store extends DdBaseEntity

関連タイプと属性型の Java サンプルコード


/*
 * DDBuilderで使える関連タイプと属性型に関する例です。ドメイン駆動設計的な観点の例ではありません。
 */
package jp.co.nextdesign.domain;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.OneToMany;
import javax.persistence.Transient;
import jp.co.nextdesign.domain.ddb.DdBaseEntity;

/**
 * 著者
 */
@Entity
public class Author extends DdBaseEntity {
    private static final long serialVersionUID = 1L;

    /** 名前 */
    private String name;
    
    /** 書籍リスト owning/parent */
    @OneToMany(mappedBy="author", cascade=CascadeType.ALL, orphanRemoval=true)
    private List<Book> bookList;

    /** 書籍リスト owning/parent */
    @OneToMany(mappedBy="author2", cascade=CascadeType.ALL, orphanRemoval=true)
    private List<Book> bookList2;

    /** コンストラクタ */
    public Author(){
        super();
        this.name = "---";
        this.bookList = new ArrayList<Book>();
        this.bookList2 = new ArrayList<Book>();
    }
    
    //OneToManyで双方向関連を維持するためのコードを含むgetBookList(),
    //setBookList(List<Book> bookList)の例
    @Transient
    private ArrayList<Book> latestBookList = new ArrayList<Book>();
    public List<Book> getBookList() {
        return this.bookList;
    }
    public void setBookList(List<Book> bookList) {
        for(Book newBook : bookList){
            if (!latestBookList.contains(newBook)){
                newBook.setAuthor(this);
            }
        }
        for(Book oldBook : latestBookList){
            if (!bookList.contains(oldBook)){
                oldBook.setAuthor(null);
            }
        }
        this.bookList = bookList;
        latestBookList = new ArrayList<Book>(this.bookList);
    }

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public List<Book> getBookList2() {
        return bookList2;
    }
    public void setBookList2(List<Book> bookList2) {
        this.bookList2 = bookList2;
    }
    
    @Override
    public String getDDBEntityTitle(){
        return this.name;
    }
    
    /** debug */
    public String getDebugInfo(){
        String info = "<" + this.getClass().getSimpleName() + ">";
        info += "\nname=" + this.getName();
        info += "\n</" + this.getClass().getSimpleName() + ">";
        return info;
    }
}
[すべてのクラスを表示]

【注意】

この例は、DDBuilderがサポートする属性型と関連の型を示すためのものです。

そのため、ドメインモデルとしての重要な責務 (メソッド) は省略しています。


【注意】

このサンプルモデルはシンプルですが、現実には、集約 (AGGREGATES) についても注意深くモデリングする必要があります。

JPAのアノテーションを使って、すべての関連を実現しようとしないことです。

JPQLやネイティブSQLを使って、関連を実装することも検討すべきです。


※引用:ここから (DDD本 p.123)

関係を最小限に抑えるように設計することにより、 関連を辿る処理は単純化され、 関係性の爆発的増加もある程度は制限される。

しかし、ほとんどのビジネスドメインは非常に強く相互に結びついているので、 結局はオブジェクトの参照を通じて、長くて深い経路を辿ることになる。

ある意味で、こうしたもつれはこの世界の現実を反映している。

現実には、はっきりした境界が引いてもらえることはめったにないのだ。

これはソフトウェアの設計における問題である。

※引用:ここまで (DDD本 p.123)


[目次に戻る]

自作ページの追加

自動生成されたWebアプリケーションに自作のページを追加できます。

DDBuilderは、自作ページを上書きしません。

ページを自作する場合には、自動生成されたコードが参考になるはずです。

より複雑なページの例も本サイト内で公開しています。

ページサンプル : Wicket DataTable ソート列 アクション列 テーブル

ページサンプル : 改ページ付き・ソート列付き・1件複数行対応 テーブル

[目次に戻る]

クラス図の扱い

DDBuilderにはクラス図をサポートする機能はありません。

しかし、ドメイン駆動設計を実践するうえで、クラス図やそれに相当するものは重要です。

本記事では、分析時のクラス図は手書きのラフスケッチという位置づけです。

そして、最終的なクラス図は、UMLツールなどで Java ソースからリバースエンジニアリングされることを推奨します。

リバースエンジニアリングであれば常に実装と一致させることができます。

[目次に戻る]

ツールへのロックインなし

開発チームは、DDBuilderにロックインされる (しばられる) ことはありません。

いつでもDDBuilderを切り離す (使用をやめる) ことができます。

切り離した後は、WicketとHibernateを使ったシンプルな Java Webアプリケーションとして開発や保守を継続できます。

あるいは、ドメインモデル層とサービス層を他のアプリケーションフレームワークに移植することもあるでしょう。

[目次に戻る]

Javaの利点

Java は、強い静的型付けのオブジェクト指向言語です。

DDBuilder では、ドメインモデルの実装言語は Java です。

自動生成されるアプリケーションも Java Webアプリケーションです。

Java は静的型付け言語なので、Eclipseなどのリファクタリング機能を使って安全に変更できます。

このことは、ドメインモデルを繰り返しリファクタリングしていくうえで、強力な利点です。

また、ドメインモデルを他の言語や他のフレームワークへ移植する場合にも、元の言語が Java であれば、より安全に移植できるでしょう。

[目次に戻る]

Apache Wicketの利点

自動生成されるアプリケーションのビュー層には、Wicketを使用します。

Wicketは、ビュー層を担う Java Web アプリケーションフレームワークです。

Struts系と違い、 オブジェクト指向プログラミングモデルを前提としたフレームワークです。

HTMLとの独立性が高く、基本的にすべてをJavaコードで実装します。

開発者が Wicketを意識するのは、自作ページを追加するときです。

[目次に戻る]

Java EE JPAの利点

自動生成されるアプリケーションの永続化層には、Hibernateを使用します。

Hibernateは、JavaEE JPA実装の1つです。

自動生成されるアプリケーションでは、特に指定がなければ、データベースとして、Java標準のJavaDBを使用します。

なお、Hibernateは、PostgreSQL, MySQL, Oracle, SQLServer, DB2などの代表的なDBMSをサポートしています。

なので、DDBuilderが自動生成したWebアプリケーションでも、設定ファイルを変更するだけでDBMSを変更できます。

ドメインモデル層のクラスを実装するときには、JPA / Hibernate の知識が必要になります。

主に、関連を定義するためのアノテーションの書き方などの知識が必要になります。

[目次に戻る]

トランザクション管理

ドメイン駆動設計されたアプリケーションでは、Java EE JPA の永続化コンテキスト (EntityManager) を使用してトランザクション管理を行います。

JPA の仕様としては、永続化コンテキストには、次の2種類があります。


[永続化コンテキストの種類]

(1) コンテナ管理

     永続化コンテキストのライフサイクル (生成、破棄) は、EJBコンテナによって管理されます。

(2) アプリケーション管理

     永続化コンテキストのライフサイクルは、アプリケーションによって管理されます。EJBコンテナは必要ありません。

DDBuilderが生成するアプリケーションでは、(2)のアプリケーション管理を使用しますので、特別なコンテナを必要としません。

永続化コンテキストのライフサイクルは、自動生成されたコードによって行われますので、DDBuilder利用者は、特に意識する必要はありません。


トランザクションを管理するためには、トランザクションマネージャを使用します。

トランザクションマネージャには、次の2種類があります。


[トランザクションマネージャの種類]

(1) JTAトランザクション

     EJBコンテナが必要です。トランザクション管理は、EJBコンテナによって自動的に行われます。

(2) リソースローカルトランザクション

     EJBコンテナは必要ありません。

DDBuilderが生成するアプリケーションでは、(2)のリソースローカルトランザクションを使用しますので、特別なコンテナを必要としません。

トランザクション管理は、自動生成されたコードによって行われますので、DDBuilder利用者は、特に意識する必要はありません。


永続化コンテキストのライフサイクル、トランザクション管理の流れを下図に示します。

トランザクション管理

[目次に戻る]

ドメイン駆動設計の入門者の学習をサポート

DDBuilderは、開発時だけではなく、ドメイン駆動設計の入門者の学習にも役立ちます。

学習のためにドメインモデルを作成しても、ドメインモデルを動かすまでに時間がかかってしまい、ドメイン駆動設計に集中できなくなることがあります。

しかし、DDBuilderを活用すれば、簡単に、動かしてみることができます。

実際にドメインモデルを実装して、具体的に動かしてみれば、ドメインモデルの役割や位置づけが理解しやすくなります。

机上だけで、ドメイン駆動設計やドメインモデルを理解することは、簡単ではありません。

ドメインがアプリケーションの本質であるという考え方や、ドメインモデルを中心にした開発方法、イテレーティブな開発についても理解しやすくなるでしょう。