開発中アプリのソースコードにDDDを持ち込む
ソフトウェアエンジニア志望の大学3年生です。
知り合いの紹介で,進行中のFlutterアプリ開発プロジェクトに参加することになりました。しかしながら,ソースコードの読解が少し難しかったため,それをDDD(ドメイン駆動設計)の考えを取り入れながら改善・追加実装していく過程を記録しようと思います。
**この記事は執筆中です。**あくまで自分で情報を整理するためのメモであり,かつ本当に書きかけですが,私が知っている限りのDDD実践に必要なキーワードを散りばめてありますので,参考程度にお読みください
背景
引き継いだFlutterアプリのソースコードが読みにくい。
原因はなにか?
- DTOに対して処理を外から付け加える,トランザクションスクリプトになっていた
→ドメインモデルとして振る舞いもそこにカプセル化 - 「リポジトリ」にビジネスロジックが書かれていた
→サービスクラス(ドメインサービス),ユースケース(アプリケーションサービス)および,コントローラー,インターフェースアダプターなどきちんと定義する
また他にも好ましくない実装があった。
- フロント側で一旦全データを取得してから,必要なデータを絞り込むロジックを書いていた
→WebアプリのようにAPIサーバーをたてるわけではないので,フロント側で全データを取得することが直ちに悪とはいえない。しかし,この実装ではフロント側がドメインモデルの実装について詳細な知識を持つ必要がある
こうした状態のもとリファクタリングするには,段階的なコード入れ替えのため腐敗防止層も必要になる。
やるべきこと
- きちんとドメインモデルのモデリングを行い,集約やコンテキスト境界を明らかにする必要がある。
→リポジトリの責務を絞るとともに,リポジトリそのものの数を減らす(集約ルート単位で操作するのが基本) - ユースケースのモデリングも行い,リポジトリ実装に書かれていたビジネスロジックを適切な粒度に分解
- UI上でのデータ表示のため,CQRS構成を導入
- 腐敗防止層の導入により,レガシーコードとの共存をはかる
FlutterにDDDを導入する上での参考記事↓
1. ドメインモデリング
リポジトリは集約単位で作成する。
→参考:https://qiita.com/os1ma/items/28f5d03d3b92e6a1e1d8
リポジトリは機能単位で作らない。あくまでドメインモデル(厳密には集約ルート)を永続化し,あたかもインメモリで動作しているかのように呼び出すための道具。
参考: https://qiita.com/mikesorae/items/ff8192fb9cf106262dbf
また,元コードはfreezedを利用して生成したデータクラスをもとにしたトランザクションスクリプトになっていたが。しかし,ドメインモデルにロジックをカプセル化するためには,メソッド=振る舞いをもったクラスとして扱う必要がある。
そこで,Factory
パターン等を取り入れてデータクラスから新しい別の振る舞いつきのオブジェクトを生成するか,ミックスインにより振る舞いを加えたオブジェクトを生成する
2. ユースケース
3. CQRS構成
ソフトウェアの実装において,データの「取得」と「更新」は根本的に性質が違う。特にDDDの文脈では,データをどのように「更新」するかに関心が行きがちだが,「取得」するだけのロジックは別で用意したほうが整理しやすい。
- 「取得」はUIの関係上,呼び出される回数が「更新」よりも多い
- 「取得」は,ドメインモデル間の関係性というより,どんな情報(フィールド)があれば必要十分なのか,に関心をおいている
そこで,CQRS(コマンド・クエリ責務分離)パターンを利用する。
CQRSにおいては,DDDで想起されるようなデータ操作は「コマンド」として指令される。一方,フロント側で表示したいデータを取得する際には「クエリ」を利用する。
CQRS参考サイト:
TODO: 続きの執筆
4. 腐敗防止層
レガシーコードやレガシーシステムと共存させながらリファクタリングや追加実装を行うために,データ形式等の変換を行う層。
参考サイト:
TODO: ちゃんと上記記事読む。
Discussion