Monolith→MultiRepo→MonoRepoでの
リポジトリ戦略
株式会社SCOUTER @kotamat
誰
- kotamat
- SCOUTER CTO
- サーバー、インフラ寄り
- 🚴♂️
だけkotamats
.
├── src
│ ├── main.ts # エントリーポイント
│ ├── dist # 呼び出し元から呼び出すjsファイル
│ │ └── output.js
│ ├── components # コンポネント集
│ │ └── a.vue
│ └── pages # ページ集
│ └── index.vue
└── webpack.config.js
import PackageIndex from "./pages/index.vue";
import { PluginObject } from "vue";
interface OptionI {
masterData: object
httpClient: object
}
export const Package = {
install(Vue: PluginObject<OptionI>, opts: OptionI) {
// Register setting values
MasterData.set(opts.masterData)
Ajax.use(opts.httpClient)
// Register components
Vue.component('package-index', PackageIndex);
},
}
転職にまつわるサービスを展開
エージェント
転職者
求人企業
転職にまつわるサービスを展開
エージェント
転職者
求人企業
エージェントを
免許無しで誰でも
できるように
転職にまつわるサービスを展開
エージェント
転職者
求人企業
エージェント向け
業務管理 + 求人DB
転職にまつわるサービスを展開
エージェント
転職者
求人企業
面接・履歴書では
わからない転職者の
リファレンスチェック
今までLaravueでやってきた
しゃべる内容
- リポジトリ戦略とは?
- 各リポジトリ戦略の紹介
- 事例紹介
- モノリポ化のメリデメ
- まとめ
リポジトリ戦略?
SCOUTERの場合
関係するユーザが多い
採用企業
管理者
エージェント
転職者
以前はこんなこと言ってた
それぞれのユーザで同ドメインの情報を扱う
同ドメインの処理の編集が頻出するため
その領域は責務を凝縮する必要がある
ユーザベース +ドメインベースのリポジトリ分割へ
採用企業
管理者
求人
選考
TODO
エージェント
コードベース拡大に伴い重複コードの扱いが大変になってきた
SCOUTERではリポジトリ戦略をいくつか取り入れてきた
- Monolithc
- MultiRepo
- MonoRepo←New!
Monolithic
いわゆる"一般的な"構成
- LaravelAppの初期インストールコマンドで入る構成
.
├── app
├── artisan
├── bootstrap
├── composer.json
├── composer.lock
├── config
├── database
├── nohup.out
├── package.json
├── phpunit.xml
├── public
├── readme.md
├── resources
├── routes
├── server.php
├── storage
├── tests
├── vendor
└── webpack.mix.js
フロントもサーバーサイドもすべて同じリポジトリに入っている。
関係するユーザが多い
採用企業
管理者
エージェント
転職者
複数アプリケーションでコードシェアができない
- 重複コードがそれぞれのアプリに記述される
- 機能変更に伴い各アプリケーションのコードを変更しなければならない。
- 結果過去のコードが残ってしまう。
MultiRepo
共通コードを"パッケージ化"
- 重複コードは単一パッケージ下で管理
- composerやnpmのリポジトリでバージョン管理
.
├── app
...
├── composer.json
├── composer.lock
├── package.json
├── node_modules
│ ├── @babel
│ ├── @scouterinc
│ │ ├── job
│ │ ├── selection
│ │ └── stylebook
├── vendor
│ ├── scouterinc
...
各アプリケーションで重複するものは
パッケージとして外出し
一見良さそうに見える
開発手順の煩雑化
レビュー前
- 子パッケージを更新する
- 子パッケージをdevビルドでpublish
- 親パッケージのバージョンをdevビルドのバージョンに変更
- 親パッケージをpush
リリース前
- 子パッケージのバージョンをセマンティックバージョンに修正
- publish
- 親パッケージをセマンティックバージョンに修正
- 親パッケージをpublish
ダイヤモンド依存関係の闇
A
B
C
D
A
B
C
D.1
D.2
Aをビルドする際にB,Cそれぞれは別のバージョンのDを参照してしまう。
Monorepo
全ては一つのリポジトリへ
- パッケージと言う概念は残しながら
- Gitのリポジトリは単一化する。
- ルートアプリケーションも一つのパッケージとして扱う
repo/
package.json
packages/
package-1/
package.json
package-2/
package.json
ルートディレクトリには全体のパッケージを、
それぞれのパッケージ内にもそれぞれnpmやcomposerの依存解決ファイルを入れる
モノリポ化で解決できること
- バージョンが単一化される。ダイヤモンド依存の解決
- コードの共有を広範囲にできる。
- 依存性管理の簡潔化
- 全体に波及する部分的な変更が容易になる
- 大規模リファクタリングとモダン化をするツールが作れるようになる。
- 他のチームとの連携ができるようになる。
- チームの境界性の柔軟性が増す
依存性管理の簡潔化
- もはやパッケージのセマンティックバージョン管理は不要に
- 都度都度のテストが通れば最新を使えばいい
- OSSなどの外部に公開するパッケージに関してはバージョンを切るということも行っている
- Laravel→illuminate/*,
- Babel→@babel/babel-*
すべてのパッケージにまたがる機能の名前空間を変えられる。
- 今までであればすべてのパッケージのコードを変えてバージョンを変更しなければならなかった。
- 一斉置換を行うことでテストが失敗せずに全変換が可能に
コード解析が可能に
- どういう変更がバグになりそうかが計測可能になる
- どの言語がどこで使われているか
- 脆弱性対応パッチの適応
チーム間での境界線をコードに依存しないで柔軟に変えられる
チームA
チームB
デメリットは?
1. ビルドやcloneのコスト増大
- どうしてもコードベースは大きくなってしまう。
- ツールの導入や規則で防ぐ
- Bazelなどで変更の合ったところだけをビルド、テストするようにする。
- cloneは先頭だけ引っ張ってくるようにする。
2. 依存性解決が簡単になりすぎる
- 簡単に依存性を追加できるようになるために、不必要な依存が勝手に追加されてしまう懸念がある。
- 必要最低限の依存だけに絞るように規則を作っていく
- どのAPIはどこで使われるのかを明示していく
3. コードが社内に公開されてしまう。
- プロジェクトごとにコードを秘匿にしなければならないところではそもそも使えない。
- 会社の文化として、オープンな社風である必要性がある。
どこで使ってるの?
すでにいろいろな企業で使われている
OSSも
便利なツール
npmパッケージのモノリポに適したもの
全パッケージの一斉publish、コミット、pushなどが簡単にできる。
既存のgit履歴をそのまま使える
Composerベースのモノリポ化ツール。
親リポジトリのcomposer.jsonは自動生成されるため、コミットしない。
こちらも一斉リリースなどができる。
Laravelで使用されているコード分割ツール
Golang製
CLIベースでコードをディレクトリごと分割し、ReadOnlyなリポジトリにタグ付きでpushしてくれる。
OSSでパッケージごとにpuublishしたいときに使える
まとめ
- 事業の特性として、依存性がキーになってくるところでは一考の余地がある。
- イシュートラッカーなどのツールにも影響されるため、開発フロー全体の設計をもとに入れていく。
- 単一リポジトリだけでいいようなプロジェクトでは特段入れる必要はない。