SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

IDDD本から理解するドメイン駆動設計

実践DDD本 第4章「アーキテクチャ」 ~レイヤからヘキサゴナルへ~

IDDD本から理解するドメイン駆動設計 第4回


  • X ポスト
  • このエントリーをはてなブックマークに追加

ヘキサゴナルでのサービス指向のアプローチ

 SaaSOvationチームは、レガシーなコラボレーションツールからの移行用サービスを作成するにあたり、ESB Muleという製品を導入しました。ESB(エンタープライズサービスバス)とは、コードを書かずにメッセージング基盤を提供するミドルウェア/インフラ製品で、SOAの機能も提供されています。

SOA(Service Oriented Architecture:サービス指向アーキテクチャ)の導入

 SOA(Service Oriented Architecture)とはサービス指向アーキテクチャのことで、ソフトウェアをサービスとして連携させ、システム全体を構築していきます。ヘキサゴナルアーキテクチャのサービス設計において、SOAの設計指針を意識することは有益です。

SOAの設計原則とヘキサゴナルアーキテクチャ
SOAの設計原則とヘキサゴナルアーキテクチャ

 サービス設計の指針としては、ビジネス戦略を示す「ビジネスサービス」とREST/SOAP/メッセージ型といった「技術的サービス」の両面を意識します。さらにDDDでは「ユビキタス言語」や「境界づけられたコンテキスト」が不自然に分断されていないかをチェックすることで、より良いサービスを公開できます。

ヘキサゴナルで使用されるREST

 DDDでWebサービスを構築する時によく利用される技術がRESTです。SaaSOvationチームは、モバイル対応、認証管理、BIレポートの対応において、RESTを採用しました。

RESTfulなシステムとは

 REST(Representational State Transfer)は、Roy Fielding氏が書いた博士論文にて登場したアーキテクチャスタイルです。このRESTに従ったサービスを「RESTful」と呼びます。RESTという言葉は普及していますが、定義が曖昧なことも多いので簡単に特徴を整理します。

RESTの特徴とDDD
RESTの特徴とDDD

(1)リソースを一意なURIで識別可能

 RESTでは「URI(Uniform Resource Identifier)」 による一意なアドレスを持ち、提供する「リソース」を識別できます。例えば、提供するサービスの顧客/プロダクト/検索結果といった情報リソースを一意なURIにて識別できることを表します

(2)ステートレスな通信

 RESTではクライアントとサーバー間の通信はステートレスに行われます。RESTfulなHTTPサーバーは前回のリクエストの状態などを記憶しないため、各HTTPメッセージには、そのリクエストを処理するために必要な情報が全て含まれています。

(3)GET/POST等の命令指針に基づいたリソース操作

 RESTでは「GET(取得)」「POST(登録)」「PUT(登録または更新)」「DELETE(削除)」等のHTTPメソッドを用いてリソースを操作できます。これらの命令には標準的な振る舞いが決まっています。例えば、GETはデータを読み込み、キャッシュ可能な振る舞いを提供します。厳密には、変更を伴わない「安全」な操作で、同じ処理を何回呼び出しても結果が変わらず問題も発生しない「冪等(べきとう)」な操作と言われます。

 これらの体系化された命令を使うことにより、URLの中に/GetFooや/DeleteBarといった動詞を含める必要が無くなり、名詞主体のわかりやすいURLを構成できます。

(4)関連リソースへのナビゲーションを扱う「ハイパーメディア」

 RESTでは、ハイパーメディアを用いて、レスポンスの中に他のリソース情報を埋め込むことができます。Fielding氏の論文では、このことをHATEOAS(Hypermdia as the Engine of Application State)と呼んでいます。

DDDでRESTの使用

 RESTは理解が容易で、疎結合で利用がしやすい仕組みのためスケーラブルなサービスの提供にも適しています。そのため、DDDのヘキサゴナルアーキテクチャとの相性も良いとされています。

 なお、RESTで公開するインターフェイスは、コアドメインをそのまま公開するのではなく、公開用の独自モデルを設計するか、ical(一般的なカレンダー形式)のような汎用的なメディアタイプを使用することが推奨されています。

コマンドクエリ責務分離(CQRS:Command Query Responsibility Segregation)

 SaaSOvationチームは、ユーザー別に複雑化してきたダッシュボード画面の通知情報を管理するため、CQRSアーキテクチャーを採用しました。CQRS(Command Query Responsibility Segregation)とは、その名前の通りコマンドとクエリを分離することで、更新と取得それぞれに特化したモデルと処理を実装します。

コマンドクエリ責務分離とは

 CQRSパターンの元になっている原則は、コマンドとクエリを分解するアーキテクチャパターンであるCQS(Command Query Separation)です。これはBertrand Meyer氏が書籍「Object-Oriented Software Construction」にて提唱しました。実装レベルに要約すると以下の2つに分けられます。

  1. コマンド(ライト):オブジェクトの状態を変更するメソッドは値を戻してはいけない。戻り値の型はVoidである。
  2. クエリ(リード):メソッドが型や値を戻す場合、オブジェクトの状態を変更してはいけない。

 従来のプログラミングでは、更新と取得を同じメソッドを記述していたかもしれませんが、CQRSでは「更新メソッド」と「結果取得メソッド」として明確に2つに分離します。

CQRSパターンとDDDを組み合わせた場合の流れ
CQRSパターンとDDDを組み合わせた場合の流れ

DDDにおけるCQRSでの流れ

 それでは図に従い、CQRSの流れを見ていきましょう。

(1)コマンドの処理(コマンドプロセッサ/コマンドハンドラ)

 サーバー側にて更新処理を開始します。この処理部分は、コマンドプロセッサ(コマンドハンドラ)と呼ばれます。このコマンドプロセッサの構築方法については、次の3方式があります。

 同期処理の場合は「(a)分類方式(アプリケーションサービスに複数のコマンドメソッドを追加)」か「(b)専用方式(単一メソッドを持つ単一クラスを作成)」を選択します。前者のメリットは開発が容易で、後者のメリットはコマンドごとの責務が明確になります。非同期処理の場合は「(c)メッセージング方式」を用います。この方式では「専用方式」のコマンドクラスを非同期のメッセージとして送信します。複雑になるためスケーラビリティが必要な場合のみ選択するようにします。

 どの方式にせよ、コマンドプロセッサは、コマンドモデルである集約のインスタンスを作成/取得し、更新用のメソッドを実行します(集約については別途10章で紹介します)。

(2)コマンドモデル(ライトモデル)

 通常、集約がコマンドモデルとなります。コマンドモデルの更新系メソッドが呼び出される場合、最後に「ドメインイベント」が発行されます。例えば「会員が登録された時」というようなイベントを発行します。

(3)コマンドモデル用データストア

 コマンドモデルの更新結果が保存されます。データの更新内容がレポジトリ経由で格納されます。

(4)コマンド処理を実行(イベントのサブスクライバ)

 サブスクライバ(購読者)が発行されたドメインイベントを受信します。そして、受け取ったイベントの内容に従ってクエリモデル(クエリ用のデータ)の更新を行います。

(5)クエリモデル用データストア

 クエリモデル用データストアには、描画用のデータが格納されています。データの格納場所の決まりはありませんが、データベースのテーブルが一般的です。高速化のため事前にテーブルをジョインして非正規化した「マテリアライズドビュー」を使用する場合もあります。マテリアライズドビューはデータベースの標準機能を使用する場合もありますし、プログラムで事前にデータを構築する場合もあります。また、性能の観点から複数台のレプリカを持つ場合もあります。

(6)クエリモデル(リードモデル)

 クエリモデルは、画面表示や印刷用のための非正規化されたデータモデルです。

(7)クエリ処理(クエリプロセッサ)

 データベースの結果セットをそのままかJSON/XMLで戻したり、DTO(データトランスファーオブジェクト)という描画モデルに詰め替えたりします。これらの方式については、プロジェクトにおいて最適な方法を選択します。

 上記の流れでCQRSによるデータの更新と描画が可能となります。

CQRSでの同期/非同期の採用指針

 CQRSで同期的に処理するか、非同期に処理するかは機能要件次第です。同じDB内に「コマンドモデル用データストア」と「クエリモデル用データストア」を用意し、同一のトランザクションを用いれば同期的に処理することができます。同一トランザクションの場合、一貫性が保たれるメリットがある反面、処理時間がかかるデメリットがあります。これに対して非同期処理を用いる場合は、いったんレスポンスを返せるため高速になりますが、一貫性が保たれないため「結果整合性」を保つ必要があります。

CQRSと結果整合性

 「結果整合性」とは「結果として一貫性が保たれることが保証されていれば問題ない」という考え方です。標準的なRDBMSアプリケーションでは、ひとつの処理でビジネスルールを正しく保つ「トランザクション整合性」が一般的(例:銀行口座の入出金では必須)です。しかし、DDDではドメインエキスパートの観点から、最終的に一貫性が保たれれば多少のタイムラグが合っても問題がないと判断される場合、「結果整合性」を用いることで複雑性を排除することができます。

 非同期のCRQSでは結果整合性は保たれますが、クエリモデルの更新までに多少のタイムラグが発生します。これを補完するためアプリケーション側で、更新依頼したユーザーに対して入力データを使った疑似的な画面更新を行ったり、データの最終更新時刻を表示したりといったUI対策を実施する場合があります。さらにリアルタイムな更新が必要な場合は、サーバー側からプッシュする技術(Commet/WebSocket/Server Sent Events等)の採用を検討する場合もあります。

 以上、CQRSをDDDと組み合わせる場合の概要について紹介しました。

次のページ
イベント駆動アーキテクチャ

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
IDDD本から理解するドメイン駆動設計連載記事一覧

もっと読む

この記事の著者

WINGSプロジェクト 青木 淳夫(アオキ アツオ)

WINGSプロジェクトについて>有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表 山田祥寛)。主にWeb開発分野の書籍/記事執筆、翻訳、講演等を幅広く手がける。2018年11月時点での登録メンバは55名で、現在も執筆メンバを募集中。興味のある方は、どしどし応募頂きたい。著書記事多数。 RSS Twitter: @yyamada(公式)、@yyamada/wings(メンバーリスト) Facebook

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

山田 祥寛(ヤマダ ヨシヒロ)

静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for Visual Studio and Development Technologies。執筆コミュニティ「WINGSプロジェクト」代表。主な著書に「独習シリーズ(Java・C#・Python・PHP・Ruby・JSP&サーブレットなど)」「速習シリーズ(ASP.NET Core・Vue.js・React・TypeScript・ECMAScript、Laravelなど)」「改訂3版JavaScript本格入門」「これからはじめるReact実践入門」「はじめてのAndroidアプリ開発 Kotlin編 」他、著書多数

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/9922 2018/11/13 12:50

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング