ソフトウエア開発者はコードの設計と維持だけでなく、その経験を生かしてビジネスサイドに方向を与える能力も持ちつつある。ドメイン駆動設計(DDD)を使うことで、開発者は顧客の振る舞いを見つけビジネスの性質を変化させるための施策を推奨できる。
Jef Claes氏(http://jefclaes.be)はオフショアの開発者によって作られたアプリケーションを自分たちのチームで管理、拡張しやすいようにリファクタリングする仕事をした。新しいリファクタリングされたアプリケーションはオンラインギャンブルという氏の会社のビジネスのニーズにより整合的ではならない。氏はリファクタリングにDDDの方法論を導入した。集約、エンティティ、値オブジェクトを作り、状態の仕組みとしてイベントソーシングを採用した。結果、システムはビジネスの施策と会社の文化に変化を起こすような顧客の振る舞いを見つけたのだった。
InfoQは氏にインタビューし、このプロジェクトについて話を聞いた。
InfoQ: オンラインギャンブルの企業で、あなたのチームは一枚岩のアプリケーションをDDDベースのマイクロサービスにリファクタリングしましたね。このプロジェクトのうまくいった点、失敗した点について教えてください。
Jef Claes: アプリケーションをマイクロサービスにリファクタリングしたというのは少し大げさかもしれません。しかし、私たちが受け継いだ一枚岩のアプリケーションは、爆発寸前まで膨れ上がり構造化されていませんでしたので、意味のある境界を見つけ分割するということにかなりの労力を費やしました。この作業は、名前空間やモジュールというかたちでモデルの境界を作ることと変わりません。最終的にすべてを抽出してそれぞれの物理的な境界に置くことになったのです。
このプロジェクトの最初のコミットは4年ほど前に行われました。ライセンスを持つベルギーのカジノ事業者が、ベルギーでオンラインギャンブルが合法になるという知らせを受け取った数ヶ月後です。
ウェブサイトの最初のバージョンはほとんどがオフショアチームによって作られました。彼らはドメインについて全く知りませんでした。しかし、私は彼らを責めるつもりはありません。仕様だけ渡されて、ドメインの専門家にアクセスできない状態なら意味のあるソフトウエアを作るのは不可能でしょう。リリースの6ヶ月前から本国のエンジニアがこのデスマーチに参画しました。そして、正式なリリース日の前日になんとか出来上がったのです。
サイトが公開されて数日で、問題点が明らかになりました。いくつかのドメインの概念がよく考えられていない、または長い間、不問に付されていたことがわかりました。これによって顧客が不幸になり私たちのお金もたくさん失ったのです。しかし、これは避けられることではなかったのです。ベルギーの規制のおかげて、ほとんどの競合各社が同じように五里霧中状態で、意味のあるモデルが見つかるまで失敗を繰り返していました。海外の市場から学ぶべきだったと思います。ベルギーのプレイヤーがIPブロックでアクセスできない海外のサイトは何らかの方法でうまくやっているのでしょう。顧客も低いパフォーマンスと品質に苦しめられました。ゲーム(ゲーム以外の機能も)遅くなり、完全に停止することもありました。基本的なクエリとインデックスのチューニングで時間を稼ぎ、重要な部分を再設計して、ユーザーが直接使うアプリケーションの外に処理を持っていきました。結合テストがなかったのでストレスのたまる数ヶ月間を過ごしました。その間、私はビジネスを成長させこの厳しい市場で戦いながら、もっとシンプルで性能が良いモデルを探しました。
InfoQ: 新しい設計ではDDDの集約とエンティティを実装することにしましたね。なぜ従来の方法ではなくこの方法を選んだのですか。
Claes: 設計の関心事として集約とエンティティによる分類が必要だったわけではありません。コンポーネントやモジュールというより小さな範囲の設計として採用しました。
コンポーネントの正しさを検証する方法を探す場合、あるメッセージをそのコンポーネントに送信し、どのような返信があるかを調べることで検証できるでしょう。返信はコマンドかもしれませんし、イベントかもしれません。SQLを送信メッセージと見立てることもできます。ディスクから状態を読み取ることですらHTTPのRPCと同様にリクエストとレスポンスのやりとりなのです。
ソフトウエアの振る舞いに信頼を感じるためにはこの方法だけで済む場合もあります。コマンドを送信し副作用を観察するだけです。ドメインを見れば、意図と副作用の間に生息する概念や抽象、モデルが見つかります。ここでDDDの構成要素が効果を発揮するのです。モデルを具体化し、メンタルモデルとコードを互いに近づけておくのにDDDが使えるのです。
既存のコードベースには、管理が必要なある種のライフサイクル(ウォレット、ゲーム、ボーナスなど)がたくさんあります。たくさんの振る舞いがありますが、あらゆる場所に分散していました。この貧血モデルは維持するには高コストでありエラーが起きやすく、結果、触りにくいものになっていたのです。ビジネスが頻繁に変わり、新しいことを試しているとき、このようなモデルは望ましくありません。それで、私たちは変更するたびに、振る舞いを集約に移行し、境界から離れて、可能な限り明確な値オブジェクトを抽出しました。借方への記入でマイナス残高になるかどうかを50箇所も調べてうんざりもしました。
集約や値オブジェクトを使ったモデルの中核になる属性(あるいは不変条件)はシステムの望ましい振る舞いについて話をするのを簡単にしてくれます。
また、多くの外部(決済事業者など)との統合を進ようとすることで、さまざまな角度からモデルを見るようになっています。中核となる属性をモデルにしっかりと強制することで安心して眠れるようになりました。外部の関係者が要求を送ってくることもありますが、私たちは協会で検証をし、いくつかの要求は丁寧に断っています。すり抜けるものもありますが、モデルの正しさは維持され、中核の属性は守られます。
InfoQ: 最初、あなたは集約を大きく作りすぎて、性能の問題が起きたそうですね。性能のために集約を適切な大きさにすることについてあなたの経験を教えてください。
Claes: 大きな集約がよく動く処理にあると書き込みが競合したり、多くのデータを読み取ったり、書き込みが重いときに問題になります。
集約は可能な限り小さく、しかし、小さくしすぎず、不変条件が満たされる限りに小さくしておきます。本当の不変条件を見つけるためには実世界の類推やシナリオを使います。潜在的な不変条件が見つかったら疑い深くなりましょう。しっかりと維持するべき不変条件と集約を意思決定しやすくするデータの間には大きな開きがあります。そのようなデータにはしっかしとした一貫性を求めません。例えば、残高から引き出しすぎるのは避けたいかもしれませんが、誰かがごく稀に保証金の限度を超えてしまうのはしっかりとチェックしないかもしれません。
InfoQ: 集約のスコープを絞り込むために今後活用できそうなガイドラインは見つかりましたか。
Claes: 状態を持つモデルを作る場合、今後、私はORMを避けると思います。もっと正確に言えば、機能な豊富なORMを避けます。グラフを通じて繋げるようにするのは、基本的にはデータベース全体を網羅できますが、トランザクション境界に対する観点が完全に抜けます。もし、ORMを使うのが重要な要件なら、遅延読み込みとやめ、ナビゲーションプロパティが集約の境界を超えないようにします。集約が他の集約への参照を持つなら、単に識別子だけを持つようにした方がよいでしょう。
状態のかわりにイベントを使うなら、イベントの一時的な性質が集約の振る舞いを簡単に推測できるようにしてくれます。集約がイベントストリームによって裏付けられているなら、タイムラインとしてその流れを視覚化できるでしょう。このタイムラインを見れば、大きすぎる、または、長すぎるという潜在的な匂いがわかります。短い間に多くのことが発生していれば流れが大きすぎるようになります。競合が多く発生した場合もそうでしょう。競合を避けることができるモデルは性能がよくなります。長いストリームを最適化するために、スナップショットを見たがるかもしれません。個人的にはこれは避けるべきだと思います。というのは、意図しない複雑さを呼び込むからです。私はこの方法以外でモデルを作る方法を探ります。成熟した現実世界のドメインでは、長い期間生きるプロセスを見つけるのは難しいです。
悲観的な同時実行よりも楽観的な同時実行のほうが好ましいです。ロックが少なく性能がよいだけでなく、問題となる競合が見つかりやすいからです。
InfoQ: 境界付けられたコンテキストを見つけ実装するにはどうしましたか。境界付けられたコンテキストが間違っていて、変更をしなければならないことはありましたか。
Claes: 巨大な一枚岩の資産を受け継ぐのにはある種のやりやすさがあります。何かを変更するときに出会った摩擦を引き金にして意思決定ができるからです。小さなチームで働いているときですら、さまざまな問題を解決するひとつのユビキタス言語に出会うことは不可能です。ひとつの言語にこだわると問題を解決するのに便利なモデルを作るときに選択肢が制限されます。
ゼロからビジネスを育てるためには、しっかりと境界付けられたコンテキストを決めることができるようになる前に、まず、サブドメイン、つまり問題空間を理解する必要がありました。ドメインの専門家たちも彼らのやり方で学習をしていました(物理的な施設のあるカジノとオンラインカジノは全くの別物です。悪魔は細部に宿るのです)ので、私たちもビジネスとITのギャップを可能な限り小さくしようとしました。日々の業務(小さいうちはとても簡単)に参加したり、国際的なトレードショウに一緒に行ったり、展示会に参加したりして、彼らが解決しようとしている問題を学んだのです。
インパクトのある境界付けられたコンテキストを確立する作業に向き合えるのはサブドメインが顕在化したときだけです。境界付けられたコンテキストを切り替えるとき、ドアを通り抜けて、バイアスのかかっていない状態で問題に取り組むための別の部屋に入るような感じがします。その仕事に対する正しいツールが使えるのです。
早すぎたのではなく、境界付けられたコンテキストを定義するのが遅すぎたため苦しみました。遅すぎると拙速に定義しようとして間違った抽象をしてしまいます。これによって、やり直しをしたり、学習したり、なれたりするまで、意図しない複雑さに苦しめられます。長い期間生き残る健康的なシステムを構築したいなら、深い洞察に向けたリファクタリングをする余裕が必要です。チーム全体がドメインになれたら、正しい境界を素早く見つけるのが得意になります。
InfoQ: イベントはあなたのプロジェクトを助けましたか。望ましくない副作用はありましたか。
Claes: 偏見があるかもしれませんが、イベントは私たちにとって一番影響があった変更だと感じています。私たちのコンポーネントの設計、システムのアーキテクチャ、運用の効率さに対して大きな良いインパクトを与えました。
コマンドと副作用の間の結合を除去することで顧客が使うAPIからバックグラウンドのワーカーへ処理を移譲できました。ロイヤルティポイントの取得処理が良い例です。ベットする処理とロイヤルティポイントを取得する処理は従来はひとつのトランザクションに含まれており、その結果、性能が犠牲になっていました。この犠牲はベット(ロイヤルティポイントの取得)の副作用をトランザクション外に持っていくだけで簡単に避けることができたのです。イベントを使うことで、ロイヤルティポイントを計算するを実行し、付与をバッチで行うことができます。これによって運用の作業量が大幅に削減できます。結果整合を保った状態でロイヤルティポイントを付与できるので、メンテナンスも配置もやりやすくなります。顧客が数分単位でしかロイヤルティポイントが付与されないことを気付いているなら、数分の間このコンポーネントを停止しても問題ありません。
イベント(イベントソーシング)を使うことで生まれたもうひとつの利点は、すべての履歴を保持することができるということです。サポート担当の仕事がとても楽になります。システムを使って顧客がやったことをすべて見ることができるのです。データ分析やデータサイエンスによって意思決定を支援するときにも大いに活用できます。
私が低く見積もっていたのはイベントの読み取り(プロジェクションのやり直し)でデータベースにどのくらい負荷がかかるかです。特別なデータベースにイベントを保存していたのではありません。イベントが始まると、かなりの領域を確保し、プロジェクションのやり直しにはディスクからメモリに読み込むためにたくさんのページが生まれます。これによってメモリに乗っているべきページが押し出され、性能が劣化します。これは対処できないような問題ではありませんが、気にしておくべき問題です。時間稼ぎとしてはより性能のよいマシンを買ってスケールアップするか、キャッシュレイヤを導入することです。データに処理が集中するコンポーネント専用のデータベースサーバを持つ、という方式を探りたくなるかもしれません。
InfoQ: イベントの分析からどのようなことを学びましたか。
Claes: イベントストリームを分類したり視覚化したりすることで、豊かなストーリーを教えてくれる視点を手に入れました。プレイヤーの振る舞いを視覚化することで、プレイヤーをタイプ別に分類し、それぞれのタイプ別に扱うことができます。 何か目立つプレイヤーを分析するために、イベントストリームを視覚化は曲がりくねった振る舞いを見つけるのに簡単に使えるツールなのです。
私たちは生のイベントストリームをそのまま分析せずに一時データを正規化されたモデルにプロジェクションするよういしています。こうすることで一般的なツールや製品を使って予測分析やプレイヤーの分類ができるようになります。
InfoQ: 開発者(同じようなことをしている開発者)がアプリケーションのリファクタリングという点で陥りがちな罠はなんでしょうか。
Claes: イベントソーシングは強力なパターンであり、意味があるやり方でアプリケーションの状態に対するすべての変更を捉えることができます。これらのイベントはシステムを統合するときに使えます。状態を持つモデルをイベントソーシングにリファクタリングするのは、大きなパラダイムシフトです。実行するに値することであるということを明確にして、チームが作業に取りかかれるようにする必要があります。モデルの重要な部分を細かく砕いてスクラッチから書き直し、移行を明確なアクションにしたいと考えることもあるでしょう。
完全なパラダイムシフトには投資したくない、あるいは、できない場合は、妥協案があります。状態を保存したあとに、特定の状態に導くためのイベントを保存するというハイブリッドなアプローチがあります。これを実現するには同じトランザクション内でプロジェクションを実行するか、集約から状態オブジェクトとイベントを取り出すかします。これにはイベントが状態にならないというバグを生み出すリスクがあります。
この妥協によって、とにかく着手すれば大きな変化を避けることができます。ゆっくりやることができるなら、技術的な必要性を育て理解する時間が取れます。プロジェクションとメッセージングの利点を活用したいなら、プルベースのアプローチが使えるかどうか検討してください。プッシュベースのアプローチよりもシンプルな場合があります。
イベント駆動のアプローチに向けてリファクタリングすることに抵抗があるかもしれません。レガシーな資産と向き合うことのひとつの役得として、ある程度の余裕が持てるということがありますが、結果、イベントのテーブルがひとつ出来上がるだけ、ということになりかねません。
そうなったらドメインエキスパートを巻き込んでください。テーブルについて話すのをやめ、ドメインについて話し合ってください。何が起きたら(イベント)どうなる(コマンド)べきなのかを質問してください。イベントストーミングの中を覗き込んで一番影響力のあるドメインエキスパートと話すようにしましょう。私の経験では、ドメインエキスパートが至るところにいる状態になるまでそれほど長い時間はかかりません。プロジェクションやプロセスマネージャ、チェックポイント、冪等性というような難解な言葉は使わないようにしてください。
また、ダッシュボードやレポートにイベントのデータを可能な限り出してしまうのもひとつの手です。イベントの専門的な部分がコードベース上の第一級市民になることに悩む必要はありません。ドメインを理解し捉えることに時間を使いましょう。ドメイン駆動設計とイベントは手を取り合って進んでいきます。もし意味を誤解していたら、システムは正しくない規約でまとめ上げられたものになってしまうでしょう。先へ進む効率も殺されるばかりでなく、倫理的にも死んでしまいます。
InfoQ: DDDのイベントに移行することによるビジネスへのインパクトは定量化できますか。実装はビジネスの利益向上や文化の変化につながりましたか。
Claes: 数字を出すのはとても難しいです。平行世界を覗き込んだらわかるとよいのですが。絶対値を公開してよいポジションではありませんが、イベントを第一級市民にしたことで相対的にかなり成長しています。移行しなくても同じ成果をあげられたかもしれませんが、進み具合や学習は遅くなったことでしょう。
文化の変化として一番大きかったのは実験への意欲です。イベントによってシステムは有機的に成長するようになり、別々の部分ごとに動くようになりました。生きている、計画されていないシステムなら、失敗したときに捨て去る実験をすることは恥ずかしいことではありません。実験の影響を分析するために必要なデータは顧客の振る舞いのログを完全に手に入れなければ、活用できません。
Jef Clae氏について
Jef Claes氏はプロのコードスリンガー(codeslinger)であり、ドメイン言語学者、データシャッフラー。これまで公衆安全と銀行業界で働き、直近の2年間はオンラインカジノ事業者向けのソフトウェアを提供するベルギーの小さな企業でオンラインギャンブルのドメインで活動している。DDD(BE)、CQRS、C#、FP、F#といったバズワードに馴染んでいる。
Rate this Article
- Editor Review
- Chief Editor Action