A1A.dev

 Look for a new angle

見積明細フォームを支える技術

November 18, 2019 · 8 min read

A1Aのhimanoaです。今回は「見積明細を支える技術」ということで、RFQクラウドにおいてテナント毎に異なる見積明細の仕様をどうやって一つの実装と形式で運用しているのかを作り直しの歴史を添えて解説したいと思います。

テナント毎に違う見積明細とは

テナント毎に違う見積明細とはどういうことでしょうか?この疑問は RFQクラウドのFAQサイトに書いてあります。以下はその引用です。

Q. 見積フォーマットの設定は自由にできるのでしょうか?
A. 見積フォーマットの保持上限はありません。
また、カスタマイズは無償で行っていただくことが可能です。
※トライアル時点でいくつかフォーマットを試していただき、使用感を確認いただくことも可能です。

カスタマイズを無償で行える必要があり、そのために開発工数がかかるのでは開発者の手間がかかりサービスを開発する速度も落ちてしまいます。

そのため、一つの実装でカスタマイズ可能なフォームを提供する夢のような実装が必要でした。

満たさないといけない要件

さて、夢のような実装が必要なのはお分かり頂けたところで、夢のような実装で何を満たさないといけないかについて解説していきます。
見積明細をサービス利用者が入力するというのは我々が提供する価値のコアであり、使い心地を高めるために「満たさないといけない要件」が複数存在します。

リアルタイムバリデーション

この記事の一番上の画像を見るとわかるのですが、一度の見積回答で入力できる入力項目は73個に及びます。
この73という数は上記の画像のフォーマットの項目数ですので、フォーマットのカスタマイズ次第では増えたり減ったりします。

この数のフォーマットをアプリケーションのサポートなしにすべて適切な値で埋めるのは人間にはとても難しく、アプリケーションによる何かしらの支援が必要になります。

そこで我々が考えた入力支援は、入力した時点で入力値がおかしいなら間違っていることを通知できる仕組みでした。

いわゆる現代的なサイトのフォームでは当たり前になってきている入力値のバリデーション実装です。
フォームの入力項目毎にどのバリデーションを使うかをフォーマット作者が設定できるような機構を作る必要がありました。

入力結果を自動計算しフォームの入力値にする

上記の入力支援の話ですが、RFQクラウドの入力支援がもう一つあります。 ご覧のGIFの通り複数のフォームの入力値を元に計算を行いその結果を反映することができます。

入力項目が多い中、人間に足し算や掛け算をさせつつ整合性あふれる入力をさせるのも酷なので存在する機能です。

またこの機能が存在することにより人間が暗算するには難しい計算結果を見積明細に落とし込むこともできます。テクノロジー様様ですね。

設定で見積明細フォームの並び方を制御できる

見積明細フォームで入力できる項目をいじることができるようになっても、昔流行った100の質問みたいに項目のグルーピングもできず、ただひたすらに縦に長いだけのものであれば、使いやすさを損ねるでしょう。

かといってケースバイケースにCSSを書いて企業毎のカスタマイズに対応している時間は残念ながら我々にはありません。なにせ新しいフォーマットを試したいのに遅いフロントエンドのビルドを待つのもつらいですよね。

そのためフォーマットの設定である程度見た目を弄れるようにする必要がありました。

見積明細を扱う部分を作り直した経緯

次に見積明細を扱う部分を作り直した経緯についてです。

既存のシステムでは上記で紹介した要件を満たしていましたが、次の問題点を抱えていました。

  • 度重なるスピード開発の結果、これ以上の機能改善が困難になってしまっていました。
  • 内部構造が複雑で混沌を生んでおりデグレーションしやすくなってしまい、安定性や品質に懸念が発生していました。
    • アプリケーションは一回作って終わりではなく、顧客の要求を元に改善していくものなので、デグレが起こりやすく改修コストが高いのはあまり面白くないです。
  • 見積明細フォームを構成するフォーマットに実装都合が露骨に表出してしまいフォーマットの作成といった運用コストも現実的じゃなくなる懸念が存在していました。
    • react-jsonschema-formベースでフォームのレイアウトはjsonschemaでフォーマットが作成されていて、入力結果を元にした自動計算はjsonschemaとは別のフォーマット形式で実装されていました。
    • 自動計算用のフォーマットは次のようなものを人間が手作りしてました。
    • 計算式だけフォーマットが別になっていると、フォームのレイアウトフォーマットでidを変更した時に追従して変更できないので、運用でデグレが発生する可能性があります
  • また、計算式をもっと簡単にかけるようにしたかったのと、Excelのセル移動のような移動方法を実現したかった
    • この要件をreact-jsonschema-formをハックして実現するのは難しそう

この時点でサービスは本番稼働してなかったのですが、本番稼働まで3ヵ月というスケジュールになっていました。
また、本番稼働後にリプレースしても適切にマイグレーションできる自信がなかったのでなんとしても本番稼働までにリプレースを完了する必要がありました。

自動計算フォーマット

自動計算フォーマットと言う謎の単語が聞こえてきていて????になっていると思いますが、既に朽ちた概念です。もともと社内では計算式を組み込むために次のような構造を保持して、JavaScriptで解析して計算機能を実装していました。

{
  target: 'materialPrice',
  formula: {
    formulaFactor1: {
      formula: {
        formulaFactor1: {
          path: 'outputWeight'
        },
        formulaFactor2: {
          formula: {
            formulaFactor1: { path: 'unitPrice' },
            formulaFactor2: { value: 1000 },
            operator: 'times'
          }
        },
        operator: 'times'
      }
    },
    formulaFactor2: {
      formula: {
        formulaFactor1: {
          formula: {
            formulaFactor1: {
              path: 'outputWeight'
            },
            formulaFactor2: {
              path: 'inputWeight'
            },
            operator: 'minus'
          }
        },
        formulaFactor2: { path: 'scrapUnitPrice' },
        operator: 'times'
      }
    },
    operator: 'minus'
  }
};

これで複雑な式を記述していたら日が暮れて深夜残業代が発生してしまって耐えられないので、リプレースをするにあたってもっとヒューマンリーダビリティが高い記述方式を採用する必要がありました。

設計

理想なきリプレースは自己満足しか産まないんで、アプリケーションコードとして、システム運用するとしてどうすれば上手くいくのかというのを最初に考えてからコードを書き始めました。

理想として置かれたポイントは次の通りです。

  • アプリケーションコードとは疎結合であること
    • アプリケーションが見積明細フォームを扱う場所は複数種類存在しそれぞれユースケースが違います。そのため、特定の特定のビジネスロジックとは疎結合にしつつ取り回しがしやすい形にする
    • リプレース後のコードでは見積明細を扱うコードを特定のフォーマットのJSONを入力にReactコンポーネントをレンダリングされるライブラリとして実装しています。
  • 計算式フォーマットとレイアウトフォーマットを一つに統合する。
    • デモ運用の段階でも計算式フォーマットのidとレイアウトフォーマットが指してるidが違うなどでデグレーションしていた。
    • 一緒に扱ったほうがいいデータを別々に扱うのは難しい。

ここまできたらあとは実装するだけなので最初にフォーマットをSlackに投下してレビューと言う名の承認欲求を満たして合意を取ってから実装に移りました

実現できたこと

設計時点で考えたことは実現できました。具体的には次のとおりです

  • アプリケーションコードと疎結合であること
    • アプリケーションのユースケースによってどのコンポーネントで描画するかが変わります。
    • 読み込みのみな物と書き込みありな物
    • 外から注入するコンポーネントを変えるだけで実現可能になりました
  • 計算式フォーマットとレイアウトフォーマットの統合
  • 計算式の記述を簡単にする
    • 特定のフォームの値を元に計算をする、みたいなケースが存在していて、普通の電卓を作るだけだと要求を満たせませんでした
    • どうしてもJSON以上プログラミング言語くらいの表現方法じゃないとメンテできそうになかったのでlispの実装を追加しました
    • (+ 1 (p "unit_price")) と書けるようになった。本当は pythonやlua のサブセットみたいなのが欲しかったのですが、時間的都合などがありLispを採用しました

フォーマットの読み書きを人間に優しい形にすることができたのですが、思ったよりカスタマイズ要件が複雑なフォーマットが実運用で多発しており、実現はできるが設定に時間がかかるみたいな問題もあります。
BtoBSaaSの洗礼というやつですね。ただこの問題は使いやすいエディタを別途作ることが決定するのでそのうち解決するでしょう。

おわりに

「創業一年会社で負債解消とか事業のスピード落としていけるの余裕ありすぎなのでは?」とか煽られそうな記事でしたが、リリース前に取り返しの付かない負債を作らないように、説得しなんとかするのは僕は開発者の責任だと自負しております。

アプリケーションは一度作って終わりじゃないので今後も見据えて選択肢を減らさない選択をしていく必要があるのですが、稼働前の忙しい時期にこういった根本を覆す実装をするのは難しいという現実も存在します。
それを理解した上で通してくれた関係各所や会社には感謝してもしきれないですね。ありがとうございました。

またA1Aでは継続してサービスやコードを改善していくことや、システムの負け筋を潰すことが好きなエンジニアを募集しております。奮ってご応募ください。応募フォームはこちらです