スーパーレガシーな商品発注システムをAngularとLaravelで作り直した話

こんにちは、エンジニアの濱崎です。最近、30歳を目前にしてようやく自動車免許を取りました。合格発表の瞬間はかなり緊張して、大学受験のときを思い出しました。DIYが趣味なので、ホームセンターで買った大きな木材を車で持って帰れるのが嬉しいです。

さて、クラシコムでは最近(2017年12月)、商品発注システムをリニューアルしました。リニューアル前に使っていたシステムは、社内にエンジニアがいない時代に外注して作ったものでした。長年クラシコムのECを支えてくれた重要なシステムではあったのですが、このシステムの中身について知っている人は社内に一人もおらず、機能の追加・改善を自由に行えない状況で、大きなリスクになりつつありました。

手がつけられなくなる前に、今後のビジネスの成長に合わせて継続的に機能追加・改善をしていける環境を作るため、社内でゼロから作り直すプロジェクトが始まりました。

商品発注のフロー

クラシコムでは、商品の仕入れ・在庫管理・商品開発などを担当しているMDグループが発注システムを使って仕入元に商品を発注し、最終的に商品が物流倉庫に納品されます。システムを使った発注フローは大まかに以下のようになっています。

  1. 直近の売上データを元に、商品をABC分析(Cronで毎朝8時に実行)
  2. 商品在庫データを元に、発注すべき商品と発注数を自動推薦
  3. 推薦データを元に人力で微調整し、マネージャーが承認
  4. 発注書のPDFを生成して仕入元にメールで送信
  5. 仕入元から返ってきた発注請書を元に、納品数・納品日を入力
  6. 納品データをCSVに出力して物流倉庫のシステムで取り込む

旧システムを紐解いてみたら

プロジェクトを始めるに当たって、旧システムについての簡単な仕様書はあったものの、中身の実装については完全にブラックボックスだったので、まずはソースコードやDBスキーマ、インフラの構成などを一つずつ紐解いていきました。いわゆる、リバースエンジニアリングという作業です。

旧システムでは、Webサーバー・MySQL・メールサーバーなどが全て1台のさくらVPS上で動いていました。一旦はこれらをAWSのインフラ上で動かすようにして、徐々にリファクタリングしていくという案もあったのですが、ソースコードやDBが想像以上にレガシーだったので、「ゼロから作り直した方が絶対早い」という結論に至りました。

  • ディレクトリやClassのネーミングが、仕様書に書いてある画面IDをベースにしたものになっており、仕様書と照らし合わせないと意味が分からない 😭
  • 依存ライブラリの管理にパッケージマネージャーが使われておらず、手動でダウンロードしたライブラリがルートディレクトリにそのまま置かれている。バージョンや依存関係の解決ができない 😭
  • Classの中で別のClassをnewしていて、特定のClassに依存した密結合な実装になっている。そのため、ユニットテストでMockを使えない 😭
  • 同じようなカラムがいろんなテーブルにある 😭
  • テストコードがない 😭

さらに、旧システムではFAXを使って仕入元に発注書を送信していました。小売の業界では、今でもFAXが結構使われているようです。PDFファイルを添付してメールを送ると相手にFAXとして届けてくれるWebサービスがあり、旧システムではそれを使っていました。

また、重たい処理も全て同期的に処理していたので、ブラウザのボタンを押してサーバーにリクエストを送ったら20分以上画面を触れないみたいなこともありました。

どうやって作り直したのか

プロトタイピング

リバースエンジニアリングによって旧システムの作りは大体分かりましたが、実際にどのように使われているのか分からなかったので、MDグループの人が使っているところを見せてもらったり、関係者にヒアリングをして情報を集めました。

ゼロから作り直すことになったので、旧システムの仕様は前提にせず、「そもそも何がやりたいのか」から考え、必要な機能を洗い出していきました。機能の洗い出しが終わったら、Adobe XDでプロトタイプを作って関係者に共有。プロトタイプに対してフィードバックをもらって修正を重ねることで、実装後の後戻りを少なくできたように思います。

Angular + Laravel

旧システムでは、サーバーへのリクエストの度にHTMLを生成して返していましたが、新システムではAngular4のSPA + LaravelのREST APIという構成にしました。フロントエンドの技術選定は悩みましたが、以下のような理由からAngularを選択しました。

  • デフォルトでTypeScriptに対応している
    • やっぱり型付き言語はIDEの補完機能が強力なのが良い。
  • DI (Dependency Injection)
    • Angularには強力なDI機能があります。クラシコムではLaravelでDIをガッツリ使っていて、テストのしやすさや実装の柔軟性などにおいてメリットを感じていたので、JSでも同じようにDIを導入できるのは魅力的でした。
  • フルスタック
    • Webアプリケーションに必要な機能が一通り揃っているので、すぐに開発を始められます。Angular CLIも便利。
  • Angular Material
    • Material DesignをAngularのComponentとして使えるようにしたもの。GoogleのAngularチームが公式で作っているので安心して使えます。管理画面のUIであれば、Angular MaterialのComponentを組み合わせるだけでもある程度の画面を作れると思います。
  • Moduleシステム
    • Feature(機能)ごとにModule化でき、アプリケーションの規模が大きくなってもコアの部分に影響を与えずに機能追加できます。

APIについては、Laravelで作った社内システム用のAPIが既にあったので、そこに商品発注用のエンドポイントを追加する形で実装しました。

FAX廃止 🎉

全ての仕入元(100社以上!)にお願いをして、発注書のやりとりをメールでできるようにしてもらいました。お陰でFAX送信機能を実装しなくて済みました。

作り直した結果

新システムは、AngularのSPAとLaravelのREST APIで実装し、上図のようなAWSの基本的な構成になっています。発注書のPDFファイルを生成して仕入元にメールを送信するタスクはコストがかかるので、LaravelのQueueを使ってバックグラウンドで処理しています。Queueのジョブでは、laravel-dompdf を使ってPDFを生成しS3に保存、SESで仕入元にメールを送信しています。

発注書を送信する前にマネージャーの承認を受ける必要があるのですが、以前はメールや口頭で承認の依頼をしていました。クラシコムでは全社的にSlackを使っているので、システムで承認依頼を出したらSlackで通知が届くようにしました。

また、リリース時には旧システムから新システムへのデータ移行が必要だったので、LaravelのArtisanコマンドとして移行処理を実装しました。忘年会の日の朝にリリースしたのですが、特に問題なくリリースできたので、忘年会では美味しいお酒を飲むことができました🍺

まとめ

レガシーなシステムを長い間放置しておくと、手を加えるのが難しくなり、後から多くのリソースとリスクを払って再開発することになってしまいます。定期的にリファクタリングをすることで、メンテナンス性を維持していくことの大切さを身をもって感じました。

レガシーなシステムを紐解くことから始まり、最終的にリリースするまでの半年間には大変なことも多々ありましたが、得られたものは大きかったです。特にフロントエンドでは、初めてのComponent指向にチャレンジすることができたのが良かったです。アプリケーションが大きくなるにつれてStateの扱いが複雑になってきているので、ngrx を導入してRedux的なアーキテクチャにしたいと思っています。また、最近別のプロジェクトでVue.jsも使い始めたので、Angularで開発した経験を活かせるはずです。

エンジニアを募集しています

クラシコムではAngularとLaravelをメインに開発しています。AngularやLaravelが好きなエンジニアの方がいらっしゃったら、ぜひ一度、オフィスに遊びに来てみてください!

株式会社クラシコム's job postings
11 Likes
11 Likes

Weekly ranking

Show other rankings
If this story triggered your interest, go ahead and visit them to learn more