こちらはDMM.com Advent Calendar 2018 13日目の記事です。
DMM GAMES のプラットフォーム開発に携わっているエンジニアのwkubotaです。
ゲームプラットフォームならではの要件にたいして、最近巷で話題のDDDとClean Architectureを武器に立ち向かった話をつらつらと書いてみようと思います。
経緯
ゲームプラットフォームでは普段みなさんがゲームをするための環境(本稿では本番環境と表現します。)とゲーム開発会社さんが、開発中のゲームを一般ユーザーには公開せずに動作させるための環境(本稿ではサンドボックス環境と表現します。)が存在します。
それぞれの環境をざっくりまとめると以下のようになります。
環境 | 説明 |
---|---|
本番環境 | 一般ユーザーに公開している。課金を含む全ての機能が利用可能 |
サンドボックス環境 | ゲーム開発会社向けで一般公開はしていない。ユーザー管理など機能は部分的あるいは模擬的なもの |
さて、そのようなそれぞれの環境に向けたAPIを提供する際のお話をしようと思います。
DDD + Clean Architectureで解決したかった問題
既存のアプローチでは、本番環境向けとサンドボックス向けそれぞれ対応する場合、様々な問題が発生していました。
それは以下のようなものです。
1. 本番・サンドボックスを別ソースとして管理
だいたいこの方針です。
ただ、2系統を維持していくのは保守運用コストとしては無視できないものですし、差分の管理は時間を追うに従って困難になります。
2. 本番・サンドボックスは同一ソースで内部で必要に応じて条件分岐
経験のある開発者なら、既に嫌な予感を覚えているかもしれませんね・・・。
if (env->sandbox) {
// サンドボックス向け処理
} else {
// 本番向け処理
}
この方針がもたらすものはIf文の嵐です。実装の各所に上記のような条件文が入り見通しが悪くなります。1
DDD + Clean Architectureを用いることでどのように問題に立ち向かったのか?
Clean Architecture とは Robert C. Martin の提唱したDDDを念頭においたアーキテクチャです。
The Clean Architecture
それ、を我々の要件に当てはめると以下のような進め方が出来るのではないかと考えました。
上図の概念としては、ドメインモデルは本番環境とサンドボックス環境の差異はなく環境毎の実装は外周部に押し出し、依存関係逆転の法則を使ってドメインモデル側では意識しないで済むようにする。
具体的な工夫あれこれ
外部データをそのままドメインモデルに持ち込まない
External Interfacesで受け取ったデータはドメインモデルに到達する前に__必ず__ドメインモデルのオブジェクトに載せ替える
DDDを採用しているところでは常識的なことかもしれませんが、今回の要件として環境毎の差異をドメインモデルに絶対持ち込みたくなかったので必ずオブジェクトを持ち替える形を徹底しました。
環境ごとの機能の有無の違いを吸収するためにNull Object pattern
ドメインモデル内で環境毎のデータモデルの存在確認やふるまいの分岐をしないで済むように Null Object Patternを多用する形ですすめました。
結果どうなったか?
実装としては、ドメインモデルとして守るべき所と環境毎の差分とで綺麗に収まるべきところが明確になり全体的にスッキリまとまりました。
ただ、最終的に完全に同一のドメインモデルを提供する方針を時間の関係でとることができず(ドメインモデルだけライブラリかAPIにするのが本当は綺麗だったはず)、本番環境とサンドボックスそれぞれにほぼ同じ実装をもったドメインモデルが存在する形になっています。
まとめ
DDD と Clean Architecture をビジネス要件が持つ複雑さへの銀の弾丸にするには、自分の力不足で不十分となってしまいましが、
テストコードの書き易さや処理の責任毎の置きどころがハッキリするなど非常にメリットも多い全体のアーキテクチャとすることが出来ました。
また、今後、保守運用していくなかでより良くするのに手を付けやすい状態にしておけたのは本件における成果だと考えています。
自分たちが選択したアプローチが皆様が普段抱えている問題の少しでも助けになったら嬉しいです。
-
ぶっちゃけ、流石にほとんどこんなことにはしていません。別ソースにするのが多い ↩