/note/tech

Go開発者によるDDDの実践:概念理解から具体的な応用まで

要約:

■ 1. プロジェクト概要とリプレース背景

  • DMMブックスのバックオフィス向け管理画面サービス(10年以上の運用歴)において技術的負債解消を目的とした大規模リプレースを実施
  • 旧構成のPHP(FuelPHP)から新構成へ移行
    • フロントエンド: React(コンポーネントベースによるUI再利用性・保守性向上)
    • バックエンド: Go(静的型付けによる安定性・保守性向上)
    • 設計手法: DDD(ドメイン駆動設計によるビジネスロジック中心の設計)
  • 再構築による期待効果
    • システムの持続可能性向上(変化に強く柔軟なシステム)
    • 開発生産性向上(迅速な機能追加・改修)
    • 保守性向上(長期的な運用コスト削減)
    • 開発者体験の向上(最新技術導入によるモチベーション向上と人材確保)

■ 2. DDD導入における課題と対策

  • 課題:
    • 概念と実践のギャップ: Entity・Value Object・Aggregateなどの概念をGoコードへ落とし込む具体的なイメージが掴みづらい
    • Go言語特化のノウハウ不足: Go向けDDD実装例が少なく自力での試行錯誤が必要
    • ビジネスロジックの配置判断: ドメイン層・UI層・インフラ層の境界線の引き方が難しい
  • 対策:
    • 実践重視のアプローチを採用し概念の疑問をチーム内で徹底議論
    • 実装コードを必ずチームレビューし原則への適合性と改善点を多角的に検討
    • レビューを通じたチーム内知識共有でDDD実装スキルを向上
    • Go言語の特性を活かした実装方法を試行錯誤し独自のベストプラクティスを確立

■ 3. DDDの基本概念

  • ドメイン:
    • ビジネスが取り組む問題領域全体(例: 商品販売・顧客管理・在庫管理・決済)
  • エンティティ:
    • 一意の識別子を持つオブジェクト(例: ProductID で識別される Product 構造体)
  • バリューオブジェクト:
    • 属性の値を表す不変オブジェクト(例: 金額と通貨で構成される Price 構造体)
    • コンストラクタ以外に値を変更する手段を提供しないことで不変性を保証
  • アグリゲート:
    • エンティティとバリューオブジェクトの集合で一貫性の境界を形成
    • アグリゲートルートが全体を管理(例: Order がルートで OrderItem をバリューオブジェクトとして保持)
  • リポジトリ:
    • アグリゲートの永続化を担うオブジェクトでデータベースアクセスを抽象化
    • インターフェースとして定義し具体的な実装はデータストアに依存
  • サービス:
    • 特定のエンティティやバリューオブジェクトに属さないドメインロジックを実行(例: 注文合計金額の計算)

■ 4. DI/DIPの実装

  • 依存性逆転の原則(DIP):
    • 上位モジュールと下位モジュールの両方が抽象(インターフェース)に依存
    • 具体的な実装ではなくインターフェースに対してプログラミングすることが原則
  • 依存性注入(DI):
    • 依存オブジェクトの生成と注入を外部が担当しモジュール間の結合度を低下
  • Goでの実装構成:
    • ドメイン層: UserRepository インターフェースと UserService をdomainパッケージに定義し UserService はインターフェースのみに依存
    • インフラストラクチャ層: MySQLUserRepository がインターフェースを実装しDBアクセスを担当
    • main関数: MySQLUserRepository のインスタンスを生成して UserService へ注入
  • DI/DIP適用のメリット:
    • テストの容易性: モックリポジトリを注入することでDBアクセスなしにテスト可能
    • 変更への柔軟性: DBを変更する場合はインターフェース実装とmainでの注入箇所を変更するだけでドメイン層は無変更