面白駆動人生

やっほー

【読書メモ】実践ドメイン駆動設計 ~第7章~ 「サービス」

前回 【読書メモ】実践ドメイン駆動設計 ~第6章~ 「値オブジェクト」

サービスとは

第7章で扱う「サービス」とは「ドメインサービス」のことを指す。

ドメインに特化したタスクを処理するステートレスな操作。集約や値オブジェクトの外に実装したほうがいいロジックなど。

  • アプリケーションサービス

ドメインモデルのクライアント。トランザクションやセキュリティなどの調整的な処理など。

いつドメインサービスを使うのか?

避けるべきこと

ただし、なんでもかんでもサービスとしてモデリングしてはいけない。
サービスを使いすぎると、ドメインモデル貧血症に陥るため。

  • ミニレイヤの実装

ドメインサービス層」というミニレイヤを作ることはアンチパターン
レイヤが肥大化する傾向にあるため。

  • アプリケーションサービス層での実装

アプリケーションサービスは、ドメイン外部の関心事を実装する場所。 ドメイン内部のビジネスロジックが、利用側(アプリケーションサービス)に流出させないため。

参考

「実践ドメイン駆動設計」から学ぶDDDの実装入門 (WINGSプロジェクト 青木淳夫 著)

以上

【読書メモ】実践ドメイン駆動設計 ~第6章~ 「値オブジェクト」

前回 【読書メモ】実践ドメイン駆動設計 ~第5章~ 「エンティティ」

What is 「値オブジェクト」

計測・定量化・説明を表現する。 例えば、数字・文字列・日付・時刻、氏名、電話番号など。

値オブジェクトのモデリング

以下の特徴に合致する際、値オブジェクトとしてモデリングする。

  • 何かを計測、定量化、説明する。(計測/定量化/説明)

例) 年齢: 誕生からどれだけ生きてきたのかを計測し、定量化した値。

  • 状態が不変である。(不変性)

  • 組み合わせることで、概念的な統一体を形成する。(概念的な統一体)

例) お金:「額」と「通貨」という属性が組み合わさったもの。
「100」と「ドル」という個別では意味を持たないものが、「100ドル」となり完結した意味を持つ。

  • 計測値、説明が変わった時、全体を完全に置き換えられる。(交換可能性)

例) 通貨の額を変えたい時、新しい「お金」オブジェクトを生成する。

  • 値が等しいか、他のインスタンスと比較できる。(等価性)

値オブジェクトが持つ各属性全ての値が同じか、を判定できること。

  • 振る舞いが、他の概念に対し副作用を及ぼさない。(副作用のない振る舞い)

どのような状態で何度呼び出しても、オブジェクトの状態が変わらない操作。

コンテキスト間の結合をミニマムにする

値オブジェクトを利用することで、下流のコンテキスト側に存在するモデルが管理するプロパティを最小限にすることができる。その結果、複数の境界づけられたコンテキスト間の結合を緩やかにすることができる。

例)
コラボレーションコンテキストの中に、Moderatorが値オブジェクトとして存在。
コラボレーションコンテキストが腐敗防止層から認証・アクセスコンテキストに問い合わせる。
ユーザがモデレータロールを付与されている時、Moderatorオブジェクトを生成する。 f:id:yktm31:20210703173608p:plain

参考

「実践ドメイン駆動設計」から学ぶDDDの実装入門 (WINGSプロジェクト 青木淳夫 著)

以上

【読書メモ】実践ドメイン駆動設計 ~第5章~ 「エンティティ」

前回 【読書メモ】実践ドメイン駆動設計 ~第4章~ 「アーキテクチャ」

What is 「エンティティ」

DDDにおいて「エンティティ」は一意なものを示す概念。長期に渡り、変化する可能性がある。
一意ではなく、変化しないものは「値オブジェクト」と呼ばれる。

ドメインモデル貧血症

DBのプロパティをpublicなプロパティーとして保持し、そのgetter/setterのみから構成されるモデル。 オブジェクトとしての「振る舞い」がほとんどない状態で、「単なる手続き型設計」になっている点が、アンチパターンとされる。

エンティティ設計

一意な識別子

  • ユーザが入力する。一意性はアプリケーション側が担保する。
  • アプリケーションが内部で生成。
  • データベースなどに生成を任せる。
  • 別の境界づけられたコンテキストが生成した識別子を、ユーザが入力する。

生成タイミング

  • 早期生成: エンティティを永続化する前に、生成と割り当てを行う。

  • 遅延生成: エンティティを永続化する時に、生成と割り当てを行う。

代理識別子

エンティティが保持する識別子とは別に、オブジェクトの識別子を別の方式で管理したい時に利用。例えば、ORMツールのHibernateは、データベースに依存した型で識別子を持つ必要がある。

レイヤスーパータイプを利用することで解決する。

エンティティの生成方法

  • コンストラクタ
  • ファクトリ
  • コンストラクタから「自己カプセル化」での呼び出し

自己カプセル化・・・ クラス内部からフィールドにアクセスする際もアクセサメソッドを利用する方法。

バリデーション

  • 自己カプセル化によって、セッター呼び出し時にアサーション。
  • 契約による設計の、事前条件としてアサーションを記述。
    • 事前条件: 呼び出し側が開始時に保証する条件
    • 事後条件: 呼び出される側がメソッドの終了時に保証する条件
    • 不変条件: データが常に満たすべき条件

参考

「実践ドメイン駆動設計」から学ぶDDDの実装入門 (WINGSプロジェクト 青木淳夫 著)

以上

【読書メモ】実践ドメイン駆動設計 ~第4章~ 「アーキテクチャ」

1. What is Architecture

2. アーキテクチャの種類

Layers

特徴

クライアント -> サーバ -> DB のように、層に分けるパターン。名前空間の分割、各レイヤの依存関係、責務の明確化。

問題点

ドメイン層がインフラ層に依存するため、ドメインの実装が、インフラ層の実装に依存してしまう。

解決策

DIP(Dependency Inversion Principle)を用いる。
インフラ層の実装をドメイン層の抽象に依存させ、ドメイン層を独立させる。

Hexagonal or Ports and Adapters

特徴

システムを内部と外部の領域に分け、外部からの入力をアダプタが変換し、内部のAPIの形式に合わせる。
テスト用のアダプターが容易に作れる点が利点。 クライアント、永続化の種類が未定のうちから、暫定的なアダプターを作ればドメインの実装を進められる。

Service-Oriented

SOAの8つの原則

原則 説明
Standardized Service Contracts サービスは契約(インターフェース)によって説明される
Loose Coupling of Service 疎結合であり、外部への依存は最低限にする
Service Abstraction インターフェース(契約)のみ公開し、内部の詳細は見せない
Service Re-Usability 実装は一つで、他サービスから再利用可能である
Service Autonomy サービスが独立して存在し、一貫性と信頼性を保っている
Service Statelessness サービスを利用する側が状態を管理する
Service Discoverability サービスをメタデータで記述し、発見可能にする
Service Composability サービスはより大きなサービスの一部に組み込み可能である

Representational State Transfer (REST)

RESTの定義

RESTとは、Fieldingが博士論文として提唱してアーキテクチャスタイル。
https://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm

RESTに従っていることをRESTfulと呼ぶ。

  • point1 リソースはURI(Uniform Resource Identifer)により、一意に識別される。
  • point2 一つのHTTPリクエストに、サーバ側での処理に必用な情報を全て含める。サーバは個別のリクエストに依存しない。
  • point3 全てのオブジェクトが同じインターフェースを持ち、決められたメソッドで操作される。(特に重要なメソッドとして、GET, PUT, POST, DELETE)
  • point4 レスポンスに他のリソースの情報を埋め込める。Hypermedia as the Engine of Application State(HATEOAS)

RESTとDDD

RESTとDDDをどう組み合わせるか? ドメインモデルをRESTful HTTPで直接公開するのは悪手。ドメインモデルに手を加えるたびに、システムのインターフェースにも手を加える必用が出てくる。

システムのインターフェースレイヤを、境界づけられたコンテキストとして分離する方法が一般的に使われる。

Command-Query Responsibility Segregation (CQRS)

解決したい課題

  • 読み取りと書き込みのデータの形は一死するとは限らない。
  • 同じデータセットに、並列で操作が実行されると競合が発生する可能性がある。
  • データストアとデータアクセス層への負荷、クエリの複雑さによって、パフォーマンスに悪影響が出る可能性がある。
  • 読み取りと書き込みが同じデータに行われるため、アクセス許可の管理が複雑になる。

参考: CQRSパターン - Azure Architecture Center

What is CQRS?

「更新」を行う「コマンド」と、データを「参照」する「クエリ」に、異なるモデルを使うアーキテクチャ

アクションを実行する「コマンド」と、データを呼び出し元に戻す「クエリ」をメソッドとして分離するという、 CQS(Command Query Separation)の考えを、アーキテクチャに持ち込んだもの。

f:id:yktm31:20210606192406p:plain

CQRSのメリット - スケーリング - 読み取り、書き込みそれぞれを個別にスケーリングできる。

  • パフォーマンス

    • 読み取り側はクエリ用に最適化できる。
  • セキュリティ

    • 適切なエンティティのみが、書き込みできる状態を維持しやすい。
  • 関心の分離

    • シンプルな読み取りの実装と、ビジネスロジックが含まれる書き込みの実装を分離でき、保守性・柔軟性を向上できる。

参考: CQRSパターン 解決策 - Azure Architecture Center

CQRSのデメリット - 複雑さ - イベントソーシングが含まれる場合、特に設計が複雑になりやすい。

  • メッセージング

    • コマンド発行・イベント更新にメッセージングが使われることがある。
    • その際、メッセージのエラー・重複処理が必要になる。
  • 一貫性

    • 読み取りデータが古くなる可能性がある。
    • 結果整合性を採用することがある。
      • サーバ側から更新のメッセージをUIに送り、リアルタイム更新する(WebSocket等)
      • UIに、最終更新日時を表示する。
      • UI上で擬似的に表示を更新する。

参考: CQRSパターン 実装に関する問題と注意事項 - Azure Architecture Center

Event-Driven Architecture (EDA)

  • イベントを受け取り、受け取ったイベントに応じて反応するアーキテクチャ
  • システム間の結合を切り離せる。

パイプ & フィルター方式

cat phone_numbers.txt | grep 303 | wc -l

上記のLinuxコマンドのパイプ・フィルターのように、イベントを送信->イベント受信->処理実行->イベント送信という流れを、システム間のやりとりとして表現する方式。

業務プロセスにおけるアクティビティの発生をモデリングしたものになる。

サーガ(長期プロセス)方式

パイプ&フィルターを拡張したもの。複数の処理を分散して処理したい場合に利用。

長期プロセスの設計方法

  • 実行者コンポーネントに、各タスクをトレースさせる。

  • パートナーアクティビティ という方法。Pat Hellandが推奨。

  • イベントを運メッセージに、プロセスの状態を保持させ、プロセスはステートレスとなるように設計する。メッセージを受け取るハンドラが、受け取ったイベントに情報を付与して、次のメッセージとして送出する。

イベントソーシング方式

  • 履歴管理に利用可能。
  • Gitのように、過去の状態を復元するような概念に類似
  • 発生したすべてのイベントを、イベントストアに格納。

データファブリック / グリッドベース分散コンピューティング

  • Pivotal社のGemFireや、Oracle社のCoherenceといった製品で使われる概念。

参考

「実践ドメイン駆動設計」から学ぶDDDの実装入門 (WINGSプロジェクト 青木淳夫 著)

以上

【読書メモ】実践ドメイン駆動設計 ~第3章~ 「コンテキストマップ」

復習

  • 問題空間: 解決すべきビジネス上の課題 (問題空間 = コアドメイン + サブドメイン)

  • 解決空間: ソフトウェアをどう実装して課題を解決するか (解決空間 = 境界づけられたコンテキスト)

目次

1. Why コンテキストマップ?

  • マップを書く主たる理由は、解決空間の全体像を見れるようにすること。

  • コンテキストマップは、現状を姿を捉えるためのもの。理想・期待する姿を書くわけではない。

  • シンプルかつアジャイルに進める。

  • コンテキストマップは組織の動きを示す。

2. コンテキストマップと組織パターン/結合パターン

組織パターン

パートナーシップ

成功/失敗の運命をともにする関係がある。インターフェース部分も共に検討して前に進める。

別々の道

協力体制なく、統合しない。

顧客/供給者の開発

上流・下流の関係。

順応者

上流・下流の関係だが、上流側が下流の要求に応えようとしない。

結合パターン

共有カーネル

明示的な境界を定め、共有部分を合意する。ソースコードレベルで共有。

公開ホストサービス (Open Host Service)

サブシステムへのアクセスのためのプロトコルを公開。RESTが主流。

公表された言語 (Published Language)

明確にドキュメント化された共有言語を利用。JSONXMLを利用。

腐敗防止層 (Anti-Corruption Layer)

パートナーシップ、顧客/供給者といった関係を築けなかった際に、上流システムの機能を、独自のドメインモデルで表現するためのレイヤ。

巨大な泥団子

大規模でモデルも混在している。他のコンテキストに影響をださないように注意。

参考

「実践ドメイン駆動設計」から学ぶDDDの実装入門 (WINGSプロジェクト 青木淳夫 著) 以上

【読書メモ】実践ドメイン駆動設計 ~第2章~ 「ドメイン、サブドメイン、境界づけられたコンテキスト」

1. ドメインサブドメイン・境界づけられたコンテキストについて

2. 問題空間と解決空間

  • 問題空間 ・・・解決すべきビジネス上の課題

問題空間 = コアドメイン + サブドメイン

  • 解決空間 ・・・ソフトウェアをどう実装して課題を解決するか

解決空間 = 境界づけられたコンテキスト

それぞれのサブドメインに、境界づけられたコンテキストが、1対1で対応するのが望ましいゴール。

3. 境界づけられたコンテキストの意味

  • 境界づけられたコンテキストとは、ドメインモデルがどこに属すのかを表すもの。
  • コンテキストによって、モデルの概念・操作・プロパティは変わってくるので、明示的に境界を引く。
  • 境界づけられたコンテキストにより、ユビキタス言語・ドメインモデルをカプセル化することが主目的。

境界づけられたコンテキストとは異なるもの

のような技術的な境界。

  • 開発要因へのタスク割り当て
  • 開発要因の管理のしやすさ

というような偽の境界。

参考

「実践ドメイン駆動設計」から学ぶDDDの実装入門 (WINGSプロジェクト 青木淳夫 著)

以上

【読書メモ】実践ドメイン駆動設計 ~第1章~「DDDへの誘い」

1. Why DDD?

  • ドメインエキスパートと開発者を一つのまとまったチームにする。
  • チーム全体が理解できる共通言語に基づいて開発を進められる。
  • 設計がコードであり、コードが設計でもある。
  • 戦略設計と戦術的設計の両面で、健全なソフトウェア開発を支える。

目指すは、事業価値をもたらすソフトウェア開発

事業価値をもたらさない、ソフトウェア開発とは?

  • ドメインエキスパートと開発者との断絶
  • ドメインエキスパート同士の意見の相違と、矛盾したソフトウェアモデル
  • 技術解をベースに進め、事業・業務が間違った方向に向かう

2. どんな時にDDDを使うのか?

  • 事業における、もっとも重要な領域。
  • 置き換えが簡単な領域ではなく、複雑で投資の見返りが一番大きな部分。 ・・・コアドメイン
  • 重要な支援的な領域  ・・・サブドメイン

3. どのようにDDDを使うのか?

ユビキタス言語を作ることが第一歩。

ユビキタス言語 == ドメインエキスパート・ソフトウェア開発者を含めたチームで作り上げる共通言語

どのようにユビキタス言語を作っていくか。

  1. ドメインについて図示し、名前とアクションをつける。
  2. 定義をまとめた用語集、もしくは概念に関するスケッチなどのドキュメントを作る。
  3. 作成した用語・フレーズをレビューしてもらう。

point1. ユビキタス言語・コードは変化するもの。

  • ドキュメントをつくって終わりではない。
  • コード内のモデル・チーム内での会話が、最もユビキタス言語の現状を表す。

最初に作ったドキュメントを常に最新の状態に更新するのは非現実的。 いつでも捨てられるようにと考える。    

point2. ユビキタス != ユニバーサル

ユビキタスは、業界全体・世界中で、という意味ではない。 境界づけられたコンテキストごとに、ユビキタス言語が存在する。

4. 導入する際の課題は?

5. アジャイルとDDD

DDDはアジャイルを想定している。 テストファーストでモデルの検討をする。

  • チームでモデルを検討
  • モデルに対するテストコードを実装
  • ドメインモデルを実装
  • リファクタ
  • ユビキタス言語の認識が正しいか確認する。