Pathee engineering blog

世界をしなやかに変えるエンジニアたちのブログ

STORECASTの管理アプリケーションについて | Pathee Advent Calendar 2021 Day 2

これはPathee Advent Calendarの2日目の記事です

こんにちは。土田です。 実はテックリードも兼務しているので、今日はそちら側の色強めで書きたいと思います。

STORECAST Manager

弊社プロダクトのSTORECASTですが、まだプロダクト自体が手作ぐりなため、管理アプリケーションの検討まで手が回っていない状態でした。 これからSTORECASTを成長させていくためにも、そろそろ管理アプリケーションが必要な感覚があったのと、ちょっと検証したいこともあったので、 10月くらいからSTORECASTの管理アプリケーション作成に着手し始めました。

ちなみに弊社の管理アプリケーションは伝統的(?)に「Manager」が付いているので、 STORECASTの管理アプリケーションも「STORECAST Manager」という名前になりました。

なぜ作るのか?

手探りとはいえ、STORECASTもリリースからこれまで2年ほど開発を続けてきたことで、多くの機能とデータを持つようになってきました。 そうなってくると今持っているSTORECASTの価値を分析、レポートするのもテクニックがいるようになってきましたし、 そもそもメンバーが機能や需要、顧客状況を全体把握するのも難しくなってきているように感じました。

そのため、STORECASTにまつわる各種データを可視化して、そのデータを内部的に活用するためのツールが必要だと判断したのです。

プロダクトミッション

とはいえ、そんなにリソースを当てられるわけではないので、開発は私一人で作業しています。 一人で作業しているとはいえ、社内に報告や協力を求めるためにも、あとは自分がぶれないためにも、プロダクトミッションを設定して周知してあります。

掲げたプロダクトミッションは

「STORECASTの契約状況を可視化し、顧客とのエンゲージメント強化と契約のアップセルに繋げる」

です。

ご契約いただいた方々にSTORECASTを十全にご利用いただくためにも、各担当者が必要な情報を苦労なく確認・認識できる状況を作っていくことを目標にしています。

プロダクトビジョン

ミッションがあれば、行動の原動力にはなるのですが、「じゃあこの結果どうなるんだっけ?」もプロダクトの成果を振り返るときには必要になりますし、 なによりこれも協力を求めるときに必要なので、プロダクトビジョンも以下のように設定してあります。

「STORECAST Managerを使うと、顧客の状況が把握でき、次に取るべきアクションがイメージできる」

このビジョンの実現のため、日々開発を頑張っています💪

どう作るのか?

さて、ではそのついでに何を検証したかったのかというと、

「フロントもバックエンドも全てNext.jsで実装できないか?」

でした。

弊社のシステムは概ね以下の構成になっているのですが、

  • フロントエンド:TypeScript(React.js/Redux)
  • バックエンド:Python(Flask、AWS Lambda)

弊社の規模感、開発スタイルでは、フロントもバックエンドも両方担当することになるため、 言語が異なっていると開発のたびにコンテキストスイッチのコストをまま払う必要があるうえ、 採用も大変なので、なんとかしたいなと常々思っていました。

先日も笑い話になったのですが、わかり易い例だと長さを測る方法ですね(.lengthとlen())

そこで去年ぐらいからTypeScriptのフレームワークである、Next.jsとNest.jsでのシステム構築を時折検証していたのですが、 あるときふと「これ管理画面ならNext.jsだけで大丈夫なんじゃね?」となり、検証の機会を伺っていたのでした😈

なので、STORECAST ManagerはフルNext.jsのアプリケーションとして作成しています!

作り方(チラ見せ)

フルNext.jsの管理アプリケーションを実現するためには、Next.jsに以下のパッケージを組み合わせることで実現できました。

  • swr
  • next-auth
  • TypeORM
  • React Hook Form

これらを利用することで最低限アプリケーションに必要な、

といった仕組みが実現できます。

※参考までに現在のpackage.jsonも載せておきます。

{
  "name": "storecast-manager",
  "version": "0.0.1",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint",
    "lint:fix": "eslint src --ext .ts,.tsx --fix",
    "test": "jest --watch --silent=false --verbose false",
    "test:ci": "jest --ci",
    "typeorm": "node --require ./node_modules/ts-node/register ./node_modules/typeorm/cli.js",
    "storybook": "start-storybook -p 6006",
    "build-storybook": "build-storybook"
  },
  "dependencies": {
    "@coreui/chartjs": "^3.0.0",
    "@coreui/coreui": "^4.0.5",
    "@coreui/icons": "^2.1.0",
    "@coreui/icons-react": "^2.0.0",
    "@coreui/react": "^4.0.2",
    "@coreui/react-chartjs": "2.0.0",
    "@coreui/utils": "^1.3.1",
    "bcrypt": "^5.0.1",
    "bootstrap": "^5.1.3",
    "bootstrap-icons": "^1.7.1",
    "chart.js": "^3.6.0",
    "dayjs": "^1.10.7",
    "isomorphic-unfetch": "^3.1.0",
    "mongodb": "^3.6.12",
    "mysql2": "^2.3.3",
    "next": "12.0.4",
    "next-auth": "^3.29.0",
    "react": "^17.0.2",
    "react-bootstrap": "^2.0.2",
    "react-bootstrap-icons": "^1.6.1",
    "react-dom": "^17.0.2",
    "react-feather": "^2.0.9",
    "react-hook-form": "^7.20.0",
    "recoil": "^0.5.2",
    "reflect-metadata": "^0.1.13",
    "sass": "^1.43.4",
    "simplebar-react": "^2.3.6",
    "slick-carousel": "^1.8.1",
    "smooth-scroll": "^16.1.3",
    "swr": "^1.0.1",
    "typeorm": "^0.2.41",
    "uuid": "^8.3.2"
  },
  "devDependencies": {
    "@babel/core": "^7.16.0",
    "@babel/plugin-proposal-decorators": "^7.16.4",
    "@storybook/addon-actions": "^6.3.12",
    "@storybook/addon-essentials": "^6.3.12",
    "@storybook/addon-links": "^6.3.12",
    "@storybook/react": "^6.3.12",
    "@types/bcrypt": "^5.0.0",
    "@types/node": "^16.11.8",
    "@types/react": "^17.0.35",
    "@types/react-dom": "^17.0.11",
    "@types/styled-components": "^5.1.15",
    "@typescript-eslint/eslint-plugin": "^4.32.0",
    "@typescript-eslint/parser": "^4.32.0",
    "autoprefixer": "^10.4.0",
    "babel-loader": "^8.2.3",
    "babel-plugin-styled-components": "^1.13.3",
    "eslint": "^7.32.0",
    "eslint-config-next": "^11.1.2",
    "eslint-config-prettier": "^8.3.0",
    "eslint-plugin-import": "^2.25.3",
    "eslint-plugin-prettier": "^4.0.0",
    "eslint-plugin-react": "^7.27.0",
    "import-sort-parser-typescript": "^6.0.0",
    "npm-check-updates": "^12.0.2",
    "prettier": "^2.4.1",
    "styled-components": "^5.3.3",
    "stylelint": "^13.13.1",
    "stylelint-config-prettier": "^9.0.3",
    "stylelint-prettier": "^1.2.0",
    "ts-node": "^10.4.0",
    "typescript": "^4.5.2"
  }
}

正直、技術選定が終われば、作り方も何も後は実装するだけなのですが、元々各種採用技術に精通していたわけではないので、色々とハマリポイントはありました。 そういった各仕組みの実現手順やノウハウは、ここに書くには時間がなさすぎるので、残りのAdvent Calendarで一つずつ記載する予定です。

今後の私(のやる気)にご期待ください!