本ページはプロモーションが含まれています

IT全般

ドメイン駆動設計(DDD)でリファクタリング!3つの原則で複雑なコードを整理した話

トム

・都内自社開発IT企業勤務/javaのバックエンドエンジニア
/java歴10年以上 ・首都圏在住30代
・資格:基本情報技術者/応用情報技術者/Java Silver/Python3エンジニア認定基礎

「修正したはずのバグが、なぜか別の機能で再発している」

「コードを読み解くだけで午前中が終わってしまった」

こんな「見えない恐怖」と戦っていませんか。画面の向こうで深く頷いている姿が目に浮かびます。私もそうでした。終わりの見えない修正作業、リリース直前に見つかる致命的な欠陥。これらは単に「コードが汚い」ことが原因ではありません。もっと本質的な問題は、「業務の知識やルールが、コード上に正しく表現されていない」点にあります。

この問題を放置するとどうなるか。エンジニアは日々の調査と修正に追われて疲弊し、新しい機能を作る時間は失われます。結果としてビジネスの成長さえも止まってしまうでしょう。これはエンジニア個人の問題ではなく、プロジェクト全体の危機です。

そこで私がたどり着いた解決策が、ドメイン駆動設計(DDD)でした。「難しそう」「理論ばかりで実践できない」と感じるかもしれません。しかし、正しく使えばこれほど強力な戦術はありません。この記事では、私が実際に崩壊寸前のプロジェクトをDDDを用いて立て直した実録を紹介します。

この記事を読めば、複雑に絡み合ったコードを「どこから」「どのように」整理すればよいか、具体的な道筋が見えてくるはずです。魔法のような銀の弾丸はありませんが、泥沼から抜け出すための確かな地図をお渡しします。

なぜドメイン駆動設計(DDD)でリファクタリングしようと思ったのか

私が担当していたシステムは、いわゆる「継ぎ足しの秘伝のタレ」状態でした。長年運用されてきたシステムは、初期の設計思想を誰も知らないまま、機能追加だけが繰り返されていたのです。毎日がお化け屋敷を探検するような気分でした。どこに罠があるかわからない恐怖と戦いながら、私は「根本的に変えなければ、いつか破綻する」と確信していました。なぜなら、コードがビジネスの変化についていけなくなっていたからです。

リファクタリング前のコードが抱えていた問題

当時のコードは、まさにカオスそのものでした。特に深刻だったのが、責務が曖昧なServiceクラスの存在です。

OrderServiceというクラスには、注文に関するありとあらゆる処理が詰め込まれていました。在庫チェック、決済処理、メール送信、ポイント計算。これら全てがひとつのクラスに書かれており、行数は5000行を超えていました。どこでデータを変更しているのか、誰も把握できません。

さらにDTOとEntityの境界崩壊も起きていました。データベースのテーブル構造を表すクラスが、そのまま画面表示や計算ロジックに使われていたのです。データベースのカラム変更が、画面の表示ロジックにまで波及してエラーを引き起こす。そんな事態が日常茶飯事でした。

結果として、仕様変更のたびに影響範囲が読めない状態に陥っていました。「一部を変えるだけ」の修正に大量のコードリーディングと工数が必要。なぜなら、あちこちに散らばったロジックを全て見つけ出し、テストしなければならないからです。

小手先のリファクタリングでは限界を感じた理由

最初は、メソッドの抽出や変数名の変更といった、一般的なリファクタリングを試みました。しかし、すぐに限界が訪れます。

命名を直しても本質は変わらないからです。「スパゲッティコード」の見た目を綺麗に整えたところで、絡まり合った中身はそのまま。変数の名前がint aからint priceになっても、そのpriceがあらゆる場所で書き換えられていれば、バグはなくなりません。

また、設計思想が共有されていなかった点も致命的でした。私がコードを綺麗に整理しても、翌週には別のメンバーがまた元の書き方で機能を追加してしまいます。「なぜここにロジックを書いてはいけないのか」という基準(ドメイン知識の置き場所)がチーム内で統一されていない限り、コードは再び腐敗していくのです。

今回のリファクタリングで採用したDDDの考え方

DDDには膨大な概念があります。すべてを一度に導入しようとすれば、チームは混乱し、開発はストップしてしまうでしょう。そこで私は「現状の課題解決に必要な部分だけ」をつまみ食いすることにしました。教科書通りの完璧さよりも、現場での実用性を優先させたのです。

DDDの全てはやっていない理由

まず、戦略的DDDは最小限に留めました。「境界づけられたコンテキスト」や「コンテキストマップ」といった概念は非常に重要です。しかし、既存のモノリシックな(一枚岩の)システムをいきなりマイクロサービスのように分割するのはリスクが高すぎます。まずは目の前のコードを整理する「戦術的DDD」に集中しました。

これは現実的な導入ラインを探った結果です。チームメンバー全員がDDDの専門家ではありません。学習コストを抑えつつ、効果を実感してもらうには、コードレベルでの改善から始めるのが最善だと判断しました。

特に意識した3つのポイント

リファクタリングの指針として、以下の3つだけは徹底しました。これさえ守れば、コードの品質は劇的に向上します。

  1. ドメインモデル中心であることです。これまでは「データ」と「処理」がバラバラでした。データを持つクラス(Entity)に、そのデータを使うルール(メソッド)も持たせる。オブジェクト指向の基本に立ち返り、データと振る舞いをセットにしました。
  2. ユビキタス言語です。コード内のクラス名やメソッド名を、現場の業務担当者が使う言葉と一致させました。「ユーザー」ではなく「会員」、「削除」ではなく「退会」。言葉のズレがバグを生む温床だからです。
  3. 依存関係の向きです。ドメイン(業務ロジック)をシステムの中心に置き、データベースやUI(ユーザーインターフェース)がドメインに依存するようにしました。ドメインがデータベースの都合に振り回されるのを防ぐためです。

実際にどうリファクタリングを進めたか

ここからは具体的な手順をお話しします。いきなりエディタを開いてコードを書き換えることはしませんでした。急がば回れ。まずは現状の理解から始めました。

まず業務ロジックをコードから言語化した

最初に行ったのは、コードリーディングからの日本語化です。

複雑怪奇なOrderServiceのコードを読み解き、「ここで何をしているのか」を日本語で書き出しました。「もし在庫がなかったらエラー」「合計金額が5000円以上なら送料を0にする」といった具合です。

これにより、クラス設計の前に「業務の流れ」を可視化しました。コードに埋もれていた仕様を掘り起こす作業です。驚いたことに、誰も知らない謎のロジックが大量に見つかりました。これらを整理するだけでも、システムは随分と身軽になります。

Entity / Value Object / Domain Serviceの切り分け

次に、書き出した業務ロジックを適切な置き場所に振り分けていきます。ここでDDDの戦術的パターンが登場します。

まず何をEntityにしないかを慎重に決めました。全てのデータをEntityにすると管理が大変です。IDを持ち、ライフサイクル(生成から消滅まで)を管理すべき重要な対象(注文、会員など)だけをEntityとして定義しました。

一方で、Value Object(値オブジェクト)を増やした理由は、ロジックの散逸を防ぐためです。例えば「住所」や「金額」といった概念です。これまでは単なるStringintとして扱われていましたが、専用のクラスを作りました。「金額は負の値にならない」「住所には郵便番号が必須」といったルールをクラスの中に閉じ込めることで、不正な値がシステムに入り込むのを防げます。

Serviceの役割を整理した

最後に、肥大化していたServiceクラスのダイエットです。

ここでやらせないことを決めたのが最大のポイントです。Serviceにはビジネスロジックを一切書きません。彼らの仕事は「ドメインオブジェクトをリポジトリから取り出し、メソッドを呼び出し、保存する」という進行役のみ。

トランザクション境界の置き方もここで整理しました。ひとつの業務処理(ユースケース)が完了する単位でトランザクションを管理し、データの整合性を保ちます。ロジックがドメインモデルに移動したおかげで、Serviceクラスは驚くほど薄く、読みやすくなりました。

リファクタリング後、コードはどう変わったか

苦労の末、リファクタリングを終えたコードは別物のように生まれ変わりました。見た目の美しさだけでなく、開発のスピードや安全性に直結する変化です。

クラス構成と依存関係の変化

before / after の考え方で言うと、以前は「手続き型」のスクリプトのようなコードでした。上から下へダラダラと処理が書かれていたのです。afterでは、意味のある小さな部品(オブジェクト)が連携して動く形になりました。

これによりレイヤー構造の明確化が達成されました。プレゼンテーション層(画面)、アプリケーション層(進行役)、ドメイン層(業務ルール)、インフラ層(DBなど)がきれいに分かれました。DBを変更してもドメインロジックには影響しませんし、画面のデザインを変えても計算ロジックは壊れません。各層が独立しているため、テストも非常に書きやすくなりました。

仕様変更への耐性はどうなったか

最も効果を感じたのは、影響範囲の予測が容易になった点です。「送料の計算ルールを変えたい」と言われたら、「ShippingCostクラスを見ればいい」と即答できます。以前のように、コード全体をgrep検索する必要はありません。

これは修正時の心理的負担を劇的に下げてくれました。「ここを直せば、他には影響しない」という安心感。これがあるだけで、エンジニアは自信を持ってコードを変更できます。お化け屋敷だったプロジェクトは、整理整頓された図書館のように、どこに何があるかすぐに分かる場所に変わりました。

DDDリファクタリングで正直つらかったところ

もちろん、全てが順調だったわけではありません。DDDにはそれなりの「痛み」も伴います。これから導入を考える方のために、正直な体験談をお伝えします。

最初はコード量が増えた

リファクタリング直後、「綺麗になったのに長い」問題に直面しました。クラスの数が倍以上に増えたのです。

String emailで済んでいたものをEmailAddressクラスにするわけですから、ファイル数は確実に増えます。単純なCRUD(登録・参照・更新・削除)だけの機能なら、DDDは明らかにオーバーエンジニアリングです。「これだけの処理にこれだけファイルがいるの?」と、自分でも疑問に思う瞬間がありました。しかし、長期的に見ればこの「冗長に見える構造」が複雑さを抑え込む防波堤になります。

チームに説明するコストが高かった

もうひとつの壁は、設計思想はコードだけでは伝わらないことでした。

なぜわざわざValue Objectを作るのか。なぜServiceにロジックを書いてはいけないのか。これをチームメンバーに理解してもらうには、根気強い対話が必要でした。コードレビューで何度も「ここはドメイン層に移しましょう」と指摘し、勉強会を開いてDDDのメリットを共有しました。共通認識ができるまでは、開発スピードが一時的に落ちる覚悟が必要です。

それでもDDDでリファクタリングして良かった理由

初期コストはかかりましたが、それを補って余りあるメリットがありました。一度軌道に乗れば、開発効率は以前とは比べものにならないほど上がります。

コードレビューの観点が揃った

DDDの導入により、良い / 悪いの基準が明確になりました。以前は「好みの問題」で揉めていたコードレビューが、「ドメインモデルとして正しいか」という建設的な議論に変わりました。

「このロジックはこのEntityが持つべき責務ではないか?」「この名前はユビキタス言語とずれている」といった指摘が飛び交うようになり、レビューの質が格段に向上しました。基準があれば、迷いがなくなります。

ドメイン知識がコードに残るようになった

何より嬉しかったのは、人が変わっても理解できる設計になったことです。

新しく入ったメンバーがコードを読んだとき、「コードを見れば業務仕様がわかる」と言ってくれました。仕様書はすぐに古くなりますが、動いているコードは嘘をつきません。ドメイン知識がコードそのものに表現されているため、コードがそのまま生きたドキュメントとしての役割を果たしてくれるのです。これは長期的に運用するシステムにとって、計り知れない資産となります。

DDDリファクタリングはどんな人・プロジェクトに向いているか

DDDは素晴らしい手法ですが、万能ではありません。向き不向きを見極めて導入することが成功の鍵です。

向いているケース

業務が複雑なシステムには最適です。ECサイト、金融システム、物流管理など、ルールが多く、例外処理が複雑なドメインではDDDの真価が発揮されます。

また、仕様変更が多い場合も向いています。ビジネスの変化に合わせてコードを安全に変更し続ける必要があるなら、DDDのコストを払う価値は十分にあります。中長期で育てるシステムなら、迷わず導入を検討すべきです。

向いていないケース

逆に、CRUD中心の単純な掲示板や管理ツールには不向きです。データを右から左へ流すだけの処理にDDDを適用すると、無駄にコードが複雑になるだけです。

また、短命なプロジェクトや、使い捨てのキャンペーンサイトなども推奨しません。学習コストや設計コストを回収する前にプロジェクトが終わってしまうからです。

まとめ:DDDは「整理術」だった

今回のリファクタリングを通じて私が得た最大の収穫は、DDDが決して難解な理論ではなく、実用的な「整理術」だという気づきです。

設計の正解が分かったわけではありません。今でも迷うことはあります。しかし、考える軸が手に入ったことは大きな進歩です。「これはドメインの知識か?」「これはアプリケーションの都合か?」。この問いかけができるだけで、コードは驚くほど整理されます。

リファクタリングの目的がブレなくなったことも重要です。単にコードを綺麗にするのではなく、「ビジネスの変化に強いコードにする」という明確なゴールに向かって進めるようになりました。

  • この記事を書いた人
  • 最新記事

トム

・都内自社開発IT企業勤務/javaのバックエンドエンジニア
/java歴10年以上 ・首都圏在住30代
・資格:基本情報技術者/応用情報技術者/Java Silver/Python3エンジニア認定基礎

-IT全般