Wallet Station管理画面開発でのドメイン駆動設計を振り返ってみる

おでんチーム(管理画面リメイクチーム)の山根正大です!

現在バックエンドエンジニアとして、Wallet Station管理画面のリメイクを担当しています。

山根が所属するチームでは、ドメイン駆動設計(Domain-Driven Design、以下DDD)を用いた開発を進めています。

今回の記事では、約1年間DDDをやってみた経験に基づき、以下の内容で振り返りを実施してみます。ドメイン駆動設計を導入するか迷っている開発チームの参考になれば幸いです!

ドメイン駆動設計(DDD)って何?

DDDは「ドメインの知識に焦点をあてた開発手法」です。

ここでいう「ドメイン」は「システムが利用される業務領域」を指します。Wallet Stationなら「決済」領域がドメインに当たりますね。

DDDでは、ドメインの業務知識に詳しい人(=ドメインエキスパート)とエンジニアの協力によりドメインの知識を抽象化した「ドメインモデル」が作られます。ドメインモデルをベースに開発を進めることで、システムの機能性向上保守性向上を狙います。

より詳細情報を知りたい方は、以下のブログや書籍が参考になると思います!

little-hands.hatenablog.com

www.shoeisha.co.jp

管理画面のリメイク・DDD導入のきっかけ

従来の管理画面は主に以下のような課題を抱えていました。

  • 品質保証が難しい
    • クラス/メソッド単位での単体テストがほぼない
    • あるのはEnd to Endなテストだけ(URLを叩いて、HttpStatus:200が返ることとDBの更新チェックをいくつかやる...みたいな)
    • End to Endテストだけだと実行時間も長いし、網羅性も担保しづらい
  • よくわからないクラス設計になっている

結果、デプロイ後にしかバグに気づけず、「ここの動作おかしくない?」とプロジェクトマネージャーから指摘が入ることが多かった印象です。そういった時は慌ててすぐ直して再デプロイするしかない状況でした。

これらのことが積み重なった結果、保守性向上のため、管理画面のアーキテクチャを刷新することになりました。

その際に保守性を上げられる設計手法としてDDDが採用された形になります。

管理画面リメイクチームにおける、DDD開発のポイント三選

以下では、DDDにおいて管理画面チームが実践しているポイントを3つ紹介します。

ドメインモデリング会の実施

「機能追加するぞ!」となった場合まずはオンライン会議(1時間×3回程度)を実施します。

作りたい機能についての共通認識を作ることと、その認識をドメインモデル図としてアウトプットすることが目的です。

時間をかければチャットだけでも共通認識は作れるのですが、スピード感を持って開発に入りたいため、オンライン会議という形式を取っています。

会議では様々な角度からの意見が欲しいため、出席者は以下のように多岐に渡ります。(大体10人ぐらい)

  • ドメインエキスパートとして
    • プロダクトマネージャー
    • プロジェクトマネージャー
  • 開発者として
    • デザイナー
    • QAエンジニア
    • フロントエンドエンジニア
    • バックエンドエンジニア

(ココだけの感想ですが、この人数の会議だと結構な数のコメントが同時に飛び交ったり、議論が発散していったりするので、山根がモデリング担当だった時はファシリテーションが結構大変でした。)

会議の最終的なアウトプットは「SUDOモデリング」をベースに行います。「システム関連図」「ユースケース図」「ドメインモデル図」「オブジェクト図」の4つをアウトプットとするモデリングのやり方で、以下ブログで詳しく紹介されています。

little-hands.hatenablog.com

管理画面リメイクチームでは、よく「この機能、誰が使うんだっけ?」という疑問から議論が始まるため、ユースケース図から作り始めることが多いですね。

このドメインモデリングについては一度作って終わりではなく、開発の中でアップデートを繰り返していく形になります。そういった改善活動の中での気づきや反省点等、まだまだたくさん語ることがあるので、詳細は別の記事として出そうかなと思っています!

ドメインモデル実装時のモブプログラミング

これは主に、DDD導入当初にやっていた施策です。

ドメインモデル図が書けたらそれをコードとして表現していく必要があるのですが、

  • そもそもドメインモデルをコードで表現するってどういうこと?
  • クラス作ったはいいけど、テストってどう書くのが良いんだろうか...

など、それぞれ導入初期特有の疑問がありました。

それを解決しながら、「ここはこうやって書くのがイイよね」というチーム内の共通認識を作るために、モブプログラミングを導入しました。

チームには新卒メンバーもいたため、2年目以降のメンバーの暗黙知(エディタのショートカットとか、細かいところも含めて)を共有することもでき、良い施策だったと思っています。

③オニオンアーキテクチャ採用とGradleマルチプロジェクト化

DDDでは、実装したドメインモデルが"ドメインの知識の変更"以外の理由で更新されることを嫌います。

例えば、ドメインモデルクラス(Aクラス)がデータベースに処理内容を永続化するための処理を書いたクラス(Bクラス)に依存している場合のことを考えます。この時、データベースへの永続化コードに変更があった際には、BクラスだけでなくAクラスも変更しないといけない可能性があります。本来改修しなくてよい場所も改修しないといけなくなり、無駄なコストがかかるので嬉しくありません。

こんな状態を避けるためには、ドメインモデルクラスがドメインの知識以外の何にも依存しない状態を作る必要があります。

これを実現するために、管理画面リメイクチームではバックエンド開発にオニオンアーキテクチャを採用しており、さらにその実現方式としてGradleによるマルチプロジェクト化を導入しています。

マルチプロジェクト化して依存関係を強制することで、アーキテクチャに反するコードをそもそも書けない(=書いたらコンパイルエラーになる)ようにしています。

「依存関係の整理」と、「それに反する違反コードの排除」を(注意喚起とかコードレビューではなく)システムで強制できているので、山根はこの構成が結構好きなんですよね。

オニオンアーキテクチャって何?

アプリケーション内の処理を責務ごとに分離した上で、責務ごとの依存方向をシンプルなものに制限するアーキテクチャです。

責務を分離することで「このクラスは何をやってるクラスなのか」が分かりやすくなったり(可読性の向上)、また依存をシンプルにすることで継続的なコードの修正がしやすくなる(保守性の向上)が見込めます。

オニオンアーキテクチャのイメージ図

※引用元: https://little-hands.hatenablog.com/entry/2017/10/04/231743

管理画面チームではどうしてるの?

管理画面チームでは責務は以下のように層として分離され、依存先が決められています。

モジュール名 層の名前 担当 依存先
domain domain層 domainの知識表現を担当 他に依存しない
application application層 domain層の知識を用いて、ユースケースごとの処理を担当 domain層にのみ依存
infrastructure infrastrucutre層 DBやファイルシステムとの入出力を担当 domain層,application層に依存
presentation presentation層 リクエスト/レスポンスなど、UI部分の処理を担当 domain層,application層,infrastructure層に依存※1

※1 オニオンアーキテクチャにおいて、基本は「presentationはinfrastructure層に依存させない」はずなのですが、presentation層におけるいくつかの実装上の都合(認証とか、SpringのHandlerInterceptorを使ったアクティビティログ取得とか)で依存を許可しています。

また、管理画面改修チームでは上記の依存方向性を強制する仕組みとして、Gradleによるマルチプロジェクト化を実施しています。以下にバックエンドソースのプロジェクト構成と、プロジェクト間の依存を設定するbuild.gradle.ktsソース例を記載します。

(root)
├── domain
│   ├── src
│   │   └── (略)
│   └── build.gradle.kts
├── application
│   ├── src
│   │   └── (略)
│   └── build.gradle.kts
├── infrastructure
│   ├── src
│   │   └── (略)
│   └── build.gradle.kts
├── presentation
│   ├── src
│   │   └── (略)
│   └── build.gradle.kts
└── settting.gradle.kts
dependencies {
    //何にも依存しない
}
dependencies {
    implementation(project(":domain")) //domain層に依存
}
dependencies {
    implementation(project(":domain")) //domain層に依存
    implementation(project(":application")) //application層に依存
}
dependencies {
    implementation(project(":application")) //presentation層に依存
    implementation(project(":domain")) //domain層に依存
    implementation(project(":infrastructure")) //infrastructure層に依存
}
rootProject.name = "rootProjectName"
// サブプロジェクトの定義
include(":domain")
include(":application")
include(":infrastructure")
include(":presentation")

この構成を作るのに以下のブログが大変参考になったので、是非一読することをお勧めします。

blog.takehata-engineer.com

このプロジェクト構成にすることで、「domain層でapplication層の処理を呼ぶ」「application層でinfrastructure層の処理を呼ぶ」といった、アーキテクチャに違反したコードが書けなくなります。

よかったと感じること

ここまで、DDD実践のポイントを3つ紹介してきました。 以下では振り返りとして、DDDを1年間やってみて良かったこと、イケてるなと思ったところを挙げます。

品質担保しやすくなった

DDD以前の管理画面が「品質担保が難しい」という課題を抱えていたことは前述の通りですが、この点はかなり改善されています。

クラスの責務を考えた実装やクラス間の依存関係の整理により、単体テストを書くハードルが下がったことがチームの品質担保に良い影響を与えているように感じます。

DDDとは直接関係ないですが、品質担保のための施策は他にも(ブラウザを経たUIテストや、BackendAPIのEndToEnd試験など)行われているため、コードに変更を入れる際の安心感はかなり上がりました。

techblog.infcurion.com

コードが読みやすくなった

従来の手続き型の実装からドメインモデルを用いた実装に移行したことにより、必要な実装がどこに書いてあるかを探索しやすくなりました。

ドメインモデルクラスには、データとビジネスロジックがまとめて記載されているため、従来の手続き型によるコードよりも高凝集になり、より読みやすくなっています。

また、読みやすさが上がったことで、「どのクラス・どのメソッドに変更を入れれば一番スムーズに仕様変更を実現できるか」の想定もしやすくなっています。

難しいと感じること

以下では、DDDを1年間やってみて「難しいな...」と思うところを挙げます。

「常にドメインモデル図とコードを一致させておく」意識付けが難しい

DDDの原則として「ドメインモデル図に書かれている仕様と、コード(ドメインモデルクラス)を一致させておく」がありますが、意図せずこれに違反してしまう場合があります。

エンジニア側からの気づきがあって、追加設計して、それをコードには反映したけど、肝心のドメインモデル図に反映するのを忘れていた ...みたいなパターンです。

ドメインモデル図とコードの間で乖離が大きくなると、仕様変更時に「この機能ってどう動くのが正しいんだっけ?ドメインモデル図とコード、どっちが正しいの?」となり、不要な議論やコミュニケーションを生む原因になります。

今居るメンバーでずっと開発出来れば問題ないかもしれませんが、後から参画したメンバーのためにも、これは避けたいですよね。

定期的にドメインモデル図を見返して、「今、ドメインモデル図とコードって乖離してないよな?」を確認していく必要があると感じています。

一発で良いドメインモデル図が書けないこともある

これはインターネットや書籍でDDDを調べているとよく見かける記載なのですが、ドメインモデル図は 「まず一度作ってみて、改善を繰り返すもの」 です。

とある機能でドメインモデル図を書き、それをコードに落とし込んで開発を始めてみたところ、どうにも実装しづらい、ということがありました。

結局、そのドメインモデルについては「壊して作り直し」ということになり、メンバー2人で約2週間ほどの作業時間がかかる結果となりました。

一度頑張って作ったものを壊すのは心苦しいですが、 「まず一度作ってみて、改善を繰り返すもの」 の精神を忘れず、根気よく良いドメインモデルを作っていきたいところです。

まとめ:払うコストはあるものの、保守性は上がっている

本記事では、Wallet Station管理画面リメイクチームで行っているDDD開発のポイント3選を挙げ、良いと感じた点・難しいと思った点を振り返ってみました。

ドメインモデルの作り直し」のようなコストもそれなりに払っているものの、DDD導入によって、「品質保証のしやすさ」と「変更のしやすさ」の観点でメリットを得られています。

管理画面リメイクは発足して1年程度のチームで、開発した機能をユーザの皆さんにどんどん使っていってもらい、フィードバックを得て改善を重ねていくようなフェーズです。

改善活動の中で、DDDに限らないところでも、ノウハウが溜まったら引き続き発信していこうと思います!

本記事をお読み頂き、ありがとうございました!