LoginSignup
277
206

More than 1 year has passed since last update.

ドメイン駆動設計(DDD)で開発されたシステムを5ヶ月保守開発した感想・学び

Last updated at Posted at 2022-12-01

巷で、顧客の課題を解決しつつ、より良いシステムを作るための設計手法として、ドメイン駆動設計(DDD)が話題になっていると思います。

このドメイン駆動設計について、どのように実践するか、実際に実践してみてどう感じたか、という話はよく出ていますが、作られたシステムがその後どのようになったのか、保守開発した結果どう感じたのかの話はあまり聞かないな、と思ったので、自分の経験から「実際のところどうなんだ」というところを振り返ってみようかな、と思い、今回の記事を書きました。

目次

私が保守開発しているシステム

NP掛け払いという事業における、購入企業審査システムの保守開発を行っています。

NP掛け払いは、与信や請求書発行、代金回収、入金管理、督促、未回収リスクの保証など、企業間における後払い決済(掛け払い)に関する諸々の運用やリスクを肩代わりするサービスです。
image.png
購入企業審査システムは、買い手企業の信用度を測り、その企業にNP掛け払いを利用していただくことができる金額を算出することを目的としたシステムです。

このシステムこそが今回テーマとしてあげる、ドメイン駆動設計で作られたシステムになります。

5ヶ月の間にやったこと

個人の成果ではなく、チームの成果なのですが、50チケットほど保守開発を行いました。
image.png
私が所属するチームでは、改修要望が上がるたびにbacklogにチケットを作成し、案件の管理を行っているので、大体50ほどの要望を捌いてきた、ということになります。

チケットの内容には、些細な技術的な変更もあれば、まさしくドメイン知識の変化の反映、と言えるような変更も含まれていました。

例えば、以下のような知識の変化に対応してきました。

  • ある企業の利用上限金額を確定する基準を変更したい
  • ある企業とある企業が同一であるとみなす基準を変更したい

このようなドメイン知識の変化に応じた改修をする瞬間こそが、ドメイン駆動設計の真価が問われる瞬間かな、と思っています。
以下では、このような改修を行う中で感じたドメイン駆動設計のメリットや、難しさ、学びなどについて書きます。

保守開発していて感じたこと

よかったこと

改修時に修正箇所が特定しやすかった

これが一つ大きなメリットとしてあげられるかな、と思います。
ドメイン駆動設計では、システムの利用者が使用する言葉をそのまま用いて、モデリングを行い、ドメイン知識をソースコードに反映します。
image.png
この画像が示すように、購入企業審査システムにおいても、ドメインマスターである事業部のメンバーと協力し、審査に関連する知識のモデリングを行い、具体的なコードに反映しています。

以下がドメイン知識を反映したコードの例です。

/**
 * 購入企業エンティティ
 */
data class Buyer(
    /** 企業名 */
    val companyName: CompanyName,

    /** 住所 */
    val address: Address,

    /** 電話番号 */
    val tel: Tel,

    ... // その他多数のプロパティ
) {
    ... // 購入企業に関する知識を反映した振る舞いなどの実装
}
/**
 * 企業名値オブジェクト
 */
data class CompanyName(
    /** 企業名 */
    val value: String
) {
    ... // 企業名に関する知識を反映した振る舞いなどの実装
}

このように、審査ユースケースに伴って登場する概念について、一つ一つエンティティや値オブジェクトとして、忠実に実装しています。

そのため、ユーザーから、

  • 企業の利用上限金額を確定する際の、〇〇の参照の仕方を変えたい
  • 企業と企業の一致判定を行う際の一要素である、〇〇の一致基準を変更したい

といった要望が出てきた際に、このエンティティや値オブジェクトといった「ユーザーが使用する言葉・概念をそのまま反映したオブジェクト」とそのオブジェクトに依存するコードを見に行けば、改修範囲を概ね特定することができます。
(綺麗に整理が行われていれば、上記のコード以外に気にするべき実装はないはずだからです。)

要するに「〇〇に関する実装って、あの処理とあの処理とあの処理に書かれてたよな、、いや、他にも何かあったか...?」と当てどなく影響範囲を調査する必要がなくなる、ということです。

テストコードが書きやすく安心して保守することができた

ドメイン駆動設計には、レイヤードアーキテクチャやオニオンアーキテクチャのように、レイヤーを切り、依存の方向性を一貫させることで、ドメイン知識に関する実装の変更を容易にする、というプラクティスがあります。
(詳しく知りたい方は以下のリンクから別記事に飛んでいただけると、と思います。)

レイヤードアーキテクチャ
image.png
オニオンアーキテクチャ
image.png
購入企業審査システムはオニオンアーキテクチャに近い形をとっています。
このプラクティスの効果は、前述した変更範囲の特定のしやすさ、にも現れているのですが、それとは別にテストコードが書きやすくなる、というメリットもありました。

レイヤーを切り、モジュール性の高い実装を行ったり、依存の方向性を一貫させ、インフラストラクチャ層などの、より外部に位置するパーツの交換をしやすくしたりしていることによって、モックを利用したテストが行いやすくなっていたり、ドメイン層のロジック単体でのテスト(E2Eではない、レイヤーごとのテスト)など特定のレイヤーに閉じたテストが行いやすくなっていたり、と非常にテストが書きやすかったです。

例えば、エンティティ(ドメイン層)に関しては他に依存しないので、以下のように単体でテストすることが可能です。

/**
 * 購入企業エンティティ
 */
class BuyerTests {

    /**
     * ... テストの内容を記載
     */
    @Test
    fun `test_example`() {
        ... // 購入企業の振る舞いに関するテスト
    }

    ... // その他多数のテストケース
}

また、ユースケースなど、他の層に依存するような、より外側にあるレイヤーについても、レイヤーが綺麗に分離され依存の方向性が一貫していることにより、以下のように簡単に他の層をモック化し、テストすることができます。

/**
 * 審査ユースケース
 */
class GrantCreditUseCaseTests {

    @Autowired
    private val mockClientA = mock<ClientA>()

    @Autowired
    private val mockClientB = mock<ClientB>()

    @Autowired
    private val mockMapperA = mock<MapperA>()

    ... // その他多数のモック

    /**
     * ... テストの内容を記載
     */ 
    @Test
    fun `test_example`() = runTest {
        ... // ユースケースの挙動に関するテスト
    }

    ... // その他多数のテストケース
}

そのため、自動テストを充実させることができ、保守開発を行う際も、何か不具合が紛れ込んだらテストが検知してくれる、という安心感を持つことができました。
この安心感は、保守の推進に大きく寄与したかな、と思っています。

成長できたという実感があった

ドメイン駆動設計をしていると、自然と、普段何気なく書いている手続き的なコードについて、ドメインのモデルに引き合わせることができないか、を考えながら実装を進めることになります。つまり、自然とオブジェクト指向的な考え方でコードを書いていくことになるので、オブジェクト指向でプログラムを書く力が上がっていきます。
また、そのような「これはドメイン知識なのか、モデルに反映できるコードなのか」というテーマについて、チームの中で活発に議論が行われるので、何を抽象的な概念と捉え、何を実装に関連した具体的な処理だと捉えるか、に関する共通認識を作っていくことができます。
この共通認識の醸成は、経験の浅いメンバーにとっては、判断の感覚を養うことにつながっており、自分自身もこの議論を行う中で成長できたという実感がありました。

この「何を抽象的な概念と捉え、何を実装に関連した具体的な処理だと捉えるか」に関する思考は、ドメイン駆動設計されたシステムに限らず、どのシステムでも行えると良いものだと思いますが、なかなか徹底するのは難しいものかな、と思っています。
ドメイン駆動設計を採用したことで「自分たちはそのような考え方・作り方をするんだ」と各メンバーが明示的に意識できる状態になったのは、このような思考を徹底する上でプラスに作用したかな、と思っています。

また、ドメイン駆動設計で開発を行うことは、問題・システムを作る目的、に向き合う姿勢を育むことにもつながったのかな、と思っています。
どうしても経験が浅いうちは、大抵の人は、自分がどのようにシステムを作るか(how)、にフォーカスして物事を考えてしまうと思います。しかし、システムを作るのは、何らかの問題を解決するためなので、問題(why)に向き合うことなくして、いいシステムは作れないと思っています。
ドメイン駆動設計をしていると、開発する中で自然と、問題となっているドメインに向き合う必要が出てくるので、問題を意識しながらコーディングを行う習慣をつけることができました。

難しかったこと・学び

ドメイン知識は次第に流出していく

改修を加えるごとにシステムの複雑性が増し、リリース時の整然としたコードは過去の幻想だったのではないか、と思われるような変化を遂げていく、というのはシステムの運用・保守開発の現場においては、あるあるかな、と思います。

ドメイン駆動設計においても、その変化が起きること自体を食い止めることはできず、初期リリース前に何度も「これはこのモデルが持つべきドメイン知識か」と議論し、モデルにおくことを決断したような実装が、小さな改修においては「簡単に実装できるから」と議論もされず、ドメイン層ではなく、インフラストラクチャ層に置かれ、次第にドメイン層が持つべき知識が他の層に流出していく、というような事象が発生しました。

例えば一例を挙げると、上で書いた「ある企業とある企業が同一であるとみなす基準を変更したい」という要求に応えるにあたって、一致判定を行うための要素の一つである「企業名」の一致判定ロジックが、企業名を表す値オブジェクトの中ではなく、他の企業のデータを取得してくるロジックの中に埋め込まれる、というようなことが起きました。

定期的なメンテナンスが大事

このようなドメイン知識の流出を防ぐためには、やはり継続的な議論と、定期的なメンテナンス・リファクタリングが必要です。
幸い、ドメイン駆動設計され、適度にモデリング化されたシステムにおいては、このメンテナンスがやりやすいな、と感じました。ドメインを反映したモデルを頭に入れつつ、たまにコードを見返して「これはこのモデルに関連する処理なんじゃないか?」と自問自答し、メンバーと議論すれば、自ずとリファクタリングが進んでいくからです。

例えば、先ほど書いた企業名に関するロジックが流出している、という事象も、メンバーの中での「企業名という値オブジェクトがある」という認識と、ふとコードを眺めて、それがドメイン知識であるかを考える、という習慣から気づけたものです。

このような経験から、ドメイン駆動設計されたシステムがその綺麗に整理された構造を保ち、上に書いたような価値を発揮し続けるためにも、このようなメンテナンスを適宜行う必要がある、という意識づけはやはり大切なのかな、と感じました。
特に、ドメイン知識に関する部分は、あくまでリリースした時のモデルが完成品ではなく、日々、変化していくことが前提となっているので、ビジネスの変化に応じて、改修を行う際には、些細な改修であっても「これはドメイン知識と言える部分なんじゃないか」「このモデルに紐付けられるんじゃないか」「そもそもこのモデルは実態に即しているのか」と思考し、しっかりと整理されたコードに仕上げていくことは重要かな、と感じています。

最後に

以上が、私がドメイン駆動設計で開発されたシステムを5ヶ月保守開発して得た感想、学びでした。

今回の記事を書くにあたって、他のチームメンバーにも「ドメイン駆動設計されたシステムを保守してみて、どう感じたか」を聞いてみましたが、私含め、私が所属しているチームのメンバーは総じて、ドメイン駆動設計に好意的な意見を持っているようでした。
まだ5ヶ月しか経過していないので、この設計手法をとったのが本当に良かったのかは継続して観察・議論していく必要があるかな、と思っていますが、現時点ではプラスの効果が多かったように感じています。

もちろん、ドメイン駆動設計は銀の弾丸ではなく、システムが綺麗な状態を保ち、その価値を発揮し続けるためには、上に書いたような定期的な議論・メンテナンスといった継続的な努力が必要です。が、それでもドメイン駆動設計されたシステムはそのような議論やメンテナンスをかなり行いやすくしてくれるので、導入するだけの価値は十分にあるのではないか、と思いました。

もし、この記事でドメイン駆動設計に興味をもった方がいらっしゃれば、ぜひ試してみていただけると幸いです。

おまけ

エンジニア以外のメンバーが感じたこと

ついでに、エンジニア以外のメンバーにも、ドメイン駆動設計をしてみて感じたことを聞いてみました。
以下のようなことを感じたそうです。

コミュニケーションしやすかった

最初は、言葉の定義を揃えていくことのコストがあったが、一度揃ってしまえば、自分たちが普段業務で使っている言葉そのままでエンジニアとコミュニケーションをとることができるので、非常にコミュニケーションしやすくなった。

また、「境界づけられたコンテキスト」という概念は非常に参考になった。
同じ言葉ではあるが、コンテキストによって微妙に意味合いが変わる言葉について、普段自分たちは無意識化で意味の使い分けを行っているが、それではエンジニアに正しく伝わらず、認識齟齬が生まれる可能性があるのだ、ということを念頭においてコミュニケーションするきっかけとなった。
このような微妙なニュアンスについて、しっかりと言語化し、共通認識を作ったからこそ、仕様の認識違いが少なく、バグも少ないシステムが作れたのかな、と思う。

システム構成を理解しやすかった

普段自分たちが意識しているユースケースをそのままシステムに落とし込んでいくので、システムの構成を理解しやすかった。システムがなぜこのような構造になっているのか、というところが把握しやすいので、業務側もアーキテクチャの意図を汲み取りやすく、エンジニアからシステムの作りについて説明された際に、理解しやすく、納得しやすかった。
また、理解しやすい分、変な認識齟齬が生まれにくく、要件漏れなども発生しにくかった、という感覚がある。

システムに興味を持ちやすくなった

ドメイン駆動という言い方だと、業務の側に合わせてシステムを作るんだな、という感覚が持てる。実際に、ドメイン駆動設計を進める上で、自分たち業務側とエンジニアが密にコミュニケーションをとりながら、認識合わせ、モデリングをしていくので「システムに関することはエンジニアの仕事だから気にしなくていい」というテンションになりにくい、というメリットがあったと思う。

以上のように、エンジニア以外のメンバーも、ドメイン駆動設計に対しては総じて良い印象を持っているようでした。
実際、私もコミュニケーションのしやすさなどは感じていたので、ドメイン駆動設計は、エンジニアとそれ以外のメンバーとの間の距離を近づけ、コミュニケーションを円滑にするためのツールとして上手く役立っていたのかな、と思いました。

ドメイン駆動設計を学習するなら

ドメイン駆動設計に興味を持った、学習して自分の現場でも実践してみたい、という方は以下に挙げた書籍や記事、動画を見てみるのが良いかな、と思っています。
また、やはり理論を学習しただけではどうしても身につきにくい部分はあると思うので、小さなものでも良いので一度、この手法にのっとってシステムを構築・運用してみるのが良いかな、と思います。
少しでも参考になれば、幸いです。

書籍

記事

動画

277
206
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
277
206