معماری چندکسبوکاری، تجاری و تجارت الکترونیک · فصل ۷ — Connector، Order، Packing، Barcode، Carrier و Notification · 271
Order State Machine
Build a canonical order state machine with explicit state axes, transition guards, triggering event, source state, target state, idempotency, invariants, terminal/reopen rules and evidence so vendor statuses map into—but never silently redefine—the internal lifecycle. میتواند «Order State Machine» را برای یک workflow محدود با state/contract/approval و verification مناسب طراحی کند.
Lesson summary
Build a canonical order state machine with explicit state axes, transition guards, triggering event, source state, target state, idempotency, invariants, terminal/reopen rules and evidence so vendor statuses map into—but never silently redefine—the internal lifecycle.
میتواند «Order State Machine» را برای یک workflow محدود با state/contract/approval و verification مناسب طراحی کند.
This lesson produces
میتواند «Order State Machine» را برای یک workflow محدود با state/contract/approval و verification مناسب طراحی کند.
Who it is for
Foundation
Use a field map, one worked scenario and explicit state vocabulary before independent construction.
Repair
Identify the failed evidence dimension, use the smallest mutated fixture and rerun only the affected gate.
Experienced
Compress definitions; require trade-off defense, counterexample analysis and changed-context transfer.
Prerequisites
When one status string becomes five different truths
An order is paid at 10:00. Fulfillment starts at 10:05. At 10:11 a delayed payment webhook reports failed, and at 10:12 the fulfillment provider retries the same packed event it already sent. A vendor UI may show one status, but your internal system actually has several independent questions: Was payment captured? How much was fulfilled? Is the commercial order cancelled or refunded? Is there an exception under review?
The outcome of this lesson is Canonical Order State Transition Ledger v1. Every accepted transition must be reconstructible from the order identity, model version, current state axes, source event, mapping, guard, invariants, event identity/time, idempotency, late-event policy, terminal/reopen rule, result and evidence reference.
COM-030 ownership boundary
COM-030 owns canonical order lifecycle state and transition evidence. COM-029 owns connector/adapter architecture and transport concerns. COM-031 owns packing queue eligibility and priority. This lesson does not let a connector status silently redefine the internal lifecycle and does not turn a state transition into permission to pack, ship, refund or notify.
Three red lines:
provider status != canonical order truthprocessing time != event timeterminal != reopenable without authority
Mental model: External observation → Canonical event → Axis-local transition → Guard/invariants → Commit or reconcile
Auditable path:
source system/status → canonical event mapping → current axis snapshot → from/to candidate → guard → invariants → idempotency/temporal checks → transition result → evidence
Do not start by asking “what should order.status become?” Start by asking which axis this event is allowed to affect. A payment event normally changes payment evidence; a fulfillment event changes fulfillment evidence. Commercial cancellation and exception/dispute are separate concerns unless an explicit policy says otherwise.
Keep FACT, INFERENCE and DESIGN separate
FACT: W3C SCXML 1.0 is a stable Recommendation for a generic state-machine execution environment. It defines states, initial configurations, transitions and event-driven execution; it does not define e-commerce order states.
FACT: Current WooCommerce documentation exposes statuses such as Pending payment, Processing, On hold, Completed, Cancelled, Refunded and Failed. Their meanings deliberately mix payment and fulfillment context because they serve WooCommerce’s product model.
INFERENCE: When external systems can emit late, duplicate or partial events, keeping payment, fulfillment, commercial and exception dimensions separate makes illegal regressions and ambiguous mappings easier to detect.
DESIGN: MAKNOW uses explicit canonical events, axis-local transitions, guards, invariants, idempotency keys, late-event policy and reopen authority. Those rules are local design authority; they are not claimed to be W3C or WooCommerce requirements.
The 23-dimension transition contract
| Field | Meaning | Critical |
|---|---|---|
order_id | Stable commerce order identity | yes |
state_model_version | Version governing state vocabulary/transitions | yes |
payment_state | Canonical payment axis | yes |
fulfillment_state | Canonical fulfillment axis | yes |
commercial_state | Commercial/cancellation/refund axis | no |
exception_state | Exception/dispute axis | no |
source_system | System that emitted observation | no |
source_status | Raw provider/vendor status | yes |
canonical_event | Internal mapped event | yes |
from_state | Expected source state on affected axis | yes |
to_state | Proposed target state on affected axis | yes |
transition_guard | Boolean/predicate result before mutation | yes |
invariant_set | Named invariants checked for transition | yes |
event_id | Source event identity | yes |
event_time | Business/source occurrence time | yes |
processing_time | Local handling time | no |
idempotency_key | Logical dedupe identity | yes |
late_event_policy | Policy for older/out-of-order event | yes |
terminal_state | Whether target/source is terminal under policy | no |
reopen_authority | Authority/evidence for leaving terminal state | no |
transition_result | Bounded transition decision | yes |
owner | Owner/reviewer for unresolved case | no |
evidence_ref | Traceable evidence reference | yes |
Example decisions
| Order | Event | From → To | Result |
|---|---|---|---|
| O-100 | PAYMENT_CAPTURED | AUTHORIZED → PAID | APPLY_TRANSITION |
| O-101 | FULFILLMENT_COMPLETED | PACKED → FULFILLED | HOLD_GUARD_FAILED |
| O-102 | FULFILLMENT_PACKED | PACKED → PACKED | NOOP_DUPLICATE |
| O-103 | FULFILLMENT_PARTIAL | UNFULFILLED → PARTIALLY_FULFILLED | APPLY_TRANSITION |
Gate 1 — Version the model before interpreting events
state_model_version belongs in the evidence. If state vocabulary or transition semantics change, an old event must be interpreted against the policy that actually governed the order—or deliberately migrated with traceable rules.
Gate 2 — Preserve independent state axes
payment_state, fulfillment_state, commercial_state and exception_state answer different questions. A provider’s completed string cannot be allowed to overwrite all four.
Gate 3 — Map provider status to a canonical event first
source_status is evidence from a provider. canonical_event is your internal interpretation. Unknown, overloaded or context-dependent statuses enter HOLD_PROVIDER_MAPPING until the mapping is explicit.
Gate 4 — Make source and target states explicit
A transition is not “set status = X.” It is a proposal from from_state to to_state on a known axis. If the actual current state differs from from_state, the transition is stale or invalid and requires reconciliation.
Gate 5 — Guards answer “may this transition run now?”
A guard can require payment captured before a fulfillment-complete transition, or prohibit cancellation after a non-reversible fulfillment milestone. A false guard never becomes true because a provider retried the event.
Gate 6 — Invariants protect rules that must remain true
Examples: captured amount cannot become negative; fully refunded cannot exceed captured amount; fulfilled quantity cannot exceed order quantity; a terminal commercial cancellation cannot be silently regressed by an older event.
Gate 7 — Event ID and idempotency key prevent repeated side effects
Retries are normal. If the same logical event is processed twice, the second pass should be NOOP_DUPLICATE or an equivalent bounded result. “We saw it twice” is not two lifecycle transitions.
Gate 8 — Event time and processing time are different
A webhook can arrive minutes or hours late. event_time represents when the source says the business event happened; processing_time is when you handled it. Sorting only by arrival order can regress state incorrectly.
Gate 9 — Terminal states need explicit reopen rules
A terminal state may be terminal for one axis but not another. If your policy allows reopening, reopen_authority must name who/what can do it and under which evidence. Missing reopen authority yields HOLD_REOPEN_AUTHORITY_REQUIRED.
Gate 10 — Partial fulfillment is an extension of fulfillment, not payment
A new provider can introduce PARTIALLY_FULFILLED. Extend the fulfillment state vocabulary and transition rules. Do not rewrite payment transitions merely because the provider’s overall order status changed.
Gate 11 — Late/out-of-order events reconcile; they do not automatically regress
If an older event arrives after a newer terminal transition, preserve it as evidence and apply late_event_policy. A safe default is HOLD_RECONCILE_LATE_EVENT, not silent rollback.
Gate 12 — Missing decisive evidence fails closed
If mapping, guard, current state, idempotency identity, event time or evidence reference is missing, use HOLD_UNKNOWN_EVIDENCE or another typed non-success state. Clean dashboards are not more important than lifecycle correctness.
Worked examples
1. PAYMENT_CAPTURED: AUTHORIZED → PAID → APPLY_TRANSITION
The event maps unambiguously to the payment axis, guard and invariants pass, the idempotency key is new, and evidence is complete. Fulfillment/commercial/exception axes remain unchanged.
2. FULFILLMENT_COMPLETED while required payment guard is false → HOLD_GUARD_FAILED
The provider may be reporting real warehouse work, but the canonical transition cannot be committed under the current policy until the contradiction is reconciled.
3. Same PACKED event retried with same idempotency identity → NOOP_DUPLICATE
The retry is recorded, but it does not create another transition or repeated downstream effect.
4. Old PAYMENT_FAILED arrives after a newer accepted payment state → HOLD_RECONCILE_LATE_EVENT
Preserve both event time and processing time. Do not roll back merely because the failure arrived later.
5. Provider status completed has ambiguous axis meaning → HOLD_PROVIDER_MAPPING
A status string without explicit mapping cannot decide whether “completed” means payment, fulfillment, commercial completion, or something vendor-specific.
6. Provider adds partial fulfillment → fulfillment axis extends; payment stays unchanged
Add PARTIALLY_FULFILLED and its transitions. The transfer succeeds only if unrelated payment transitions remain identical.
Common failure modes
- one mutable
order.statuscarrying payment, fulfillment, refund and cancellation at once; - copying vendor status names directly into canonical state;
- transition without explicit
from_state/to_state; - evaluating the guard after mutating state;
- no invariant checks;
- using delivery/processing order as business-time order;
- treating retries as new transitions;
- reopening terminal state without authority;
- allowing a late event to regress a newer terminal outcome;
- adding partial fulfillment by changing unrelated payment logic;
- converting missing mapping/evidence into success.
Alternatives and trade-offs
Single status enum: easy to query and display, but becomes overloaded as payment, fulfillment, returns and exceptions diverge.
Full event sourcing: powerful audit/replay model, but larger operational commitment than this lesson requires. The lesson borrows event identity/time discipline without mandating a specific storage architecture.
Workflow engine / SCXML implementation: useful for executable state semantics, but a notation/runtime does not define your commerce policy.
Vendor-native statuses only: fast integration, but couples internal truth to one provider and makes multi-provider reconciliation difficult.
Multiple explicit axes + transition ledger: slightly more modeling work, but better isolation, explainability and transfer. That is the bounded pattern used here.
Experienced bridge: fourteen review questions
- Which model version governs this order/event?
- Which axis is the event allowed to change?
- Is source status mapped explicitly to a canonical event?
- Does actual current state equal
from_state? - Is
to_statelegal from that source? - Did the guard pass before mutation?
- Do invariants still hold after the proposed transition?
- Is this event/idempotency key already processed?
- Are event time and processing time both preserved?
- Can this event regress a newer state?
- Is the target terminal for this axis?
- If reopening, is authority explicit?
- Are unrelated axes unchanged?
- Can the result be reproduced from
evidence_refwithout hidden reasoning?
Guided and independent practice
The offline lab contains eight fixtures. Start with a valid payment transition, then inspect guard failure, duplicate retry, late event after terminal/newer evidence, ambiguous provider mapping, missing decisive evidence, unauthorized reopen and partial-fulfillment transfer.
Independent exercise: introduce provider status partially_fulfilled for an order whose payment axis is already PAID. Add a canonical event and fulfillment transition that preserves payment/commercial/exception states. Then process the same event twice and an older FULFILLMENT_STARTED event afterward. The correct design should apply once, deduplicate once and reconcile the older event rather than regress.
Verification evidence
A passing artifact populates all 23 required dimensions or returns an explicit typed non-success result. Provider status is mapped before transition; guards and invariants are evaluated before commit; duplicate identity is bounded; event time is preserved; terminal/reopen rules are explicit; and changed-context transfer modifies only the affected axis.
The lab is offline and synthetic. It does not need a WooCommerce store, payment gateway, fulfillment provider, customer data or production credentials.
Assessment and transfer
To finish the lesson, you must be able to:
- explain the COM-030 boundary against COM-029 and COM-031;
- build Canonical Order State Transition Ledger v1 from visible evidence;
- reject a transition whose guard is false;
- deduplicate a repeated logical event;
- reconcile an older event after a newer/terminal transition;
- preserve explicit reopen authority;
- add partial fulfillment without changing unrelated payment transitions;
- explain why a vendor status is mapping evidence rather than canonical authority.
References and freshness
- W3C — State Chart XML (SCXML) 1.0, W3C Recommendation dated 2015-09-01; rechecked 2026-09-15. Stable generic state-machine authority, not commerce-state authority.
- WooCommerce — Order Statuses documentation; rechecked 2026-09-15; review due 2026-10-18. Current vendor meanings for Pending payment, Processing, On hold, Completed, Cancelled, Refunded and Failed.
- Multi-axis state vocabulary, transition guards/invariants, idempotency, late-event handling and reopen policy in this lesson are MAKNOW DESIGN and are not attributed to those external sources.
Assessment
There is no answer key in this lesson pack; answer it yourself and mark honestly. "Needs review" sends the lesson to your review queue.
COM-030-S1 retrieval
Explain the COM-030 boundary and one adjacent non-goal.
COM-030-S2 application
Build Canonical Order State Transition Ledger v1 from baseline fixture.
COM-030-S3 diagnosis
Diagnose guard-false, duplicate and late-event mutations without silently regressing state.
COM-030-S4 transfer
Add partial fulfillment while preserving unrelated payment transitions.
Assessment rubric
A passing submission must preserve scope, bind external facts to their authority class, populate the 23-dimension ledger or explicit non-success states, reject guard/invariant failures, deduplicate retries, preserve event-time ordering, enforce terminal/reopen rules, and recompute the partial-fulfillment transfer without changing unrelated axes.
References & freshness
Case studies
Lab files
در همین پوشه python run_checks.py را اجرا کن. هشت fixture مصنوعی mapping isolation، guard/invariant، idempotency، late-event reconciliation، terminal reopen authority، missing evidence و transfer مربوط به partial fulfillment را میسنجند. شبکه یا credential لازم نیست.