Upgrade to Pro — share decks privately, control downloads, hide ads and more …

開発効率向上のためのリファクタリングの一歩目の選択肢 ~コード分割~ / JJUG CCC 2...

ryounasso
October 27, 2024

開発効率向上のためのリファクタリングの一歩目の選択肢 ~コード分割~ / JJUG CCC 2024 Fall

JJUG CCC 2024 Fall のランチセッションにてお話しした資料です。

ryounasso

October 27, 2024
Tweet

More Decks by ryounasso

Other Decks in Programming

Transcript

  1. 8 kintone開発チームの体制 機能領域ごとの担当チーム • ドメイン‧コード理解の促進のため分担 • エンジニアはいずれかの領域チームに所属 • エンジニア‧QAエンジニア‧スクラマスターで構成 アプリの作成/設定

    アプリの利⽤ ナビゲーション・ コミュニケーション システム管理・ 外部サービス連携 拡張機能(カスタマイ ズ/プラグイン)基盤 レコード基盤 私はここ
  2. 14 複数のコンテキストを含みクラスが肥⼤化 アプリ設定機能 カテゴリー設定に関する コントローラー カテゴリー設定更新処理 アプリ利⽤機能 レコードに関する コントローラー カテゴリー情報取得処理

    カテゴリーデータクラス カテゴリーに関するサービスクラス カテゴリー設定取得処理 アプリを⾒る⼈ アプリを設定する⼈ クラスが肥⼤化 カテゴリー情報を取得 カテゴリーの設定‧取得
  3. 15 影響範囲の調査に時間がかかる アプリ設定機能 カテゴリー設定に関する コントローラー カテゴリー設定更新処理 アプリ利⽤機能 レコードに関する コントローラー カテゴリー情報取得処理

    カテゴリーデータクラス カテゴリーに関するサービスクラス カテゴリー設定取得処理 アプリを⾒る⼈ アプリを設定する⼈ カテゴリー情報を取得 カテゴリーの設定‧取得 ⼀⽅の変更が もう⼀⽅に影響を
  4. 16 新⼈メンバーにとって理解が難しいクラス アプリ設定機能 カテゴリー設定に関する コントローラー カテゴリー設定更新処理 アプリ利⽤機能 レコードに関する コントローラー カテゴリー情報取得処理

    カテゴリーデータクラス カテゴリーに関するサービスクラス カテゴリー設定取得処理 アプリを⾒る⼈ アプリを設定する⼈ アプリの利⽤‧設定 両⽅の理解が必要 カテゴリー情報を取得 カテゴリーの設定‧取得
  5. 20 機能が関わらないコードは依存させない アプリ設定機能 カテゴリー設定に関する コントローラー カテゴリー設定更新処理 アプリ利⽤機能 レコードに関する コントローラー カテゴリー情報取得処理

    カテゴリーデータクラス カテゴリーに関するサービスクラス カテゴリー設定取得処理 アプリを⾒る⼈ アプリを設定する⼈ ⼀つのクラスが 複数のユースケースを担う ↓ • 可読性の低下 • 影響範囲の拡⼤ • 検討事項のコスト拡⼤
  6. 21 機能が関わらないコードは依存させない カテゴリー設定に関する コントローラー カテゴリー設定更新処理 kintone.appsettings kintone.app レコードに関する コントローラー カテゴリー情報取得処理

    カテゴリー情報 データクラス カテゴリーの利⽤に関する サービスクラス カテゴリー設定取得処理 カテゴリー設定情報 データクラス カテゴリーの設定に関する サービスクラス 関わらせない ❌ ❌ アプリ設定機能 アプリ利⽤機能
  7. 22 機能が関わらないコードは依存させない カテゴリー設定に関する コントローラー カテゴリー設定更新処理 kintone.appsettings kintone.app レコードに関する コントローラー カテゴリー情報取得処理

    カテゴリー情報 データクラス カテゴリーの利⽤に関する サービスクラス カテゴリー設定取得処理 カテゴリー設定情報 データクラス カテゴリーの設定に関する サービスクラス 関わらせない ❌ ❌ アプリ設定機能 アプリ利⽤機能
  8. 27 カテゴリーに関する機能をアプリ設定⽤パッケージ内に配置 アプリを⾒る⼈ アプリを設定 する⼈ カテゴリー情報を取得 カテゴリーの設定‧取得 appsettings.category パッケージ アプリ設定⽤のパッケージを作成し、機能のサブパッケージを作成

    機能に対応するコードは、対応する機能のパッケージ内に存在している状態 RecordController CategoryService CategoryData CategoryController カテゴリーに関するクラスは カテゴリー⽤パッケージに配置
  9. 28 依存関係のルールを違反するユースケース アプリを⾒る⼈ アプリを設定 する⼈ appsettings.category パッケージ RecordController CategoryService CategoryData

    CategoryController アプリを⾒る⼈の ユースケースが 依存関係のルールを違反 アプリの設定に関する ユースケースのため依存可能 カテゴリー情報を取得 カテゴリーの設定‧取得
  10. 29 アプリの設定パッケージ外からの参照を許可するインタフェースを作成 データクラスも分割 CategorySettingService CategorySettingData CategoryInfoService CategoryInfoData Impl DI を⽤いて

    依存を注⼊ <Interface> CategoryController appsettings.category パッケージ アプリを⾒る⼈ RecordController カテゴリー情報を取得 カテゴリーの設定‧取得 アプリを設定 する⼈
  11. 30 ArchUnitを⽤いて依存を禁⽌ CategorySettingService CategorySettingData CategoryInfoService CategoryInfoData Impl <Interface> CategoryController appsettings.category

    パッケージ アプリを⾒る⼈ RecordController カテゴリー情報を取得 カテゴリーの設定‧取得 ArchUnitを⽤いて 依存を禁⽌ ❌ アプリを設定 する⼈
  12. 32 コード分割後の形 CategorySettingService CategorySettingData CategoryInfoService CategoryInfoData Impl <Interface> CategoryController category

    アプリを⾒る⼈ RecordController appsettings パッケージ アプリ設定⽤パッケージ その下に機能毎のパッケージ を作成 機能に対応するコードはすべて 対応するパッケージ内に配置 アプリを設定 する⼈
  13. 33 コード分割後の形 CategorySettingService CategorySettingData CategoryInfoService CategoryInfoData Impl <Interface> CategoryController category

    アプリを⾒る⼈ RecordController appsettings パッケージ インタフェース‧データクラスを 作成 アプリを設定 する⼈
  14. 37 影響範囲がわかりやすくなった カテゴリー設定⽤REST API開発 カテゴリー情報を取得 カテゴリーの設定‧取得 appsettings.category CategoryInfoService CategoryInfoData Impl

    <Interface> アプリを⾒る⼈ アプリを設定 する⼈ RecordController CategorySettingService CategorySettingData CategoryController CategorySettingAPI Service ⼿を⼊れるべき範囲が明確 影響がないことが明確
  15. 38 影響範囲がわかりやすくなった カテゴリー設定⽤REST API開発 appsettings.category CategoryInfoService CategoryInfoData Impl <Interface> アプリを⾒る⼈

    アプリを設定 する⼈ RecordController CategorySettingService CategorySettingData CategoryController CategorySettingAPI Service パッケージ外から意図してな い使⽤をされる⼼配がない カテゴリー情報を取得 カテゴリーの設定‧取得
  16. 39 影響範囲がわかりやすくなった appsettings.category CategoryInfoService CategoryInfoData Impl <Interface> アプリを⾒る⼈ アプリを設定 する⼈

    RecordController CategorySettingService CategorySettingData CategoryController 参照箇所を辿ることで 影響範囲の把握が可能 実装クラスに変更がある カテゴリー情報を取得 カテゴリーの設定‧取得
  17. 42 クラスの責務が明確になった • 例: カテゴリー CategoryService CategoryData カテゴリー情報を取得 カテゴリーの設定‧取得 •

    CategoryData get() • CategoryData getIfActive() アプリを⾒る⼈ アプリを設定 する⼈ RecordController CategoryController それぞれのメソッドに違う⽬的 → 認知負荷が⾼く クラスの可読性が低下
  18. 43 クラスの責務が明確になった 例: カテゴリー それぞれのクラスのアクターが分割され、クラス‧メソッドの責務が明確に → 各クラスのコードを読む際に考慮するユースケースが混ざらない CateogrySettingService CategorySettingData CateogryInfoService

    CateogryInfoData Impl • CategoryInfoData getIfActive() • CategorySettingData get() <Interface> アプリを⾒る⼈ アプリを設定 する⼈ RecordController CategoryController appsettings.category カテゴリー設定に関する クラス‧メソッド
  19. 44 クラスの責務が明確になった 例: カテゴリー CateogrySettingService CategorySettingData CateogryInfoService CateogryInfoData Impl •

    CategoryInfoData getIfActive() • CategorySettingData get() <Interface> アプリを⾒る⼈ アプリを設定 する⼈ RecordController CategoryController appsettings.category メソッドが移動 それぞれのgetメソッドの満たしたいユースケースが明確に クラスも⼩さく簡潔に → クラスの可読性が向上
  20. 46 クラスの責務が明確になった もしカテゴリー設定にメモ機能が追加されると CateogrySettingService CategorySettingData appsettings.category CateogryInfoService CateogryInfoData Impl •

    CategoryInfoData getIfActive() • CategorySettingData get() • List<Category> categories • String memo 新しいプロパティを追加 → パッケージの外に 影響がない CategoryController RecordController 実装時にカテゴリーの設定以外を 考えなくて良い <Interface> アプリを⾒る⼈ アプリを設定 する⼈
  21. 47 クラスの責務が明確になった もしカテゴリー設定にメモ機能が追加されると CateogrySettingService CategorySettingData appsettings.category CateogryInfoService CateogryInfoData Impl •

    CategoryInfoData getIfActive() • CategorySettingData get() • List<Category> categories • String memo CategoryController RecordController <Interface> アプリを⾒る⼈ アプリを設定 する⼈ メソッドを修正 → 影響範囲が抑えられる 機能追加‧修正による影響範囲を抑えられる クラスの肥⼤化も抑制
  22. 48 クラスの責務が明確になった もしカテゴリー設定にメモ機能が追加されると CateogrySettingService CategorySettingData appsettings.category CateogryInfoService CateogryInfoData Impl •

    CategoryInfoData getIfActive() • CategorySettingData get() • List<Category> categories • String memo CategoryController RecordController <Interface> アプリを⾒る⼈ アプリを設定 する⼈ 修正箇所が素早くわかる 必要以上の調査が不要
  23. 59 現在のテストでは、確実に他領域を壊していないと判断できない アプリを設定 する⼈ CateogrySettingService CategorySettingData CateogryInfoService CateogryInfoData Impl <Interface>

    CategoryController appsettings.category アプリを⾒る⼈ RecordController 全体に対するテスト アプリ設定領域テスト 変更差分 パッケージの外に 影響が及ぶ可能性
  24. 60 現在のテストでは、確実に他領域を壊していないと判断できない アプリを設定 する⼈ CateogrySettingService CategorySettingData CateogryInfoService CateogryInfoData Impl <Interface>

    CategoryController appsettings.category アプリを⾒る⼈ RecordController 全体に対するテスト アプリ設定領域テスト このクラスの振る舞いが壊れてない ことを検査すれば⼗分 パッケージ外への振る舞い
  25. 61 現在のテストでは、確実に他領域を壊していないと判断できない アプリを設定 する⼈ CateogrySettingService CategorySettingData CateogryInfoService CateogryInfoData Impl <Interface>

    CategoryController appsettings.category アプリを⾒る⼈ RecordController 全体に対するテスト アプリ設定領域テスト メソッドのリファクタ テストの拡充に取り組み中 パッケージ外への振る舞い
  26. 62 影響範囲の部分に絞ったテストを実⾏することが可能 アプリを設定 する⼈ CateogrySettingService CategorySettingData CateogryInfoService CateogryInfoData Impl <Interface>

    CategoryController appsettings.category アプリを⾒る⼈ RecordController 全体に対するテスト アプリ設定領域テスト 新しく拡充するテスト 変更差分が閉じている場合 影響範囲の部分に絞ったテストを実⾏することでテストの実⾏時間短縮を実現できそう