by MintJams

OSSコマース・オペレーション基盤構築記 第2回 | WebhookからBPM、そしてリアルタイム画面へ。コマース基盤を支える見取り図

Shopify WebhookからBPMNへの完璧なリレー!EIPによる共通エンベロープ設計、JCRへの不変保存、そしてTeamsやLINE、汎用Webhookまで拡張されたプラガブル通知を支える見取り図をコードと共に解説します。

前回の第1回では、Shopifyの在庫が減った瞬間にSlackへ通知が飛び、バックオフィスに「担当者のためのタスク」が全自動で起票される業務体験をお届けしました。

今回は、いよいよその裏方の世界に潜入します。 「画面のボタンを何も押していないのに、なぜShopifyの動きと連動して、自動的にタスクや画面の数値がリアルタイムに書き換わるのか?」

その秘密は、ShopifyからWebhookが届いた瞬間にスタートする、OSSコンポーネントたちによる連携リレーにあります。コンパイルやサーバーの再起動を一切必要とせず、CMS上のワークフローとスクリプトだけで構築された、アーキテクチャの全体像(見取り図)を紐解いていきましょう!


コマース・オペレーション(ComOps)の全体像(見取り図)

まずは、データがどのようにシステムを駆け巡るのか、全体像を俯瞰してみましょう。

Shopifyからイベント(Webhook)が飛んできてから、業務タスクが生まれ、リアルタイムダッシュボードやSlackへ通知が届くまで、システムは大きく「3つのレイヤー」に分かれて仕事をしています。

  1. Groovyエンドポイント+EIPルート: 「データの受入と交通整理(堅牢なパイプライン)」
  2. JCR(store+normalize): 「データの防護・不変保存(状態管理と不変の監査証跡)」
  3. BPMNワークフロー: 「業務の主導と担当者の意思決定(進捗・タスクの制御)」

この3者が明確な役割分担をしているからこそ、大量の通知や仕様変更にもビクともしない堅牢な基盤が手に入ります。それぞれのレイヤーで、具体的にどんなコードが動いているのかを見ていきましょう。


レイヤー1:どんなECでも繋がる「共通エンベロープ」の魔法

最初の入り口となるのが、ECからのイベントを受け取るGroovyエンドポイント(webhook.groovy)です。 ここではShopify固有の X-Shopify-Hmac-SHA256 署名ヘッダーを検証し、データの安全性を最前線で守ります。

注目してほしいのは、認証をパスした後の処理です。届いたデータをそのまま下流に流すのではなく、「共通エンベロープ(封筒)」に詰め替えてから、EIPのインジェストコア(direct:commerce-ingest)へ非同期で送信します 。

// webhook.groovy より抜粋
IntegrationAPI.createMessageSender()
    .setEndpointURI("direct:commerce-ingest")
    .setBody(bodyString)
    .setHeader("event_source", "shopify")
    .setHeader("event_topic", topic)
    .setHeader("event_id", webhookID)
    .setHeader("received_at", new Date().toInstant().toString())
    .setHeader("runAs", "commerce-service-user") // 公開エンドポイントのため特権ユーザーを明示
    .sendAsync()

💡 なぜ、わざわざ「封筒」に詰め替えるのか?

共通の封筒に詰め替えることにより、将来的に「楽天」や「BASE」、あるいは社内の「自社基幹システム(ERP)」を接続したくなっても、この下流のルートやBPMNを変更する必要がありません。それぞれの独自署名を検証し、上記と同じヘッダー(封筒)を組み立てて direct:commerce-ingest にポストする専用アダプターを1つ追加するだけで、すべてのパイプラインへ即座に統合されます 。


レイヤー2:JCRへの「不変保存」と「べき等性チェック」

バトンを受け取ったEIPルートは、BPMNワークフローへデータを引き渡す前に、JCRレポジトリへ「生のペイロード(JSON)」をそのまま永続化保存します(logEvent.groovy)。

// ingest.xml からの呼び出し
// 1. Durable event log (生のペイロードを保存し、リプレイや監査を可能にする)
toD uri="cms:/etc/commerce/scripts/commerce/logEvent.groovy?inputs=..."

これにより、すべてのトピックがファーストクラスのビジネスイベントとして記録され、万が一処理が失敗しても、過去のログから「イベントを手動・自動リプレイ(再送)」することが可能になります。

さらに、JCRのパスを活かしたべき等性(重複排除)チェックをします。

<toD uri="cms:exists?query=/jcr:root/content/commerce/history/webhooks/${header.shopify_webhook_id}&amp;@header.webhookAlreadyProcessed=exists"/>

EC運用では、同じWebhookがネットワークの都合等で2回送られてくる(イベントが重複する)ことは日常茶飯事です。このルートは一度処理したイベントをJCRに記録しておくことで、重複イベントを確実にガードします。

また、Shopifyで連続して商品が更新された際に、BPMNインスタンスが乱立するのを防ぐため、すでに同じ商品のプロセスが走っている場合は、新しい起動をスキップし、起動中のプロセスが最新データを参照して処理を継続する設計になっています。


レイヤー3:BPMNが司る、動的なルール解決と「プラガブル通知」

データの防護が終わると、いよいよ頭脳であるBPMNワークフロー(product-update-flow.bpmn)へプロセス開始のシグナルが送られます。

[Check Threshold Config] ➔ [Threshold configured?] ➔ (Noなら手動設定) ➔ [Check Inventory Level]

ここで動くスクリプト(checkInventoryLevel.groovy)には、「動的なしきい値ルールエンジン」が組み込まれています。 単に画面で手動設定した数値を見るだけでなく、inventory-rules.ymlの設定や過去の注文履歴から計算された「販売速度(Sales Velocity)」を読み込み、「カテゴリ・タグ・季節・販売速度」ベースの高度なアラート判定をこなしています。

そして判定の結果、在庫が危険水域に達すると、業務タスクの作成イベントをフックして通知モジュール(notifyTaskCreated.groovy)が起動します 。

この通知モジュールは、1つのチャネルに依存していません。共通のメッセージオブジェクト(NotificationMessage)を組み立てたら、有効化されているすべてのチャネル(Slack、Discord、Teams、LINE、Email、汎用Webhook)へ、一斉にディスパッチ(配信)します 。

// notifyTaskCreated.groovy より
// チャンネルに依存しない抽象的なメッセージを組み立て、通知先設定ファイルから一斉配信
def message = NotificationMessage.create().title("📦", "Inventory alert workflow")
...
Notifications.dispatch(log, "notifyTaskCreated", config, message)

🔌 無限の可能性を持つ「汎用Webhook(Generic Webhook)」

通知設定の中に「Generic Webhook」という項目が増えたことにお気づきでしょうか? これは、「JSONを受け取れるシステムなら、世界中の何とでも連携できる」という最強の拡張ポートです。 例えば、在庫が切れた瞬間にNotionのタスクボードへ自動でカードを起票したり、iPaaS(Makeやn8n)のトリガーに接続して社内スプレッドシートに自動書き込みを行ったり……。外部のSaaSと無限に繋がるハブとして機能します。


まとめ

第2回は、ShopifyのWebhookを最前線で受け止め、堅牢にガードしながら業務タスクへと変換し、プラガブルに通知する「リレーの全体像」をコードを交えて解説しました。

これらの高度なイベント駆動システムは、コンパイルもサーバーの再起動もせず、仮想デスクトップにワークフロー定義ファイルとスクリプトを『アップロードするだけ』で動的にデプロイされて実行されます。

さて、これだけ設定ファイルや機能が増えてくると、次に気になるのは「運用の安全性」です。

「設定ファイルを書き換えたときに、タイポ(誤字)があったらシステムはどうなるの?」

「同じ通知が乱立したり、最悪システムが詰まって通知が来なくなったりしない?」

次回(第3回)は、「受信の堅牢性とマルチテナント(EIP編)」と題し、エラーハンドリングの設計や、shopify.yml などの設定を安全にバリデーションする「スキーマ駆動の仕組み」、そして多重起動ガードの真価に迫ります。システムの裏側をさらにタフにするテクニックをお楽しみに!