Questetraの運用負荷が高い状態から脱却するために、アプリ共有アドオンをフル活用している話

記事タイトルとURLをコピーする

前置き

サーバーワークスでは、QuestetraというBPMツールを2015年ごろから利用しています。 BPMとは、Business Process Managementの略で、業務プロセス管理のことを表しています。

似たようなツールとしては、Intra-martや、KickFlowなどがあります。

Questetraで提供されているスクリプト機能

Questetraでは、ユーザが書いたスクリプトをワークフローに組み込む機能である、「スクリプトタスク」機能が提供されています。 これはJavaで実装されたJavaScriptエンジンで多少Javaの型などを意識する必要がありますが、基本は素のJavaScriptのように書くことができます。

うまく使うと、ワークフロー x スクリプトによる業務の自動化をゴリゴリ進めることができ、サーバーワークすでも数多くのスクリプトタスクがワークフローに組み込まれていきました。

出てきた運用・保守上の課題

ある程度スクリプトタスクが増えてきたところで、Questetraの運用・保守に課題が出てきました。

スクリプト実行環境の制限

  • 利用できるJavaScriptのバージョンがかなり古かった(今は改善された)
  • 外部ライブラリ利用不可。SaaSとのリクエストのやりとりなど、JavaScriptの標準ライブラリ、Questetraプラットフォームにより提供されるライブラリのみで頑張る必要がある
  • スクリプトタスクのみを実行できず、ワークフロー全体でデバッグする必要がある
    • さらに、デバッグ時のみSaaSの検証環境を利用するみたいなことが困難である
  • スクリプトをブラウザ内のテキストエディタで書く必要がある

Questetraを運用する上での課題

  • スクリプトタスクを多用するとノードが増え、本来のワークフロー(タスクの流れ)の見通しが悪い
  • スクリプト実行環境のバージョンアップが実施される場合、全アプリのスクリプトタスクのバージョンアップ作業を手作業で一個ずつ対応する必要がある

たくさんありますね。 ちなみになぜサーバーワークスのQuestetraはスクリプトタスクだらけになってしまったのかというと、主に以下のような理由です。

  • 業務フローの中に自動化を組み込んでいる (冒頭で述べた通り)
  • Questetraで他のSaaSの操作をラップしている
    • 内部統制(社員のSaaS権限をなるべく絞り、変更はシステムにさせる)
    • 定型作業の自動化
  • みんなSlack通知が大好き

特にSlack通知は曲者で、メンションを使おうとするとQuestetraのユーザIdをSlackのユーザIdを保持する辞書式のデータセットをワークフロー内に定義したり、その辞書をもとにIdを変換するスクリプトタスクを書いたりと、ワークフローには本来不要な項目・タスクが複数個生まれる厄介なものでした。

本題

というわけで、前置きの部分で述べた課題をアプリ共有アドオンを活用するという、今回の本題に入っていきます。

結論から申し上げると、既存のスクリプトタスクを「アプリ共有アドオン」で代替し、既存のスクリプトの実装内容はアプリ共有アドオンのconfigとして表現するようにしました。 そして、AWS上にConfigを解釈して各種自動化処理を実行できるロジックをAPI Gateway x Lambdaの構成で実装し、アプリ共有アドオン側のスクリプトの仕事はQuestetraのフィールドの読み書きとAPI Gatewayとのデータのやり取りのみに絞るようにしました。

アプリ共有アドオンとは、ワークフロー間で共有して利用できるスクリプトタスクとConfigを一つに固めたような機能です。 この機能を使うことにより、ベースのAPI Gatewayとのやりとりは共通化しつつ、各ワークフローで異なる自動化の内容をConfigとして表現できるようになるためQuestetra上で新規にスクリプトを書くことをやめられるようになりました。

仕組み

全体構成

全体の構成としては、「Questetra - アプリ共有アドオン - API GW - Lambda - 外部SaaS」のような構成となっています。 言語はTypeScriptとして、リポジトリは3つに分けて開発を行いました。

アプリ共有アドオン(codename: 蘇りの石)

Questetraの全ワークフローから利用できる共有のアドオン。XMLで定義。カスタムコード(ECMAスクリプト)を埋め込める。 TypeScriptで実装し、Transpile後のJavaScriptのコードをXMLに埋め込んでいる。

API(codename: ニワトコの杖)

構成の「API GW - Lambda - 外部SaaS」の部分、アプリ共有アドオンからのリクエストを受け取り、各種SaaSの操作を行う。 TypeScriptで実装、Serverless Frameworkでデプロイ。

共有型定義 (codename: 透明マント)

アプリ共有アドオンとAPIで共通する型定義

構成図と処理の流れ

構成図は下記の通りです。

f:id:swx-nakamura:20220131172239p:plain
構成図

この構成図ではサンプルとして、Questetraを使ってSalesforce内に格納されている商談担当者とプリセールスのメールアドレス(一部ユーザのみが閲覧できるようになっているフィールド)を取得する、といったことをおこなった際の処理の流れを表しています。

アプリ設計時の作業

  1. アプリ作成者: アプリ共有アドオンのConfigに「外部SaaSとQuestetraのフィールドのやりとりを記載するConfig」を設定する

実際に稼働するワークフローにおける流れ

  1. 申請者: 取得したい情報が保持されているSalesforceの商談IdをQuestetraのq_sfid項目にセットして申請する
  2. アプリ共有アドオン: Configを解釈し、q_sfidを元にAPI GWに商談担当者とプリセールスのメールアドレスをAPIに問い合わせる
  3. API: アプリ共有アドオンから送られてきたPayloadを解釈し、Salesforceから商談担当者とプリセールスのメールアドレスを取得し、アプリ共有アドオンに返す
  4. アプリ共有アドオン: APIから帰ってきたPayloadを解釈し、ワークフローのフィールド(q_owner_mailaddress, q_presales_mailaddress)にそれぞれPayloadの中身をセットする
  5. 申請者: 申請結果をワークフローから確認できる

この構成の理由

少し話を戻して、この構成におけるポイントは以下3つです。

  1. 開発言語を TypeScript にしたこと
  2. 3つのリポジトリの構成としたこと
  3. API GW - Lambda - 外部SaaSの構成としたこと

理由をそれぞれ1, 2 と 3に分けて記載します。

1, 2 の理由

  • AWS Lambdaの実行環境としてNode.jsがサポートされている
  • Questetra側のコードがECMA Script固定なので、サーバ側もjs系にあわせてあげると型やコードを共有できる
  • ネストした型情報をGenericsなどを使って比較的簡単に記述できる。また、それをDocument化する手段(package)が豊富である

3 の理由

  • アプリ共有アドオンの制限として「実行時間30秒、HTTP Requestは10回」があるため、同期のAPIを用意してあげるのがシンプルで良いと思った
    • (また、この時点で非同期APIの待ち受けや、バッチ処理などはこの仕組みでカバーしないと決めた)

開発の進め方

開発の流れとしては、以下のように実施しました。

  1. アプリ共有アドオン/APIの共通の型をGenericsでCRUDの4種分定義する
  2. Genericsにより生成された型をもとにAPI仕様書を作成できるスクリプトを用意する
  3. アプリ共有アドオン開発
  4. Questetra上で処理する必要のある各種SaaS操作の定義を1のGenericsをもとにAPIの型として書き起こす
  5. 3で作った型をもとにSaaSを操作するAPIを実装する
  6. Questetra上のワークフローに組み込んでリリースする

プロジェクト発足時にはQuestetraで動いていたSaaS操作系のワークフローは30以上ありました。そして、とある理由でそれを全て期限内に書き換える必要があったため、1と2でベースを作った後は、ひたすら3-4-5を繰り返していました。

ログ

Questetra側

残念ながらこれまでの各スクリプトタスクでロギングが実装されているタスクは皆無でしたので、このプロジェクトではログ出力も大事にしました。 Questetraのアプリ共有アドオンでは以下の内容を出力するようにしています。

  • Request / ResponseのPayloadの内容
  • LambdaのContextで確認できる内容 (ロググループ、Functionの残り時間)
  • Request先の環境 (prod/stage/dev)
  • 正しくConfig(JSON)を読み込めているかどうか
  • 受け取ったResponseをもとにワークフローのフィールドに値をセットできているかどうか

f:id:swx-nakamura:20220131172252p:plain
Questetraから確認できるタスクのログ

今回はアプリ共有アドオンを使う方式をとったので、アプリ共有アドオンの一箇所でさえロギングを実装すればそれを利用する各ワークフロー上でも全てログが出力されるようになります。実際に、有事の際の調査がとても楽になりました。(これまでは何かエラーが起きていることはわかるが、詳細は不明…という状態だった)

AWS側

AWS側でもロギングは行っており、 dazn-lambda-powertools のロガーを導入しました。プロセスIdや各種SaaSのIdを使ってAPIコール履歴を追っていけるようになったため、調査が捗ります。CloudWatch Insightsでダッシュボードを作成し、コールされているAPIの分布なども簡単に把握できるようにしました。

f:id:swx-nakamura:20220131172255p:plain
AWS CloudWatch Dashboard

そういえば、最近AWS製のPowertoolsもgithubにあがりましたね。正式リリースが待ち遠しいです。

作った仕組み(システム)による効果

この記事の冒頭で、スクリプト実行環境の制約による課題やQuestetraの運用上に生じた課題をいくつか書きました。 それらの課題は今回のこの仕組みによって以下のように解決されました。 (一部仕組みではなく、Questetraのアップデートにより解決されたものもあります。SaaSの良いところですね)

課題1: 利用できるJavaScriptのバージョンがかなり古かった

-> アプリ共有アドオンでは変化なし。QuestetraのプラットフォームのUpdateにより改善された

課題2: 外部ライブラリ利用不可。SaaSとのリクエストのやりとりなど、JavaScriptの標準ライブラリのみで頑張る必要がある

-> API Gatewayとのリクエストのやりとりは書く必要はあるが、一度書けばアプリ共有アドオンとして使い回しできるため負担ではない。また、SaaSの操作や各種自動化はLambda側で自由にPackageをimportして簡単にかけるようになった

課題3: スクリプトタスクのみを実行できず、ワークフロー全体でデバッグする必要がある

-> Lambda側に想定するConfigを渡すことで、特定の処理のみをデバッグ実行可能になった

課題4: ワークフローのデバッグ時のみSaaSの検証環境を利用するみたいなことが困難

-> アプリ共有アドオンでConfigを設定できる機能があるので、ワークフローの実行状況(本番 or デバッグ)により、APIの向き先が変化されるようにした。

課題5: ブラウザ内のテキストエディタで書く必要がある

-> メインの処理はLambda側で実装されているため、好きなエディタを使えるようになった

-> また、アプリ共有アドオンのコードについても、TypeScriptをTranspileしたJavaScriptをXMLに埋め込むスクリプトを作成し、その成果物(XML)をデプロイするようにしたため好きなエディタを使えるようになった

課題6: スクリプトタスクを多用すると、本来のワークフロー(タスクの流れ)の見通しが悪い

-> 従来では一つの処理(やりたいこと)に複数のスクリプトタスクが必要なことがあったが、アプリ共有アドオン一つで済むことが多くなり、ワークフローの見通しをよくすることができるケースが増えた

課題7: スクリプト実行環境のバージョンアップが実施される場合、手作業で一個ずつ対応する必要がある

-> 実行環境のバージョンアップが行われる場合であっても、バージョンアップ作業はアプリ共有アドオン一つで済むようになった

より具体的な効果

テキストだけだと効果が分かりづらいと思うので、Questetraのアプリ設計画面のBefore/Afterを紹介します。 以下の例は、QuestetraからSalesforceの特定のレコードを操作したい時に必要な設定項目です。

Before

スクリプトタスク、HTTP送信タスク*2、分岐*2の計5つのノードが存在します。そして、スクリプトタスクにはECMA Scriptが記載され、HTTP送信タスクにも設定が記載されています。また、HTTP送信タスクやSlackとの連携に利用する、本来の承認フローには関係のないフィールドが多数存在します(下半分)。

f:id:swx-nakamura:20220131172242p:plain
before

After

アプリ共有アドオン、リトライタスク*1、分岐*2です。設定項目はアプリ共有アドオンにしかなく、設定内容はカスタムコードではなくJSONです。また、Beforeにあったシステム連携用のフィールドの多数が不要になっています。

f:id:swx-nakamura:20220131172248p:plain
after

このように視覚的に確認すると、アプリのノードやフィールドが減りワークフローの見通しが良くなったほか、カスタムコード根絶や設定項目の削減により保守コストも下がったことがわかります。

まとめ

というわけで、Questetraの運用負荷が高い状態から脱却するためにアプリ共有アドオンをフル活用している話でした。

TypeScriptについては元々趣味で作っていたプロダクトで使っていたのでそれなりに書ける状態ではあったのですが、私自身としては今回業務システムにTypeScriptを用いたというのが初でした。実際に業務で利用してみて以下の良い点を感じました。

  • VSCode上での開発体験がとても良い(主に補完)
  • ESLintで細かくルールチェックできるのでチームで書き方がぶれない
  • 上記二つの理由により、型を最初に定義してしまえばビギナーでも割と実装は楽(考えることがかなり少なくなる)
  • フロントからバックエンドまで同じ言語でかける

この仕組みによりかなり運用負荷は軽減されましたが、まだアプリ共有アドオンに載せ替えられていないスクリプトタスクが大量に存在するため、引き続き地道な改善作業が必要です。