معماری چندکسبوکاری، تجاری و تجارت الکترونیک · فصل ۷ — Connector، Order، Packing، Barcode، Carrier و Notification · 268
Shopify Integration
workflow تجاری را به UI یا integration منفرد تقلیل میدهد و state، rights، approval و measurement را یکپارچه نمیکند. موضوع جاری: «Shopify Integration». میتواند «Shopify Integration» را برای یک workflow محدود با state/contract/approval و verification مناسب طراحی کند.
خلاصهٔ درس
workflow تجاری را به UI یا integration منفرد تقلیل میدهد و state، rights، approval و measurement را یکپارچه نمیکند. موضوع جاری: «Shopify Integration».
میتواند «Shopify Integration» را برای یک workflow محدود با state/contract/approval و verification مناسب طراحی کند.
خروجی این درس
میتواند «Shopify Integration» را برای یک workflow محدود با state/contract/approval و verification مناسب طراحی کند.
پیشنیازها
وقتی «GraphQL موفق بود» هنوز به معنی «integration درست است» نیست
فرض کن سفارش Shopify از طریق webhook میرسد، همان delivery بهخاطر timeout دوباره ارسال میشود و همزمان یک mutation برای تغییر موجودی جوابش در شبکه گم میشود. اگر connector فقط بگوید «HTTP موفق شد» یا «webhook معتبر بود»، هنوز چند سؤال حیاتی بیپاسخ است: درخواست روی کدام API version اجرا شد؟ scope لازم واقعاً وجود داشت؟ gid://shopify/... به کدام شناسهی داخلی نگاشت شده؟ این webhook همان delivery قبلی است یا یک رویداد مستقل؟ retry mutation همان intent قبلی است یا عملیات جدید؟ و اگر پاسخ گم شد، واقعاً چه چیزی در Shopify اتفاق افتاد؟
خروجی این درس Shopify Connector Contract and Event Ledger v1 است. هدف ساخت یک client عمومی Shopify نیست؛ هدف این است که هر observation و mutation قبل از اثرگذاری روی state داخلی، evidence قابلبازسازی داشته باشد.
مرز مالکیت COM-027
COM-027 فقط integration اختصاصی Shopify را مالک است: shop identity، GraphQL Admin surface، API version، access scopes، external GID mapping، webhook verification/dedupe، mutation idempotency، retry state و reconciliation. COM-029 معماری generic connector را مالک است و COM-028 integration اختصاصی WooCommerce را.
سه خط قرمز:
Shopify object != canonical local objectvalid HMAC != business completionsuccessful response != proof that a retry may use a new operation identity
مدل ذهنی: Shop → Version/Scopes → External identity → Event authenticity → Dedupe → Mapping → Mutation identity → Retry/Reconcile → Local promotion
مسیر قابلممیزی:
shop_ref → GraphQL Admin + requested/observed version + scopes → external_order_id/GID → webhook topic + webhook/event ID + HMAC + digest → dedupe/order check → schema_mapping_version → mutation + idempotency key + attempt → response/retry state → reconciliation_cursor → canonical_order_id → evidence_ref
هر gate یک مسئلهی متفاوت را حل میکند. HMAC مشکل mapping را حل نمیکند؛ idempotency مشکل access scope را حل نمیکند؛ و GraphQL success بهتنهایی uncertainty بعد از timeout را نمیبندد.
FACT، INFERENCE و DESIGN را جدا نگه دار
FACT: مستندات فعلی Shopify، GraphQL Admin API را سطح اصلی برای appها و integrationهای admin معرفی میکنند. در 15 سپتامبر 2026، 2026-07 latest stable و 2026-10 release candidate است.
FACT: APIهای versioned Shopify هر سه ماه نسخه میدهند. اگر app نسخهای را هدف بگیرد که دیگر accessible نیست، Shopify میتواند request را با قدیمیترین stable قابلدسترسی پاسخ دهد و X-Shopify-API-Version نسخهی واقعی اجراشده را نشان میدهد.
FACT: webhook deliveryها HMAC، X-Shopify-Webhook-Id، X-Shopify-Event-Id، API version و trigger timestamp دارند. Shopify ترتیب delivery را تضمین نمیکند و duplicate delivery ممکن است رخ دهد.
FACT: idempotency مستندشده فقط برای mutationهایی است که آن قابلیت را پشتیبانی میکنند. retry همان operation باید همان key را reuse کند و operation مستقل key مستقل میخواهد. retention مستندشده برای این keyها 24 ساعت است.
INFERENCE: چون version، event delivery و mutation retry مستقل از هم تغییر میکنند، evidence آنها را باید جدا ذخیره کرد تا یک موفقیت سطح transport روی state تجاری سایه نیندازد.
DESIGN: MAKNOW قبل از local promotion، mapping/version، event identity، retry identity و reconciliation را gate میکند. این policy محلی است، نه الزام Shopify برای همهی appها.
قرارداد ۲۳بُعدی Shopify
| case | decision | api_version | access_scope_set | schema_mapping_version | event_id_or_dedupe_key | authenticity_check | idempotency_key | retry_state | evidence_ref |
|---|---|---|---|---|---|---|---|---|---|
| baseline | ACCEPT_CONNECTOR | 2026-07 | read_orders write_inventory | map-v7 | wh-1001 | VERIFIED | idem-1001 | CLOSED | evidence://COM-027/baseline |
| scope_missing | HOLD_ACCESS_SCOPE | 2026-07 | read_orders | map-v7 | wh-1002 | VERIFIED | idem-1002 | BLOCKED | evidence://COM-027/scope |
| duplicate | HOLD_EVENT_DEDUPE | 2026-07 | read_orders write_inventory | map-v7 | wh-dup-1 | VERIFIED | idem-1003 | DUPLICATE_EFFECT_DETECTED | evidence://COM-027/dup |
| version_transfer | HOLD_MAPPING_VERSION | 2026-10 | read_orders write_inventory | map-v7 | wh-1004 | VERIFIED | idem-1004 | PENDING_MAPPING | evidence://COM-027/version-transfer |
Gate 1 — shop و connector را هویت بده
connector_id و shop_domain_or_tenant_ref باید دقیق باشند. «Shopify prod» برای audit کافی نیست. secret یا access token واقعی داخل artifact آموزشی ذخیره نمیشود.
Gate 2 — API surface و version را pin کن
api_surface در این درس GRAPHQL_ADMIN است. api_version بخشی از contract است، نه تنظیمی که در URL گم شود. برای baseline فعلی، stable production target باید وضعیت STABLE_SUPPORTED داشته باشد. release candidate برای migration test خوب است، نه baseline production این درس.
Gate 3 — requested و observed version را مقایسه کن
اگر request برای نسخهی retired ارسال شود و Shopify آن را fall-forward کند، response ممکن است موفق باشد اما contract دیگر همان contract درخواستشده نیست. نسخهی واقعی اجراشده باید از response evidence ثبت شود و mismatch نتیجهی HOLD_API_VERSION میدهد تا mapping دوباره بررسی شود.
Gate 4 — access scope را از authentication جدا کن
داشتن session/token یعنی هویتسنجی انجام شده؛ معنیاش این نیست که scope لازم برای query یا mutation حاضر است. access_scope_set باید با operation موردنیاز تطبیق داده شود. کمبود scope => HOLD_ACCESS_SCOPE.
Gate 5 — Shopify GID را canonical identity فرض نکن
external_order_id و external_gid شناسههای Shopify هستند. canonical_order_id فقط از mapping قابلنسخهبندی ساخته میشود. downstream نباید به shape یا prefix شناسهی vendor وابسته شود.
Gate 6 — mapping version با تغییر schema یا معنا باید revalidate شود
schema_mapping_version باید معلوم کند adapter بر اساس کدام field contract نوشته شده است. اگر version عوض شود یا field deprecated/removed شود، baseline success قابل reuse نیست؛ تا mapping جدید بسته نشده، HOLD_MAPPING_VERSION حفظ میشود.
Gate 7 — webhook را روی raw body verify کن
authenticity_check باید HMAC را روی body واقعی delivery بررسی کند. body که قبل از verify parse/transform شده میتواند evidence را مخدوش کند. HMAC نامعتبر یا نامعلوم => HOLD_AUTHENTICITY.
Gate 8 — delivery identity را جدا از business event نگه دار
event_id_or_dedupe_key باید حداقل delivery identity قابلپیگیری داشته باشد. duplicate delivery با همان X-Shopify-Webhook-Id نباید side effect دوم ایجاد کند. X-Shopify-Event-Id میتواند correlation بین deliveryهای یک merchant action را تقویت کند، اما جای business mapping را نمیگیرد.
Gate 9 — ترتیب webhook را فرض نکن
Shopify order delivery را تضمین نمیکند. اگر update قدیمی بعد از update جدید برسد، event ledger باید timestamp/version evidence را نگه دارد و از regression state جلوگیری کند. arrival order oracle نیست.
Gate 10 — idempotency را به mutation پشتیبانیکننده محدود کن
وجود یک string به نام idempotency key کافی نیست. ابتدا باید معلوم باشد mutation قابلیت idempotency مستندشده را دارد. اگر ندارد، connector باید operation identity و dedupe محلی خودش را نگه دارد.
Gate 11 — retry همان intent، همان key
اگر mutation همان operation قبلی است و فقط بهخاطر timeout retry میشود، key باید reuse شود. تولید UUID جدید برای هر attempt protection را از بین میبرد. در مقابل، دو operation مستقل—even با variables مشابه—باید key مستقل داشته باشند.
Gate 12 — timeout بعد از send = UNKNOWN، نه FAILED قطعی
اگر request ارسال شده ولی پاسخ نرسیده، response_state=UNKNOWN_AFTER_SEND است. retry کورکورانه قبل از reconciliation میتواند duplicate action بسازد. idempotency کمک میکند، اما outcome باید با query/ledger یا روش bounded دیگر reconcile شود.
Gate 13 — response cached هم business oracle نیست
در semantics مستند idempotency، retry موفق ممکن است response cache شده بگیرد و response بازسازیشده حتی بازتاب state فعلی database باشد. بنابراین response برای transport safety مهم است، اما تصمیم business باید evidence خودش را داشته باشد.
Gate 14 — evidence ناقص fail-closed است
اگر version، scope، mapping، HMAC، dedupe key، idempotency evidence یا reconciliation نامعلوم باشد، نتیجه typed HOLD/UNKNOWN میشود؛ نه اینکه «فعلاً قبول کنیم» و بعداً دنبال mismatch بگردیم.
مثالهای کاری
۱. baseline پایدار → ACCEPT_CONNECTOR
Shop مشخص است، GraphQL Admin روی 2026-07 stable pin شده، observed version همان requested version است، scopes کافیاند، GID با map-v7 نگاشت شده، webhook HMAC verified است، delivery dedupe شده و mutation/retry ledger و reconciliation بستهاند.
۲. scope لازم حذف شده → HOLD_ACCESS_SCOPE
Session معتبر است ولی mutation لازم scope کافی ندارد. authentication موفق، authorization کافی را اثبات نمیکند.
۳. HMAC نامعتبر → HOLD_AUTHENTICITY
حتی اگر payload شبیه order واقعی باشد، قبل از authenticity verification نباید event برای promotion استفاده شود.
۴. همان webhook دو بار side effect میسازد → HOLD_EVENT_DEDUPE
duplicate delivery باید idempotent باشد. دوبار fulfillment یا inventory adjustment failure جدی connector است.
۵. timeout و retry با key جدید → HOLD_IDEMPOTENCY
intent همان است ولی key عوض شده؛ protection vendor دیگر نمیتواند request را duplicate همان operation بداند.
۶. request نسخهی retired به version دیگری fall-forward میشود → HOLD_API_VERSION
HTTP success کافی نیست؛ requested/observed mismatch یعنی schema assumptions باید دوباره بررسی شوند.
۷. transfer: نسخه عوض میشود و field نگاشتشده deprecated است → HOLD_MAPPING_VERSION
حتی اگر version جدید stable باشد، map-v7 oracle نیست. mapping باید revalidate/bump شود.
۸. response بعد از send گم میشود → HOLD_RECONCILIATION
تا وقتی operation outcome از query/ledger evidence بسته نشده، success یا failed قطعی صادر نکن.
failure modeهای رایج
- استفاده از
latestذهنی بهجای version pin صریح؛ - نادیدهگرفتن
X-Shopify-API-Versionو fall-forward؛ - یکی گرفتن token معتبر با scope کافی؛
- استفاده از Shopify GID بهعنوان مدل هویت داخلی؛
- نگهداشتن mapping قدیمی بعد از API version/schema change؛
- verify کردن HMAC بعد از transform شدن body؛
- پردازش دوبارهی
X-Shopify-Webhook-Id؛ - فرض ترتیب eventها بر اساس arrival time؛
- استفاده از idempotency key برای mutationی که آن semantics را پشتیبانی نمیکند؛
- ساخت key جدید برای retry همان intent؛
- reuse کردن key برای دو operation مستقل؛
- تبدیل timeout بعد از send به FAILED قطعی؛
- مساوی گرفتن response موفق با business completion؛
- کپیکردن baseline decision بعد از version change بدون recomputation.
گزینهها و trade-offها
Webhook-only: latency پایین دارد، ولی duplicate/out-of-order/missed delivery و versioned payload را باید مدیریت کند. بدون reconciliation برای uncertainty کافی نیست.
GraphQL polling-only: کنترل بیشتری روی snapshot میدهد، اما latency و API cost/limits بالاتر میرود. برای reconciliation مفید است، نه لزوماً تنها مسیر event ingestion.
Webhook + bounded GraphQL reconciliation: contract بیشتری میخواهد، اما ambiguity را بهتر میبندد و pattern این درس است.
Vendor GID downstream: شروع سریع، coupling شدید به Shopify semantics.
Versioned Shopify adapter: طراحی بیشتری میخواهد، اما version/scopes/mapping و retry evidence را در مرز provider نگه میدارد.
پل برای فرد باتجربه: ۱۸ سؤال review
connector_idو shop reference دقیق چیست؟- API surface دقیقاً چیست؟
- requested API version کدام است؟
- observed version از response چیست؟
- version فعلی stable/support status چه وضعی دارد؟
- operation به چه access scopeهایی نیاز دارد؟
- external order ID و GID چطور trace میشوند؟
- canonical order ID با کدام mapping version ساخته شده؟
- چه چیزی mapping-version bump را trigger میکند؟
- webhook topic و delivery ID چیست؟
- HMAC روی raw body چگونه verify شده؟
- payload digest چیست؟
- duplicate delivery چگونه خنثی میشود؟
- out-of-order event چگونه از regression جلوگیری میکند؟
- mutation فعلی idempotency vendor را پشتیبانی میکند؟
- retry همان intent همان key را reuse کرده؟
- response/retry/reconciliation state چیست؟
- reviewer میتواند outcome را فقط از
evidence_refبازسازی کند؟
تمرین هدایتشده و مستقل
Lab آفلاین ۸ fixture دارد: baseline، scope ناقص، HMAC نامعتبر، duplicate webhook، out-of-order regression، retry همان intent با key جدید، API version fall-forward و transfer با mapping stale بعد از version change.
تمرین مستقل: integration از 2026-07 به نسخهی بعدی migrate میکند و field مورد استفادهی adapter deprecated میشود. مشخص کن چه چیزهایی باید دوباره verify شوند، کدام evidence قابل reuse است، چه چیزی mapping bump میخواهد و چرا successful request در نسخهی جدید بهتنهایی اجازهی promotion نمیدهد.
evidence قابلقبول
Artifact باید هر ۲۳ بُعد را پر کند یا non-success state صریح بدهد. secret/token واقعی ذخیره نمیشود؛ version requested/observed traceable است؛ scopes صریحاند؛ GID مستقیم canonical truth نمیشود؛ HMAC و event identity قبل از event acceptance بررسی میشوند؛ duplicate/out-of-order رفتار side effect اضافی نمیسازد؛ idempotency به support واقعی mutation محدود است؛ retry همان intent key ثابت دارد؛ و uncertainty بعد از send با reconciliation بسته میشود.
Lab کاملاً synthetic و offline است؛ network، shop واقعی، token یا دادهی مشتری لازم ندارد.
Assessment و transfer
برای پایان درس باید بتوانی:
- مرز COM-027 را در برابر COM-028 و COM-029 توضیح بدهی؛
- Shopify Connector Contract and Event Ledger v1 را بسازی؛
- version pin و observed version را از هم جدا کنی؛
- access scope را مستقل از authentication بررسی کنی؛
- external GID را با mapping versioned به canonical identity وصل کنی؛
- webhook authenticity، dedupe و ordering را جداگانه کنترل کنی؛
- retry idempotency را فقط برای operation پشتیبانیشده و با key ثابت اجرا کنی؛
- version-change transfer را بدون reuse کور baseline بازسازی کنی.
منابع و freshness
- Shopify GraphQL Admin API — rechecked در 15 سپتامبر 2026؛
2026-07latest stable و endpoint versioned مرجع FACT این درس است. - Shopify API versioning — rechecked در 15 سپتامبر 2026؛ quarterly versions، supported lifetime، release candidate و fall-forward semantics مرجع FACT هستند.
- Shopify Webhooks / Verify deliveries / Delivery structure — rechecked در 15 سپتامبر 2026؛ HMAC، delivery/event IDs، version header، duplicate و out-of-order behavior مرجع FACT هستند.
- Shopify Implementing idempotency — rechecked در 15 سپتامبر 2026؛ semantics فقط برای mutationهای پشتیبانیکننده، same-intent key reuse و retention 24 ساعته مرجع FACT هستند.
- Mapping، typed HOLDها و local-promotion policy در این درس MAKNOW DESIGN هستند.
ارزیابی
پاسخنامهی این درس در بستهی آموزش وجود ندارد؛ خودت جواب بده و صادقانه علامت بزن. «نیاز به مرور» درس را به صف تکرار میفرستد.
COM-027-S1 retrieval
Explain what COM-027 owns and name one adjacent non-goal.
COM-027-S2 application
Build Shopify Connector Contract and Event Ledger v1 for the baseline fixture.
COM-027-S3 diagnosis
A mutation timed out after send and a retry generated a new idempotency key. Diagnose the unsafe boundary and minimal repair.
COM-027-S4 transfer
The Shopify API version changes and a mapped field is deprecated. Recompute version/mapping evidence without copying the prior success result.
ملاکهای ارزیابی (rubric)
| Dimension | Sufficient evidence |
|---|---|
| Shop/API identity | connector, shop, API surface and requested/observed version are explicit |
| Access boundary | required access scopes are explicit; no real token is embedded |
| Identity mapping | Shopify ID/GID maps through a versioned adapter to canonical identity |
| Webhook authenticity | HMAC and payload digest are verified before event acceptance |
| Delivery safety | duplicate and out-of-order deliveries cannot create duplicate effect or regression |
| Mutation idempotency | supported mutations use stable same-intent keys; distinct operations differ |
| Reconciliation | unknown-after-send or retry uncertainty closes against bounded evidence |
| Transfer | version/schema change is recomputed rather than copied from baseline |
منابع و تازگی
مطالعهٔ موارد (case studies)
فایلهای آزمایشگاه
python run_checks.py را اجرا کنید. همه fixtureها ساختگیاند و هیچ shop واقعی، شبکه، access token یا داده مشتری لازم نیست.