述語ベース CustomerGroup(auto group)¶
- 作成日: 2026-05-21
- ステータス: 仕様確定(実装は段階リリース)
- 関連仕様:
2026-03-collection-facet-boundary.md(同期方式の先行事例),2026-03-smile-customer-import-upsert-rules.md,2026-05-commercial-rule-facet-targeting.md
背景¶
SMILE から取り込まれた顧客属性(customerStatus, salesRateClassCode, billingCustomerType 他)は Customer の custom field として独立保持されており、Vendure の CustomerGroup には連動していない。商品側はすでに Collection × Facet の宣言的メンバシップ(保存時の on-save 同期)が稼働しているが、顧客側は同等の仕組みを持たず、SMILE 属性更新のたびに CustomerGroup の手動メンテが必要だった。
本仕様では、CustomerGroup を 述語(predicate)で自動的にメンバを決める auto group として運用可能にし、既存の手動 CustomerGroup と並存させる。
ゴール¶
- Customer の custom field を条件式にして、
CustomerGroupメンバを自動で確定する。 - Customer 保存・SMILE import を契機に 物理的にメンバを add/remove する(評価時動的解決ではない)。
- 既存の手動
CustomerGroup運用は変更しない(並存)。 - 商流ルール / 表示制御などの下流コードはメンバ生成方法を意識しない。
非ゴール¶
- 任意 JavaScript 表現での述語評価。
- OR / NOT 演算子(初版は AND のみ。OR は別 auto group への分割で表現する)。
- Customer 側の facet 概念の新設(Vendure 標準で Customer は facetable ではない)。
データモデル¶
CustomerGroup custom field¶
| field | 型 | 必須 | 用途 |
|---|---|---|---|
isAuto |
boolean | yes | true の場合 auto group。default false。一度 true に設定すると手動メンバ編集が禁止される |
predicate |
text | no | JSON 文字列の述語。isAuto=true のときのみ必須。上限 8KB |
実装位置: apps/vendure-server/src/config/custom-fields.ts(既存ファイルに CustomerGroup ブロックを追加)。
既存スキーマへの影響¶
Customer,CommercialRuleには変更なし。- マイグレーションは Vendure custom field の自動マイグレーションに従う(
customer_groupテーブルへの 2 列追加)。
述語スキーマ¶
{
"version": 1,
"all": [
{ "field": "customerStatus", "op": "in", "values": ["special", "vip"] },
{ "field": "salesRateClassCode", "op": "equals", "values": ["A01"] },
],
}
op¶
| op | 意味 | values の扱い |
|---|---|---|
equals |
等価。複数 values 指定時は OR にせず無効 | 単一要素必須 |
in |
いずれか一致 | 1 件以上 |
not_in |
いずれにも一致しない | 1 件以上 |
exists |
値あり (NOT NULL かつ空文字でない) | 空配列を強制 |
not_exists |
値なし | 空配列を強制 |
field の制約(allowlist)¶
- 参照可能な field は 明示 allowlist で制限し、
packages/plugins/src/system-integration/smile/smile-field-guard.ts同等の仕組みをcustomer-group-predicate.allowlist.tsとして新設する。 - 初版 allowlist:
customerStatussalesRateClassCodebillingCustomerTypecollectionMethoddirectShippingEnabled- 名称解決が必要な表示は code を元に SMILE master / enum / 請求先区分マスタ経由で行う
(derivative の
salesRateClassName/billingCustomerTypeName/collectionMethodLabel/shippingCompany/salesRateClassは廃止済み) - 機密性が高い field(パスワード関連、メール、住所等)は allowlist に入れない。
バリデーション¶
- Admin API 保存時に JSON schema + allowlist チェックを実施し、失敗時は ErrorResult を返す。
valuesの型は対象 field の type 定義(customer-custom-fields.ts)と一致すること。
同期サービス¶
配置¶
- 新規:
packages/plugins/src/standard-extensions/admin-extensions/customer-group-auto-sync.service.ts - 述語 → SQL コンパイラを分離:
customer-group-predicate.compiler.ts(純粋関数 / 単体テスト対象)
責務と契約¶
| トリガー | 動作 |
|---|---|
CustomerEvent (created / updated) |
対象 customer を全 auto group に対して再評価し差分 add/remove |
CustomerGroupEvent (predicate 変更時) |
対象 group を bulk recompute(全 customer 走査) |
Admin mutation recomputeAutoCustomerGroup(id) |
同 bulk recompute を明示的に再実行 |
Bulk recompute の SQL パターン¶
collection-save-sync.service.ts の TypeORM CTE 差分パターンを踏襲する。概念:
WITH target AS (
SELECT id FROM customer WHERE <predicate sql>
),
current AS (
SELECT customerId FROM customer_group_customers_customer WHERE customerGroupId = :id
)
-- INSERT (target - current), DELETE (current - target)
Fail-soft 方針¶
- 同期処理中の例外は Sentry に送信するが、
CustomerEventのハンドラから例外を投げない(Customer 保存自体は成功させる)。 - これにより、auto group の同期失敗が pricing 計算や注文確定をブロックしない。
競合制御¶
- auto group 同士は独立評価。Customer が複数の auto group の述語に一致した場合は 全ての該当 group にメンバとして登録される(重複所属を許容)。
- auto group と手動 group も独立。Customer は両方のグループに同時所属できる。
- 評価結果は述語コンパイラが純関数のため評価順序に依存しない。
Admin API¶
| 種別 | 名称 | 振る舞い |
|---|---|---|
| Mutation | addCustomersToCustomerGroup |
対象が isAuto=true の場合 ForbiddenError(auto group is read-only) |
| Mutation | removeCustomersFromCustomerGroup |
同上 |
| Mutation | recomputeAutoCustomerGroup(id: ID!): Job! |
bulk recompute を明示実行(CLI / Admin から) |
| Query | previewAutoCustomerGroupMatches(predicate: JSON!): Int! |
保存前 dry-run。当該述語にマッチする customer 件数を返す |
| 権限 | UpdateCustomerGroup |
predicate の編集 / recompute / preview すべてこの権限 |
React Dashboard¶
- auto group 詳細画面に predicate editor を追加(field 選択 + 演算子 + 値)。
- predicate editor 上で
previewAutoCustomerGroupMatchesを自動呼び出しし、影響件数を表示する。保存時は件数表示があることを必須にする(誤定義防止)。 - 手動メンバ追加 UI は
isAuto=trueで非表示。 - 一覧画面で auto / manual のバッジを表示。
SMILE import との連携¶
packages/plugins/src/system-integration/smile/services/smile-customer-row.parser.ts経由の Customer upsert は最終的にCustomerService.updateを呼びCustomerEventを発火するため、auto sync が自動追随する。- SMILE 側コード変更は不要。
- bulk import 直後の整合確認用に、CLI / Admin から
recomputeAutoCustomerGroupを全 auto group に対して順次叩く運用手順を補助スクリプトとして提供(実装フェーズ 6 の範疇)。
監査 / 運用ガード¶
- predicate 変更は
CustomerGroupEvent経由で既存の audit log に diff を残す。 - predicate field allowlist は
customer-group-predicate.allowlist.tsに集約し、追加時はレビューとテスト必須。 - 保存前
previewAutoCustomerGroupMatchesは UI 上必須化(誤って広範囲の顧客を auto group に含めるリスク低減)。 - 同期エラーは Sentry に
issue_kind: operational_failureで送信。 - 想定 auto group 件数: 初期は 10 件以下。これを超えるようなら同期コストを再評価し、job queue 化を検討する。
関連ドキュメントへの追記方針¶
下記 3 本に、auto group の概要を 3〜5 行で追記し本仕様へリンクする:
docs/03-implementation/vendure-plugins/customer-management.mddocs/03-implementation/vendure-plugins/customer-visibility.mddocs/03-implementation/vendure-plugins/pricing-system.md
追記の趣旨: 「auto group は述語で自動決定され、商流ルールや表示制御からは通常の CustomerGroup ID として参照可能」。
実装フェーズ¶
- Predicate schema + compiler + unit test(field allowlist 含む)
CustomerGroupのisAuto/predicatecustom field 登録 + Admin 保存時 validatorCustomerGroupAutoSyncService+CustomerEvent/CustomerGroupEvent購読- Admin resolver の
ForbiddenErrorガード +recomputeAutoCustomerGroup/previewAutoCustomerGroupMatchesmutation/query - React Dashboard predicate editor + dry-run プレビュー UI
- SMILE bulk import 後の整合 e2e + 補助スクリプト
- ドキュメント整合反映(auto/manual の運用差分)
テスト戦略¶
Unit¶
customer-group-predicate.compiler.spec.tsで各opの SQL 生成 / 評価関数を網羅。customer-group-predicate.validator.spec.tsで allowlist 違反 / 型不一致 /values制約違反を検証。
Integration (apps/vendure-server e2e)¶
- Customer create / update → 該当 auto group のメンバ反映
- predicate 変更 → bulk recompute でメンバが差分更新
- auto group への
addCustomersToCustomerGroupが 403 - SMILE import 経由 update でもメンバが追従
- 同期エラー発生時に Customer 保存が成功し、Sentry にイベントが届く
回帰¶
- 既存の手動
CustomerGroup操作とCustomerGroup参照に影響がないこと。
Critical Files(実装時参照)¶
apps/vendure-server/src/config/custom-fields.tsapps/vendure-server/src/config/custom-fields/customer-custom-fields.tspackages/plugins/src/standard-extensions/admin-extensions/collection-save-sync.service.tspackages/plugins/src/standard-extensions/admin-extensions/admin-extensions.plugin.tspackages/plugins/src/system-integration/smile/services/smile-customer-row.parser.tspackages/plugins/src/rule-engine/visibility/entities.ts
リスク¶
| リスク | 対処 |
|---|---|
| 誤定義による広範囲影響 | 保存前 previewAutoCustomerGroupMatches の件数表示を UI で必須化 |
| 機密フィールド参照 | predicate field allowlist で強制 |
| 同期失敗が pricing をブロック | fail-soft(Customer 保存自体は成功、Sentry 通知) |
| 想定件数増 | 同期コストの再評価 → job queue 化 / OR・NOT 演算子の追加を別フェーズで検討 |