Slide 1

Slide 1 text

SaaS型Webサービス「カオナビ」のチーム開発で Package by Featureを取り入れた話 株式会社カオナビ プロダクトデベロップ本部 @fujinon39 藤野崇志 © kaonavi, inc.

Slide 2

Slide 2 text

自己紹介 © kaonavi, inc. 2 略歴 - ゲーム会社でサーバエンジニアとして開発に 従事しつつ、執筆活動や社内勉強会の発足、 関西ゲーム勉強会の実行委員などを経験 - 2022年から株式会社カオナビで、 🐅、🐳、🐇、🦘チームを渡り歩きつつ、 Project Leadとして開発に従事 @fujinon39 藤野 崇志 (ふじのたかし)

Slide 3

Slide 3 text

● Package by Featureとは ● チームにPackage by Featureを取り入れた背景 ● Package by Featureを取り入れた開発での悩み ● Package by Featureを取り入れた開発の成果 ● 今後の方針 もくじ

Slide 4

Slide 4 text

Package by Featureとは © kaonavi, inc. 4 Package by Featureとは、ディレクトリ構成の 概念です。 明確な起源を探るのは正直難しかったですが、 用語としては、Javaのpackage由来だと思われ ます。 Javaの記事が2006年くらいに上がっていたの で、時期的にも最初だろうと思われています。 概念としては、2000年代に、こちらの書籍でも 取り上げられていて、 ロバート.C.マーチンさんのアジャイルソフトウェ ア開発の奥義ではパッケージ設計の原則につ いての記載がありました。

Slide 5

Slide 5 text

Package by Featureとは © kaonavi, inc. 5 packages ├─ packageA │ ├─ HogeController │ └─ FugaModel │ └─ packageB ├─ PiyoController └─ FooModel Package by Featureとは、 単一の機能に関連する全ての項目を、 単一のディレクトリに配置し、管理する概念で す。 人によっては、設計だったり、ディレクトリ構造 自体のことをPackage by Featureと呼称する場 合があります。 非常にざっくりしたディレクトリ構成で表現しま すと、packageA Bのように機能毎にディレクトリ を分けて配置することを指します。

Slide 6

Slide 6 text

Package by Featureとは © kaonavi, inc. 6 packages ├─ packageA │ ├─ HogeController │ └─ FugaModel │ └─ packageB ├─ PiyoController └─ FooModel layers ├─ controllers │ ├─ HogeController │ └─ PiyoController │ └─ models ├─ FugaModel └─ FooModel Package by Feature Package by Layer Package by Featureを語る上で、 対としてよく語られる概念が Package by Layerです。 Package by Featureが機能毎にまとまっている のに対し、 Package by Layerは、種別毎にまとまっている のが特徴です。

Slide 7

Slide 7 text

Package by Featureとは © kaonavi, inc. 7 Package by Featureのことを、 もっと知るために、図を用意してみました。

Slide 8

Slide 8 text

Package by Featureとは © kaonavi, inc. 8 皆様 何かしらの作業をする時、ファイルを探すことが 多くないでしょうか? 私の場合は、新機能であっても既存の実装を 参考にするためにファイルを探したり、 改善をするときは、改善対象のファイルを探し ています。 ファイルを探すときに、 左の図のように、どこに何があるかわからない と、苦労します。

Slide 9

Slide 9 text

Package by Featureとは © kaonavi, inc. 9 こちらはPackage by Layerをイメージして図を作成しました。 横軸の箱が、同じレイヤーのファイルをまとめたディレクトリです。 同じレイヤーとは、例えば四角形が MVCでいうControllerで、三角形は Modelというイメージです。 処理の流れを考えると、四角形が三角形を利用したり、三角形が丸を利 用したりと、横軸の箱(レイヤー)を跨いだ範囲で開発する必要があるとい うところが特徴です。 また、青の四角が、どの三角を使うか?や、緑の四角はどの三角を使う か?は、それぞれの四角形の実装を見る必要があるため、ディレクトリを 俯瞰したときに得られる情報が少ないことも、特徴です。 まとめると、下記の通りです。 ・レイヤーを跨いで認識する必要がある ・依存関係を把握するにはファイルの中身を見る必要がある

Slide 10

Slide 10 text

Package by Featureとは © kaonavi, inc. 10 こちらは、Package by Featureをイメージして図を作成しま した。 縦軸の箱が、同じ機能のファイルをまとめたディレクトリで す。 ある機能の四角は、その機能内の三角を利用する。 ある機能の三角を、改善したい場合、広い範囲を見る必要 はなく、機能内のディレクトリのみを見るだけで良くなりま す。 皆様が、開発を行う時、機能単位で作業することが多いの ではないでしょうか? その際、機能毎にディレクトリがまとまっていれば、 把握する範囲が明確で、非常にわかりやすいかと思いま す。

Slide 11

Slide 11 text

Package by Featureとは © kaonavi, inc. 11 私の所属するチームでは、 package直下に全ての ファイルを置いてしまうと、膨大なファイル数なので 辛いことが目に見えていました。 そのため、自己流ですが更に分割しています。 簡単なイメージにはなりますが、 package内をレイヤーで分割し、 レイヤーの中をモジュールで更に分割しています。 この程度まで分割されると、 目当てのモジュールの修正を行う時に、ファイルを 探し当てるのが、非常に簡単になります。 また、チームに新しい方が Joinする際に、 モジュールの説明を行うことで、よりスムーズなディ レクトリ把握、実装理解に繋がります。 packages ├─ packageA │ ├─ controllers │ │ ├─ hoge_module │ │ │ └─ HogeController │ │ └─ fuga_module │ │ └─ FugaController │ └─ models │ ├─ hoge_module │ │ └─ HogeModel │ └─ fuga_module │ └─ FugaModel └─ packageB

Slide 12

Slide 12 text

Package by Featureとは © kaonavi, inc. 12 https://medium.com/sahibinden-technology/package-by-layer-vs-package-by-feature-7e89cde2ae3a こちら、エネス・オーラルさんの記事にあった画 像を引用させていただいています。 優れたシステム設計は、パッケージ内の凝集性 が高く、パッケージ間の結合性が低いことが重 要であることを説いていました。

Slide 13

Slide 13 text

Package by Featureとは © kaonavi, inc. 13 https://medium.com/sahibinden-technology/package-by-layer-vs-package-by-feature-7e89cde2ae3a 例えば、 左の箱の機能が、右の箱の関数を利用する場合、 右の箱の改善を行う際に、左の箱の機能を考慮する 必要があります。 私たちの開発は年々巨大化しており、一個人が、全 システム、全機能を、把握し切るのが困難になってい ます。 そんな中、右の箱の関数の利用が、 右の機能に閉じない場合、考慮することが多く、 作業時の、時間、労力、負荷がかかります。

Slide 14

Slide 14 text

Package by Featureとは © kaonavi, inc. 14 https://medium.com/sahibinden-technology/package-by-layer-vs-package-by-feature-7e89cde2ae3a もし、上の図のように基本的に機能単位で閉じ ていることが分かる状態であれば、 把握すべき範囲は機能に閉じるため、認知負 荷も下がり、結果的に開発コストも下がります。 注意点 上の図も1本だけ緑の線が出ています。 Package by Featureは、例外なく機能に閉じる べきという考えではなく、 一定のルールを定めた上で、他の機能を利用 することを想定しています。

Slide 15

Slide 15 text

● Package by Featureとは ● チームにPackage by Featureを取り入れた背景 ● Package by Featureを取り入れた開発での悩み ● Package by Featureを取り入れた開発の成果 ● 今後の方針 もくじ

Slide 16

Slide 16 text

チームにPackage by Featureを取り入れた背景 © kaonavi, inc. 16 カオナビ 既存の機能群 Packages 新機能 私たちのチームは、新機能の開発を指示され ました。 つまり、どこにディレクトリを切るか選択しやす い状態にあります。 開発するスペースは「既存の機能群」と 「Packages」があります。 既存の機能群の特徴 ・長い歴史が積み重なっている ・比較的巨大である ・複数の機能を保持している Packagesの特徴 ・比較的最近つくられ始めた ・Package by Featureの考え方でつくられた

Slide 17

Slide 17 text

チームにPackage by Featureを取り入れた背景 © kaonavi, inc. 17 既存の機能群に新機能を作る場合のメリット ● 既存の機能群への実装は開発者の知見が多い ● 既存の機能群の他の実装を制限なく利用しやすい ○ 同じ処理を改めて書く必要性が薄い ● 開発期間が短いため、知見の多いやり方を選択したい

Slide 18

Slide 18 text

チームにPackage by Featureを取り入れた背景 © kaonavi, inc. 18 Packagesに新機能を作る場合のメリット ● Package by Featureは最近導入されたが、既にいくつかの機能は Packagesに存在するため、参考情報は存在する ● 既存の機能群の利用は、一定のルールを守って利用すれば良い ● 開発期間が短いが、後の追加、修正のコストを下げるには Package by Featureは利点として働く 両者を考えた結果、 Package by Featureを取り入れる方が、 チームの未来に良い影響を与えると考え、 Packagesに新機能を作ることを選択しました。

Slide 19

Slide 19 text

● Package by Featureとは ● チームにPackage by Featureを取り入れた背景 ● Package by Featureを取り入れた開発での悩み ● Package by Featureを取り入れた開発の成果 ● 今後の方針 もくじ

Slide 20

Slide 20 text

Package by Featureを取り入れた開発での悩み © kaonavi, inc. 20 カオナビ 既存の機能群 新機能 Package Package by Featureを利用する上で、  Packageを超えた機能を利用する場合、 一定のルールを守った上で利用することと紹介 しました。 実際、どのようなルールを設けたか紹介させて ください。

Slide 21

Slide 21 text

Package by Featureを取り入れた開発での悩み © kaonavi, inc. 21 既存機能群 新機能 Package 関数 直接利用NG 基本的なルール Packageを超えた関数を利用する際に、直接関数を呼び出すことを禁止としました。

Slide 22

Slide 22 text

Package by Featureを取り入れた開発での悩み © kaonavi, inc. 22 既存機能群 新機能 Package 改善! 影響! 関数 理由ですが、もし既存機能群の関数を修正した場合 該当の関数を利用している Packageは、影響を受けるからです。 影響を受ける場合、関数を改善する立場の人からすると どのような影響が新機能側に発生するか、考慮する必要があり、気 軽な改善が行いにくくなります。

Slide 23

Slide 23 text

Package by Featureを取り入れた開発での悩み © kaonavi, inc. 23 既存機能群 新機能 Package 関数 Interfaceを経由 Interface 基本的なルール 関数を利用する場合、 Interfaceを介して、直接利用は避ける方針となりました。

Slide 24

Slide 24 text

Package by Featureを取り入れた開発での悩み © kaonavi, inc. 24 interface HogeInterface { /** * $fuga_idに紐づくFugaの存在確認 * * @param int $fuga_id * @return bool true:存在する false:存在しない */ public function isExistFuga(int $fuga_id): bool; } phpでのInterfaceの宣言の例です。 関数を利用する側は、 入力値のidと、出力値のboolを保証させることが できます。 つまり、入力値の fuga_idに紐づいた、 Fugaの存在をboolで返すことが分かります。 例え実装側が、中の処理の仕方を変えたとして も、 入力値のfuga_idを受け取り、 Fugaが存在すればtrue, 存在しなければ falseを 返すルールを守っていれば、利用サイドの影響範 囲を無視できます。

Slide 25

Slide 25 text

Package by Featureを取り入れた開発での悩み © kaonavi, inc. 25 擬人化で完全に理解する クリーンアーキテクチャ 2024/02/11 ペチコン関西2024 しまぶ@shimabox https://speakerdeck.com/shimabox/ ni-ren-hua-dewan-quan-nili-jie-suru kurinakitekutiya 他にもInterfaceを利用するメリットがあります。 そちらに関しては、 カオナビの島袋がぺちこん関西 2024で登壇した際の資 料に詳しく載っております。 もしよろしければ、ご参照ください。

Slide 26

Slide 26 text

Package by Featureを取り入れた開発での悩み © kaonavi, inc. 26 基本的なルールはOK ● どうやってルールを守るの? ● 例外はないの?

Slide 27

Slide 27 text

Package by Featureを取り入れた開発での悩み © kaonavi, inc. 27 基本的なルールはOK ● どうやってルールを守るの? ○ deptrac ■ PHP専用の静的コード分析ツール ● 例外はないの? https://github.com/qossmic/deptrac 基本的なルールは以上です。 このルールを守るために、 カオナビではdeptracと呼ばれるPHP専用の静 的コード分析ツールを利用しています。 他の言語の例えば javaでは、メソッドの可視性 が設定できるため、特段のツールは不要です が、PHPにはないため、ツールを導入していま す。 こちらのツールを利用することで、違反している ファイルがあるかどうかを CIで判定しています。

Slide 28

Slide 28 text

Package by Featureを取り入れた開発での悩み © kaonavi, inc. 28 既存機能群 新機能 Package Enum 直接利用OK 例外的なルール Enumは、直接利用OKという方針で開発を進めました。

Slide 29

Slide 29 text

 HogeEnum   TypeA   TypeB   TypeC New PackageC  HogeEnum   TypeA   TypeB   TypeC New PackageB  HogeEnum   TypeA   TypeB   TypeC New PackageA Package by Featureを取り入れた開発での悩み © kaonavi, inc. 29  HogeEnum   TypeA   TypeB   TypeC New 既存機能群 既存機能群にTypeCを追加する場合、 各PackageにもTypeCを追加した上で、追加に よる影響範囲を考慮する必要があります。 既存機能群のEnumを利用する場合でも、 各PackageにEnumを追加する場合でも、 影響範囲の考慮は必要になるので、 TypeCの追加場所は1つの方が良さそう。

Slide 30

Slide 30 text

 HogeEnum   TypeA   TypeB   TypeC New PackageC  HogeEnum   TypeA   TypeB   TypeC New PackageA Package by Featureを取り入れた開発での悩み © kaonavi, inc. 30  HogeEnum   TypeA   TypeB   TypeC New 既存機能群  HogeEnum2   TypeA   TypeB   TypeC New? PackageB もし各PackageにEnumを追加した場合 各PackageのEnumは、同一か類似かの 判断が必要になります。 同一かどうかを判別できる別の仕組みを 考えるより、直接利用する方が開発者に とって幸せだという点から Enumは直接利用OKとなっています。

Slide 31

Slide 31 text

● Package by Featureとは ● チームにPackage by Featureを取り入れた背景 ● Package by Featureを取り入れた開発での悩み ● Package by Featureを取り入れた開発の成果 ● 今後の方針 もくじ

Slide 32

Slide 32 text

Package by Featureを取り入れた開発の成果 © kaonavi, inc. 32 Package内が高凝集になった ● 処理を改善する際の影響範囲が絞られていると感じた ● 認知負荷が下がっていると感じた Package内のディレクトリ構成がわかりやすい ● 目当てのファイルを探しやすくなった ● 部品単位での置き換えがしやすくなった

Slide 33

Slide 33 text

Package by Featureを取り入れた開発の成果 © kaonavi, inc. 33 Package間が疎結合になった ● 新機能とその他の機能の境界が十分に明確 ● Interfaceの入力値、出力値のルールが守られる限り既存機能の 改修時に、新機能への影響がないことが保証された

Slide 34

Slide 34 text

● Package by Featureとは ● チームにPackage by Featureを取り入れた背景 ● Package by Featureを取り入れた開発での悩み ● Package by Featureを取り入れた開発の成果 ● 今後の課題 もくじ

Slide 35

Slide 35 text

今後の課題 © kaonavi, inc. 35 既存機能群をPackagesに移行したい ● 問題点 ○ 既存機能群は巨大で複雑のため、 機能単位の移行だと、時間がかかりすぎる ● 移行に際して ○ API毎の移行がシンプルだと思われる

Slide 36

Slide 36 text

まとめ © kaonavi, inc. 36 Package by Featureはシンプル 明日からやってみよう!

Slide 37

Slide 37 text

ご清聴ありがとうございました!