ピンポイントタイム散布(PTS)でのバックエンド改善の軌跡 ~8. Testable ~

こんにちは、農業系プロダクトの開発を担当している糸井です。

弊社ではピンポイントタイム散布(以下、PTS)という防除のデジタル化サービスを展開しております。

www.optim.co.jp

PTSシステムのバックエンドではSpringBootを使用しております。
立ち上げ期より、既存の農業系プロダクトの課題感をもとに改善を進めてきましたので、その軌跡をご紹介します。

前回までの記事はこちら


前回までの記事で詳細な改善事例をご紹介してきました。
いよいよ最後の記事となります。今回はこれまでの改善のまとめとして単体テストの事例についてご紹介します。

8. Testable

ドメイン駆動設計 モデリング/実装ガイド にて簡単なUsecaseのサンプル実装がありますが、
PTSの実装ではUsecaseの1つのPublicメソッドの中で全部のロジックが実装されていることが多く、
そのメソッドは数百行に渡っていました。

そのため、単体テストが書きづらい、
後からプロダクションコードをいじらずに単体テストだけを追加しづらい状態でした。
(リファクタリングを始めるためにはまずは単体テストを書きたいところですが、
得てして最初にプロダクションコードをいじる必要性がありがちです。
ですが、プロダクションコードをいじると影響範囲も含めて検証してもらう必要が出てくるので、
単体テストを書く時間が取れなくても、最低限後から単体テストだけを追加できる実装にしておきたいという思惑があります。)


3. PublicSetter に端を発した改善のおかげで、
最も重要なロジックを詰め込んでいる Domain の Entity の単体テストが書きやすくなりました。

Testableになった結果

まずは一番上の、 jp.co.optim.farm.agri.pts の行にご注目ください。
クラス数, メソッド数, 行数, ブランチ(分岐)数、全ての項目が約1.5倍ほど増えていますが、
テストカバレッジはむしろ上がっています。

次に、common の行にご注目ください。
このModuleには、最も重要なビジネスロジックが含まれているDomainのEntityなどが実装されています。
他の項目も増えてますが、特に ブランチ(分岐)数 は、約3.6倍に増えています。
ですが、テストカバレッジは全ての項目で約10%も上がっています。

2023/08/18 2024/07/30


(MultiModule 構成になっており、 DB を使ったテストがいっぱい書かれている module を実行してもテストカバレッジに反映されなかったため、 実際はもう少しカバレッジは上がります。


(lombokで自動生成されるメソッドについては、カバレッジ算出の対象外とする設定を入れています。

Configuration system
Lombok can be configured to add @lombok.Generated annotations to all generated nodes where possible;
useful for JaCoCo (which has built in support), or other style checkers and code coverage tools:

  lombok.addLombokGeneratedAnnotation = true

Testableのその先


質とスピード(AWS Dev Day 2023 Tokyo 特別編、質疑応答用資料付き) / P.114 ソフトウェアの規模がスイートスポットに与える影響 (和田卓人様より転載の許諾を頂きました。ありがとうございます。


PTSのバックエンドのLOC(LinesOfCode)も3万行を超えてきて、今後もさらなる機能追加や改修が入ることを考えると、
内部品質について取り組むべき時期に差し掛かっていると思います。

内部品質を向上させていく取り組みの前段として、設計・実装時の当たり前基準を向上させるような種蒔きは、
1. OnionArchitecture から本記事まででできてきたかなと思います。

と言うことで、本記事までの改善事例をマザー・テレサ風にまとめてみます。

本記事までのまとめ

DomainのEntityにPublicSetterを付けるのをやめなさい。そうすればDomainのEntityにビジネスロジックを詰め込めるから。
DomainのEntityにビジネスロジックを詰め込みなさい。そうすれば最も重要なビジネスロジックのテストが容易になるから。 (*1, *2)
テストを書きなさい。そうすればリファクタリングできるから。 (*3, *4, *5)
リファクタリングしなさい。それは良い設計・内部品質に繋がるから。 (*6)
良い設計・内部品質にしなさい。それは変更を容易にし、保守性を高めるから。 (*7, *8)
変更を容易にし、保守性を高めなさい。それはビジネス面での競争力にも繋がるから。 (*9, *10, *11)

*1: DomainのEntityは依存関係の頂点にありWebやDBに依存しないので、単体テストの実行速度が速いという利点もあります。
*2: DDDでは、DomainModelは運用を経て得た知識を繰り返しソフトウェアに反映し継続的に改善することが前提とされており、それ故にアジャイル開発との親和性も高いとされています。また、継続的な改善に耐えるためにDomainのEntityのテストを書くことが前提とされています。
*3: TDDは単なるテストファーストではなく、リファクタリングを繰り返してより良い設計にしていくためのプラクティスです(【翻訳】テスト駆動開発の定義)。
*4: リファクタリング中も、自動テストは間違いを素早く補足してくれます(開発生産性の観点から考える自動テスト(2024/06版) / P.5 自動テストの動機:常に変化を可能にするため)。
*5: ただテストを書くだけでは品質は上がりません(開発生産性の観点から考える自動テスト(2024/06版) / P.56 咳さん曰く)。
*6: コードは3回書き直すと良い設計になるらしいです(コードは2回書きたい - Mitsuyuki.Shiiba / t-wadaのブックマーク)。
*7: ここでいう 変更を容易にする とは、今その瞬間にCommitを素早く行える という意味ではなく、機能追加・機能改修を素早く行える という意味です(アジリティを支える品質特性 / P.23 変更用意性の高いソフトウェアによるアジリティの獲得)。
*8: 変更容易性(修正性)を内包する保守性とは、アジリティを支える前進力、駆動力そのものです(アジリティを支える品質特性 / P.25 アジリティを支える品質特性である保守性と、その品質副特性)。
*9: テスト容易性は、パフォーマンスを向上させる上で重要な特性の内の1つです(アジリティを支える品質特性 / P.65 テスト容易性とデプロイ容易性(デプロイ独立性))。
*10: 開発速度と品質はトレードオフの関係ではなく、高い内部品質が高い開発速度を導くとされています(質とスピード (2023) / P.53 ご清聴ありがとうございました), (質とスピード (2023) / P.65 4つのキーメトリクス)。
*11: 自動テストを書くのは変化を可能とする能力を備えておくためであり、変化する技術、市場の状況、顧客の好みに対し、より迅速に適応するための要素の1つとなっています(開発生産性の観点から考える自動テスト(2024/06版) / P.5 自動テストの動機:常に変化を可能にするため)。



改善に向けて動き始める以前より、チームの他のメンバーからも「テストを書きたい。けど、書き方が分からない。」などの悩みを伺っていました。
私自身もキャリアを Ruby on Rails から初めたので、レイヤード系アーキテクチャでの理想的なコーディングについて不勉強だったため、様々な書籍やサイトを読み漁って学習を進めてきました。
結果、まずは戦術的DDDを部分的にでも適応するところから開始し、このような改善を推し進めてきました。
今後も戦略的DDDを開始したり、原理主義に陥らない程度に様々な書籍・サイトを参考にしたり、ハイパフォーマーになるべく取り組んでいきたいと考えています。

最後に

OPTiMではチームで協力し、難しく大きな課題を楽しみながらチャレンジしていきたいというメンバーを大募集しております。 ご興味がありましたら、下記フォームよりご応募ください。

www.optim.co.jp