Strategy → Architectural fork
Typed conditions vs. expression engine.
Four architectural options for adding customer-attribute targeting to BC's promotions engine. The recommendation is a two-track approach: Option A in the PHP monolith now to ship the Sony use case, Option C (CEL) in the Go service extraction in parallel because the struct is a 2-field stub with zero callers — the right moment to design it correctly.
Source authority: research/current-state/architectural-options.md ↗
Typed PHP conditions
Add CustomerEmailDomainCondition to the existing typed DSL in the PHP monolith.
One transformer change, one new condition class, one CP rule builder registration.
Proof of concept for the transformer plumbing. Ships the Sony use case immediately.
Zero new service calls — email is already in the checkout record.
Ships the Sony use case. Minimal scope. No new dependencies.
CEL expression engine (Go)
Add github.com/google/cel-go to the Go promotions service.
Replace the bare domain.Customer struct with a typed PromotionEvaluationContext.
Compile-once-eval-many, non-Turing-complete sandbox. The Go service currently has 2 fields
and zero callers at the evaluation layer — design it correctly now, not after 20 callers exist.
Unlocks general extensibility. Right architectural moment. Medium scope.
All four options
Full comparison from architectural-options.md. Options B and D are assessed but not recommended.
| Option | Label | Where | Extensibility | New condition cost | Platform risk | Status |
|---|---|---|---|---|---|---|
| A | Typed PHP conditions (DSL extension) Phase 1 | bigcommerce/bigcommerce (PHP monolith) | Typed per-field | New class per condition type (EmailDomainCondition, CompanyCondition, …) | low | ✓ recommended |
| B | Attribute map / dynamic key-value | bigcommerce/bigcommerce (PHP monolith) | Typed per-field | New key registered in attribute map schema | low | not recommended |
| C | CEL expression engine Phase 2 | bigcommerce/promotions (Go service) | General purpose | No new code — merchants write expressions over the context struct | medium | ✓ recommended |
| D | Eligibility functions (Shopify model) | Merchant-authored code (Functions API) | General purpose | Merchant writes and deploys a function | high | not recommended |
Why not Option B (attribute map)?
An attribute map still requires a new entry per condition type — the extensibility gain over Option A is marginal. The trade-off is reduced type safety and less discoverable CP authoring (merchants see a generic key picker instead of named condition types). The structured condition DSL in Option A is both safer and more merchant-friendly.
Why not Option D (eligibility functions)?
The Shopify Functions model pushes implementation complexity to merchants — every new condition type requires the merchant to write and deploy code. This breaks the no-code CP authoring surface that is the primary merchant value proposition for Advanced Promotions. Ruled out: merchant sophistication requirement and loss of the no-code path.
The context assembly problem
The expression engine evaluates against a typed context struct. The cost of that struct depends entirely on where the data comes from — not on the evaluator itself.
Zero cost — already in flight
cart.email
The checkout-entry email is in the cart record for all customers — guest and authenticated alike — by the time promotions evaluate.
The data exists; it is simply not extracted into ExecutePromotionRequest today.
No new service call. This is Phase 1.
~2–5ms — Redis-cached gRPC
Company name, customer record fields
Available via a gRPC call to the customer record — Redis-cached, ~2–5ms on cache miss. Requires a customer materialized-view snapshot keyed by customer ID. Phase 2.
~5–15ms — gRPC + async snapshot
Custom form fields
Require a gRPC call to customers-scala.
Best served via an async materialized-view snapshot refreshed on customer.updated events.
Requires customers-domain coordination. Phase 3.
Source: bc-customer-data-model-and-feasibility.md ↗ §3, architectural-options.md ↗ §5
Source documents
- research/current-state/architectural-options.md ↗ — full four-option analysis with SAP/Drools pattern comparison
- research/current-state/bc-customer-data-model-and-feasibility.md ↗ — full customer data model, per-attribute cost analysis
- docs/feasibility.md ↗ — Stage 5 feasibility document with per-phase go/no-go verdict
- docs/strategy.md ↗ — Stage 5 strategy document with decision asks