バックエンドの5年分の技術的負債を返済し始めた話
ストレッチ専門店 Dr.stretch を運営する 株式会社nobitel でシステム開発に取り組んでいる Nsi です。
nobitel では「健康 × スポーツ × IT」を掲げ、全国約200店舗(2023年9月現在)の Dr.stretch で使われるお客様・店舗向け業務アプリ「Ficks」を社内で開発しています。
元々2018年に開発がスタートし、紆余曲折あり2021年にリリースしてから継続的な追加開発を行っていましたが、ようやく技術的負債の返済に向けて動き出せる体制になってきたこともありフロントエンド・バックエンド共に時間をかけてリファクタリングに取り組んでいます。
フロントエンドの取り組みについては業務委託の shinnoki さんに記事を書いて頂いていますが、
今回はバックエンドの取り組みについて紹介したいと思います。背景
弊社でエンジニアチームが立ち上がったばかりの頃に制作されたプロダクトが基本的にLaravelを採用してバックエンドを構築しており、その流れでFicksもLaravelを採用することになりました。
しかし開発チームにLaravelやWebフレームワークの設計に詳しいエンジニアがおらず、精々PHP製CMSを触ったことがあるくらいの経験しかないメンバーで開発がスタートしたため、コードの責務を全く意識しないまま「動けば良い」でコードを書き続けてきました。結果、以下のようなコードが大量に生産されることになりました:
- バリデーションのロジックがControllerやRepositoryに散らばっている
- Controller内で認可やデータベースアクセスなど、値を返す以外のロジックが混在している
- Modelにメソッドが全く生えておらず、クエリビルダを組み立てることが出来ない
- Repository内で生SQLがガリガリ書かれている
Laravelの機能を活用せずに見通しが悪いコードを量産していたので、機能追加のたびにバグが発生し改修に追われているような状況でした。またこのままではエンジニアが新規で入っても機能開発を行うまでにキャッチアップしなければならない事が多すぎてしまい成果を出し辛くなってしまうので、きちんと責務を分け見通しを良くすることでチームがスケールしても問題がないような作りに変えていく必要がありました。
またサポートが終わったバージョンを使い続ける危険性は非常に高く、Laravelのバージョンアップについても併せてやっていく必要がありました。
やっていること
Laravel / PHPのバージョンアップ
元々Laravel5系を使っているのでPHPのバージョンも7系で止まっており、以下のような段階を踏んでバージョンアップを進めています:
- Laravel 5系→7系へのバージョンアップ
- EC2 → ECSへの移行
- PHP7系 → 8系へのバージョンアップ
- Laravel7系 → 10系へのバージョンアップ
まずは今のPHPのバージョンの中でLaravelのバージョンを上げられるだけ上げておき、バックエンドの動作環境を切り戻しがしやすいコンテナ環境に移行した上でPHPとLaravelのバージョンを上げるという戦略です。
Laravelのバージョンアップについてはスプレッドシートでアップデートによる差分をまとめ、それぞれ対応が必要なものから順々に確認を進めています。
コンテナベースで管理することでPHPのバージョンアップを伴う環境変化にもついていきやすくなるので、本体のバージョンアップに追随出来るようにしていきたいところです。
コードの改善
開発チーム内でもLaravelに特に知見を持っているエンジニアがいなかったということもあり、まずは聖典を定めてそれに則ってコードを整理しようという結論に至りました。
開発チームでは現在以下のような形でコードの整理を行っています:
- 認可処理はRequestに書く
- 元々聖典に従ってPolicyに書いていたが、認可がモデルでなくAPIと紐付いていたために今はRequestベースに書いたほうが良いと判断した
- 変わる可能性はある
- バリデーションロジックは全てRequestに書く
- ドメインロジックとデータベースアクセスはUseCaseに書く
- 返却する値の整形と値の規定はResourceに書く
- Controllerはそれらを束ねて返す
Laravelを利用するならば当たり前のことかもしれませんが、まずは愚直にこの形に従ってコード整理を行っています。
整理を行ったことで見通しが良くなったのはもちろんですが、特にテストが書きやすくなったのは大きなメリットでした。権限・リクエストのバリデーション・返却する値についてそれぞれテストを書けるようになり、単体テストの量がガッツリ増えました。リファクタリングと並行して機能追加を行う際にも、きちんとテストを書くような文化が出来つつあります。
今後の課題
データベース設計の改善
- 適切な正規化がされていないテーブル
- ドメインモデルが適切に反映されていないテーブル
- 外部キー制約が適切にかかっていないテーブル
- 複合主キーを用いており、サロゲートキーが存在しないテーブル
など、改善すべきテーブルが複数存在しています。特に複合主キーを用いたテーブルはEloquentを使い倒す際に面倒なことになるので早く爆破したい存在です。ここに関しては:
- 構成は同一でサロゲートキーのカラムを追加しただけの新テーブルを作成する
- レコードを同期させる
- テーブル操作を旧テーブルと新テーブルの両方に対して行うようにする
- Modelで参照しているテーブルを切り替える
- 問題が無ければテーブル操作を新テーブルのみ行うようにし、旧テーブルを爆破
という手順で安全に切り戻しが出来るように対応していく予定です。
BFF(Backend For Frontends)の導入
基本的にどの画面も複数のAPIを用いて構成されており、現在は都度APIを叩いて情報を取得しているような状態です。フロントエンドの基盤はNext.jsに移行したのでNext.jsでAPIリクエストを合成したり、GraphQLサーバーを導入したり等、複数のリソースを一括で取得できる手法を導入して表示速度の改善を図っていきたいと考えています。(どちらかといえばフロントエンドの改善に関するトピックかもしれません)
おわりに
フロントエンドの移行と並行してバックエンドのリファクタリングも行っているので開発体制が急にガラッと変わった感じですが、開発メンバーからは以前よりも開発が行いやすくなったとの声を頂いており、エイヤで取り組みを始めて良かったなと感じています。
とはいえ改善はまだまだ道半ばで、使って下さっている社内や社外のスタッフとお客様に素早く安全に価値を届けるにはまだまだこれからといった状況です。
弊社では我々と一緒に改善に取り組んで下さるエンジニアを募集しています!
Discussion