معماری Event-Driven؛ Event، Outbox، Saga و عملیات

سفارش در دیتابیس ثبت شده، پول کسر شده و Broker هم پیام را تحویل داده است؛ اما مشتری دو پیامک گرفته، موجودی دوبار کم شده و تیم نمی‌داند کدام مصرف‌کننده واقعاً موفق بوده. این شکست از «کمبود Kafka» نیست. معماری Event-Driven وقتی Contract، مرز تراکنش، Idempotency، Ordering، Replay و Observability نداشته باشد، خطای همزمان را به خطای توزیع‌شده و دیرکشف تبدیل می‌کند.

EDA می‌تواند وابستگی زمانی سرویس‌ها را کم، Burst را جذب و چند واکنش مستقل را ممکن کند؛ اما به‌خودی‌خود مقیاس‌پذیری، تاب‌آوری یا Exactly-once تجاری نمی‌سازد. این راهنما از تصمیم معماری تا عملیات Production را قدم‌به‌قدم پوشش می‌دهد و نشان می‌دهد چه زمانی API یا Modular monolith انتخاب بهتری است.

معماری Event-Driven چیست؟

در معماری رویدادمحور، یک Producer وقوع تغییری معنادار را به شکل Event منتشر می‌کند؛ Broker/Router آن را نگه می‌دارد یا مسیریابی می‌کند و Consumerها بر اساس Contract خود واکنش نشان می‌دهند. Producer معمولاً لازم نیست Consumerهای فعلی را بشناسد و Consumer لازم نیست هم‌زمان با Producer در دسترس باشد.

Producer → durable publish → broker/topic/stream → subscription/group
                                           ↘ consumer A → effect
                                            ↘ consumer B → effect

این جداسازی زمانی و مقصدی مفید است، اما Dependency حذف نمی‌شود؛ از Endpoint به Schema، Semantics، Ordering، Retention و Operating contract منتقل می‌شود.

Event، Command و Message را یکی نگیرید

مفهوممعنانام نمونهانتظار
Commandدرخواست انجام یک عمل؛ ممکن است رد شودReserveInventoryگیرنده/مالک مشخص، Outcome لازم
Eventواقعیتی که رخ داده و تغییرناپذیر توصیف می‌شودInventoryReservedصفر تا چند واکنش مستقل
Queryدرخواست خواندن بدون تغییر وضعیتGetOrderStatusپاسخ همزمان یا Read model
Messageپاکت انتقال؛ می‌تواند Command/Event/Reply باشدEnvelope + payloadSemantics را Payload/Contract تعیین می‌کند

OrderCreated نباید در عمل به معنای پنهان «ایمیل بفرست و موجودی را کم کن و اگر نشد Producer را Fail کن» باشد. نام Past tense، Owner، شرایط وقوع و Truth source را مشخص کنید. Command را به‌اشتباه Event نامیدن، Coupling را فقط پنهان می‌کند.

EDA با Microservices، CQRS و Event Sourcing یکی نیست

یک Modular monolith هم می‌تواند Event داخلی داشته باشد و Microserviceها می‌توانند با HTTP/gRPC همزمان ارتباط بگیرند. CQRS مسیر Command و Query را جدا می‌کند؛ Event Sourcing تاریخچه رویدادها را منبع حقیقت State می‌گیرد. هیچ‌کدام الزام EDA نیستند.

پیش از شکستن سرویس، مرز دامنه و مالکیت داده را روشن کنید. راهنمای مونولیت ماژولار یا میکروسرویس کمک می‌کند هزینه توزیع را با استقلال واقعی تیم/Deploy مقایسه کنید. EDA برای پوشاندن Monolith نامنظم، فقط Spaghetti را به Topicها منتقل می‌کند.

چه زمانی Event-Driven انتخاب خوبی است؟

نیازEDA چه کمکی می‌کند؟هزینه/پیش‌شرط
چند واکنش به یک واقعیتFan-out مستقل بدون تغییر ProducerContract و Consumer ownership
Burst یا نرخ متغیرBuffer و پردازش با Pace مصرف‌کنندهLag SLO، ظرفیت و Backpressure
پردازش طولانیپاسخ سریع و Completion ناهمزمانStatus/notification/timeout UX
Integration چند سیستمDecoupling زمانی و Replay کنترل‌شدهSchema، Identity و Reconciliation
Audit/stream analyticsLog قابل Replay و ProjectionRetention، Privacy و Storage
استقلال ReleaseProducer/Consumer با Contract پایدارCompatibility test و Governance

چه زمانی EDA انتخاب بدی است؟

  • یک Transaction محلی ساده همه Invariantها را بهتر حفظ می‌کند.
  • کاربر به پاسخ فوری و قطعی برای ادامه همان Interaction نیاز دارد.
  • تیم هنوز Monitoring، On-call، Backup و Incident response پایه ندارد.
  • حجم/پیچیدگی کم است و Broker، Schema registry و Replay ارزش اقتصادی نمی‌سازند.
  • Ownership دامنه و داده مبهم است؛ Eventها Shared database را پنهان می‌کنند.
  • قوانین کسب‌وکار Compensation ندارند و سازگاری دیرهنگام قابل پذیرش نیست.

اغلب معماری Hybrid بهترین است: Query و Commandهای نیازمند پاسخ با API، کارهای طولانی با Queue و Factهای چندمصرف‌کننده با Event. راهنمای قرارداد، امنیت و قابلیت اطمینان API مرز همزمان را تکمیل می‌کند.

پیش از ابزار، Architecture contract بنویسید

Business journey: چه Outcomeی و برای چه کاربری؟
Event/use case: چه واقعیتی، چه Producer و چه Consumers؟
Volume: avg/peak/burst/growth, payload, fan-out
Latency: publish-to-process و end-to-end SLO
Semantics: delivery, ordering scope, duplicate policy
Consistency: invariant, stale window, reconciliation
Failure: producer/db/broker/consumer/dependency/region
Data: class, tenant, residency, retention, deletion
Operations: owner, on-call, replay authority, RTO/RPO
Economics: build/run/people/incident/exit TCO

بدون این Contract، مقایسه Kafka، RabbitMQ یا سرویس Managed شبیه مقایسه خودرو بدون دانستن مسیر و بار است.

چهار سبک رایج رویداد را تفکیک کنید

سبکPayloadمزیتریسک
Event notificationID و حداقل ContextPayload کوچک، داده در OwnerConsumer برای Fetch به Producer وابسته می‌شود
Event-carried state transferState لازم ConsumerRead محلی و Decoupling بیشترPII، Stale copy، Schema و حذف دشوارتر
Event streamرکوردهای مرتب در Key/PartitionReplay و پردازش جریانRetention/lag/partition operational cost
Event sourcingهر تغییر دامنهتاریخچه و ProjectionModel، Migration، Snapshot و حذف بسیار پیچیده

همه Eventها را Full snapshot یا همه Aggregateها را Event-sourced نکنید. سبک را per use case و بر اساس Truth، Privacy، Replay و Latency انتخاب کنید.

Domain Event و Integration Event را جدا کنید

Domain event می‌تواند جزئیات داخلی مدل را حمل کند؛ Integration event Contract عمومی و پایدار میان Boundaryهاست. در Boundary، داده را به زبان مشترک ترجمه و فقط چیزی را منتشر کنید که Consumer مجاز و نیازمند است. نام جدول، Enum داخلی یا Entity کامل را Leak نکنید.

Producer مالک معنای Event است؛ Platform مالک Guardrail و Transport؛ Consumer مالک پردازش، Idempotency و Outcome خود. مالکیت مشترک بدون تصمیم‌گیر نهایی، Schema drift و Incident بی‌صاحب می‌سازد.

Event contract چه فیلدهایی دارد؟

{
  "specversion": "1.0",
  "id": "evt_01...",
  "source": "/commerce/orders",
  "type": "ir.example.order.confirmed.v1",
  "subject": "orders/ord_123",
  "time": "2026-08-11T10:15:30Z",
  "datacontenttype": "application/json",
  "dataschema": "registry://order-confirmed/v1",
  "tenant_id": "tn_42",
  "correlation_id": "journey_abc",
  "causation_id": "cmd_xyz",
  "partition_key": "ord_123",
  "data": {
    "order_id": "ord_123",
    "currency": "IRR",
    "total_minor": 12500000
  }
}

این مثال از Envelope استاندارد CloudEvents الهام می‌گیرد؛ CloudEvents متادیتای مشترک را استاندارد می‌کند، اما Business semantics، تحویل، Ordering یا مجوز را برای شما حل نمی‌کند. ID باید برای Dedup قابل اتکا و Time باید زمان وقوع باشد؛ Ingest time را جدا ثبت کنید.

Schema evolution را پیش از اولین تغییر طراحی کنید

  • Schema را در Registry/Repository با Owner، Compatibility mode و Lifecycle نگه دارید.
  • افزودن Field اختیاری معمولاً امن‌تر از Rename/Delete/تغییر Type است؛ اما Consumer واقعی را تست کنید.
  • Default، Nullability، Enum expansion، Decimal، Currency، Timezone و Unknown field policy را صریح کنید.
  • Producer contract test و Consumer-driven fixtureها را در CI اجرا کنید.
  • Breaking change را با نسخه/Topic یا Dual publish محدود، Migration window و Sunset ببرید.
  • هر Event کاتالوگ قابل جست‌وجو، Sample، Owner، Consumer، SLO و Deprecation date داشته باشد.

ساختار سند AsyncAPI می‌تواند Channel، Message و Operation را Machine-readable کند؛ خود سند جای تست Runtime و توافق معنایی را نمی‌گیرد.

مشکل Dual Write: دیتابیس و Broker یک تراکنش نیستند

الگوی ساده «در دیتابیس Commit کن، بعد Event بفرست» بین دو عمل Gap دارد. اگر برنامه بعد از Commit Crash کند، داده ثبت می‌شود اما Event گم می‌شود. اگر اول Event بفرستد و Transaction Rollback شود، Consumer بر واقعیتی عمل می‌کند که وجود ندارد.

BEGIN
  update business_state ...
  insert into outbox(event_id, aggregate_id, type, payload, occurred_at)
COMMIT

relay/CDC → publish → mark/offset

Transactional Outbox، تغییر State و رکورد Event را در یک Transaction محلی می‌نویسد و Relay/CDC بعداً منتشر می‌کند. راهنمای رسمی Transactional outbox آمازون نیز Duplicate و نیاز به Consumer idempotent و Ordering را جزو ملاحظات می‌داند. Outbox انتشار «حداقل یک‌بار» را محتمل‌تر می‌کند، نه اینکه تمام Side effectها دقیقاً یک‌بار شوند.

Delivery semantics: Exactly-once را دقیق تعریف کنید

Semanticsممکن استکنترل کسب‌وکاری
At-most-onceپیام از دست برود، Duplicate نشودفقط برای Signal قابل چشم‌پوشی
At-least-onceتحویل تکرار شودIdempotency + Dedup + Reconciliation
Broker exactly-onceدر Scope خاص Broker/TransactionDB، Payment، Email و API بیرونی هنوز جدا هستند

«Exactly-once» باید Scope داشته باشد: تولید رکورد؟ خواندن و نوشتن در همان Stream؟ یا اثر تجاری مثل یک‌بار Refund؟ برای اثر بیرونی، Idempotency key، Inbox/processed-event table، Unique constraint و Reconciliation لازم است.

BEGIN
  if exists(processed_events, event_id): ACK duplicate
  validate current state and business invariant
  apply local effect with unique business key
  insert processed_events(event_id, processed_at)
COMMIT
ACK

TTL Dedup باید از بیشترین Replay/Redelivery window بزرگ‌تر باشد. Idempotency فقط «Event ID دیده شده» نیست؛ گاهی Business key مانند payment_id + operation از Duplicate معنایی با Event ID متفاوت جلوگیری می‌کند. راهنمای State machine، Idempotency و Reconciliation فروشگاه این منطق را در عملیات سفارش باز می‌کند.

Ordering جهانی معمولاً لازم و ارزان نیست

Ordering را به Aggregate/Entity محدود کنید: همه Eventهای یک سفارش با Partition key یکسان و Sequence افزایشی. Ordering میان دو سفارش مستقل غالباً ارزش ندارد و Parallelism را کم می‌کند.

  • Out-of-order: Consumer نسخه/Sequence را بررسی و Duplicate/Old event را Ignore یا Buffer کند.
  • Missing event: Gap detector، Timeout و Reconciliation با Source of truth داشته باشید.
  • Late event: Event time و Processing time را جدا و Policy دیررس تعریف کنید.
  • Repartition: تغییر تعداد Partition/Key می‌تواند Ordering و Hotspot را عوض کند؛ Load test کنید.

Retry، Poison message و DLQ را یک سیستم بسازید

خطا را Transient، Permanent، Bug، Data/Schema، Dependency یا Policy طبقه‌بندی کنید. Retry فوری برای خطای دائم فقط Storm می‌سازد. Backoff نمایی با Jitter، سقف تلاش، Timeout و Circuit breaker را متناسب با SLO بگذارید.

receive → validate → process → durable commit → ACK
              ↘ permanent/invalid → quarantine
              ↘ transient → bounded retry/backoff
              ↘ exhausted → DLQ + case + owner + runbook

DLQ قبرستان نیست. Alert، Owner، Reason code، Payload redaction، ابزار Inspect، مسیر Fix، اجازه Replay، Batch limit، Audit و Closure SLA می‌خواهد. Replay کور پس از Fix می‌تواند ایمیل، Refund یا موجودی را دوباره اجرا کند؛ Dry-run و Idempotency را قبلش اثبات کنید.

Backpressure و Capacity را با Lag اداره کنید

Broker ترافیک را حذف نمی‌کند؛ فقط زمان می‌خرد. اگر نرخ ورود طولانی‌مدت از خروج بیشتر باشد، Lag تا پرشدن Retention/Storage رشد می‌کند.

required throughput ≥ peak sustained ingress × fan-out × safety factor
drain time ≈ backlog / (consumer capacity - current ingress)

Publish rate، bytes، partitions/queues، consumer lag age/count، processing latency، error/retry/DLQ، in-flight، ack time، storage و throttling را ببینید. Concurrency را با DB pool، API quota و Ordering محدود کنید؛ Autoscale بر CPU تنها ممکن است Consumer بیشتری بسازد و Database را از کار بیندازد. Batch size، prefetch، pause/resume، rate limit و load shedding را تست کنید.

سازگاری داده: Saga و Reconciliation

Transaction توزیع‌شده را به مجموعه Step با State روشن تبدیل کنید. Saga دو سبک دارد:

سبکمزیتریسکمناسب برای
ChoreographyCoordinator مرکزی کمترFlow پنهان، Loop و Debug سختچند واکنش ساده و کم‌خطر
OrchestrationState/Timeout/Compensation مرئیوابستگی به OrchestratorJourney چندمرحله‌ای و حساس

Compensation معکوس زمان نیست. لغو رزرو ممکن است ممکن باشد، اما پس‌گرفتن SMS یا افشای داده نیست. برای Step برگشت‌ناپذیر، Approval/Point-of-no-return و ترتیب محافظه‌کارانه بسازید. هر Saga Timeout، Retry، Human intervention، Cancel، Reconcile و Audit دارد.

Event Sourcing و CQRS را فقط با مسئله واقعی انتخاب کنید

Event Sourcing برای Audit عمیق، مدل زمانی یا بازسازی Projection ارزش دارد؛ هزینه آن Event evolution، Snapshot، Replay code، Upcaster، حذف/Privacy، Debug و Skill است. Audit log ساده با Event sourcing یکسان نیست. CQRS نیز وقتی Read/Write واقعاً نیاز متفاوت دارند مفید است؛ دو مدل و Lag را بی‌دلیل اضافه نکنید.

ابتدا Outbox + Integration event + Read model محدود را Pilot کنید. اگر Domain به تاریخچه به‌عنوان Truth نیاز ندارد، CRUD/transactional model سالم‌تر می‌ماند.

Broker را با Requirement انتخاب کنید

معیارسؤال
DispatchQueue، Pub/Sub، Stream، routing/filter یا request-reply؟
SemanticsDurability، ack، redelivery، ordering scope و transaction چیست؟
Replay/retentionConsumer مستقل، offset، rewind، compaction و archive لازم است؟
ScaleMessage/s، bytes/s، payload، fan-out، partitions و burst؟
OperationsCluster/upgrade/backup/DR/on-call یا Managed responsibility؟
SecurityIdentity، ACL، tenant، encryption، audit، private network؟
EcosystemSDK، connector، schema، observability، local skill؟
Economics/exitIngest/storage/egress/support/people/migration و export؟

RabbitMQ، Kafka، NATS، Pulsar یا سرویس‌های Cloud «بهترین» عمومی ندارند. با Dataset و Failure نماینده، publish-to-process latency، sustained throughput، replay، rebalance، upgrade، restore و TCO را Pilot کنید. Managed بودن عملیات را کم می‌کند اما Responsibility، Eligibility و Exit را حذف نمی‌کند.

Observability: از Trace تا Outcome

در EDA یک Request ID کافی نیست. event_id، correlation_id برای Journey، causation_id برای زنجیره علت، Aggregate/tenant و Trace context را حمل کنید. Secret/PII را در Span/Log نریزید.

Semantic conventions پیام‌رسانی OpenTelemetry عملیات Create/Send/Receive/Process/Settle و Propagation زمینه Producer/Consumer را تعریف می‌کند؛ وضعیت Stability نسخه را هنگام پیاده‌سازی بررسی کنید. برای طراحی Dashboards و SLO از راهنمای Observability و مانیتورینگ کمک بگیرید.

business fact committed
→ outbox ready
→ publish accepted
→ broker durable
→ delivered/received
→ processed/committed
→ business outcome reconciled

هر شکاف Funnel یک کلاس Failure دارد. SLO را فقط Uptime Broker نگذارید: درصد Eventهای واجد شرایط که در Window به Outcome صحیح می‌رسند، Lag percentile، Duplicate effect، DLQ age و Reconciliation gap مهم‌ترند.

امنیت و حریم خصوصی Event pipeline

  • Producer/Consumer identity قوی، ACL کمینه در Topic/Operation و جداسازی tenant.
  • TLS در مسیر و Encryption at rest با چرخه کلید، Secret rotation و Audit.
  • Schema و اندازه Payload، Type/Range/Enum و Content را قبل از پردازش Validate کنید.
  • Deserialization، Injection، decompression bomb و Poison payload را در Threat model بیاورید.
  • PII/Token/Password/Payment data را Minimize؛ Reference/Tokenize و دسترسی را ثبت کنید.
  • Retention/Archive/Backup/Replay را در Data map و حذف/Legal hold لحاظ کنید.
  • Replay authorization، Rate limit، Nonce/age و Duplicate abuse را کنترل کنید.
  • Connector، Schema registry، Broker plugin و Client library را در SBOM/Patching وارد کنید.

Event تاریخچه عملیاتی و گاهی داده مشتری را تکثیر می‌کند. راهنمای Event/Identity در تحلیل داده مشتری مرز Purpose، Identity و Incrementality را برای داده تجاری توضیح می‌دهد.

آزمون EDA باید Failure را تولید کند

لایهآزمون لازم
ContractSchema valid/invalid، old/new fixture، unknown field/enum
ConsumerDuplicate، out-of-order، missing، late، poison، oversized
TransactionCrash قبل/بعد Commit/Publish/Ack، rollback و relay restart
DependencyDB/API timeout، quota، partial side effect و recovery
Brokernode/network loss، rebalance، partition، storage pressure
Capacitypeak/burst/soak، hot key، backlog drain و downstream saturation
OperationsDLQ triage، replay dry-run/live، restore، failover/failback

Contract test در CI کافی نیست؛ Production-like broker/config و Dataset نماینده لازم است. Ruleهای امنیت/کیفیت را در Pipeline و Paved road قرار دهید؛ راهنمای DevSecOps برای Risk tier، Evidence و Rollback قابل استفاده است.

استقرار و تغییر بدون شکستن Consumer

  1. Schema سازگار و Consumerهای پذیرنده نسخه جدید را اول Deploy کنید.
  2. Producer را با Feature flag یا Canary تغییر دهید.
  3. Dual publish را فقط با مدت، هزینه و Owner محدود اجرا کنید.
  4. Lag/error/outcome را برای Consumer قدیم/جدید مقایسه کنید.
  5. Backfill/Replay را Rate-limit و از Live traffic جدا کنید.
  6. پس از اثبات، Consumer/Field/Topic قدیم را با Sunset contract حذف کنید.

Infra و Workload باید Desired state، Quota و Rollback داشته باشند. اگر Consumerها روی Container platform اجرا می‌شوند، راهنمای Kubernetes و عملیات Container برای Readiness، Shutdown/drain، Resource و HPA مفید است.

مثال ایرانی: Journey سفارش فروشگاه

فرض کنید سفارش با پرداخت آنلاین، رزرو موجودی، انبار، 3PL و پیام‌رسانی درگیر است. State machine نمونه:

Draft → PaymentPending → Paid → InventoryReserved
→ FulfillmentRequested → Shipped → Delivered
                         ↘ Cancelled / RefundPending / Refunded
  1. Callback پرداخت با Idempotency key معتبر، State را در DB تغییر و Outbox PaymentConfirmed می‌سازد.
  2. Inventory Consumer با order_id مرتب و Idempotent رزرو می‌کند؛ نتیجه Event جدید است.
  3. Orchestrator Timeout و Compensation دارد؛ اگر رزرو ممکن نشد، Refund command را یک‌بار و قابل Reconcile صادر می‌کند.
  4. Notification فقط از State معتبر پیام می‌فرستد و Business key از SMS تکراری جلوگیری می‌کند.
  5. Logistics از Event قراردادی استفاده و وضعیت‌های پذیرش/تحویل را برمی‌گرداند؛ راهنمای لجستیک فروشگاه بزرگ Event milestone، 3PL و Reconciliation را پوشش می‌دهد.

Dashboard باید «پرداخت موفق بدون رزرو بیش از پنج دقیقه»، «رزرو بدون Fulfillment»، «Refund معوق» و «Event/State mismatch» را نشان دهد؛ نه فقط تعداد Message.

واقعیت اجرای EDA در ایران

Managed service و دسترسی

Eligibility کشور/سازمان، KYC، Payment، Renewal، Support، Console/API/Registry access و خطر Suspension را در زمان تصمیم از منبع رسمی Provider بررسی کنید. Export، Self-host fallback و Recovery credential را قبل از Incident تست کنید.

شبکه و قطعی

Publisher محلی Buffer محدود، Disk pressure policy، Retry/Jitter و Idempotency دارد. مسیر بین Siteها را از چند ISP آزمایش و مشخص کنید هنگام Partition چه چیزی پذیرفته، رد یا Queue می‌شود؛ پس از اتصال چگونه Replay و Reconcile انجام می‌شود.

هزینه و ظرفیت

هزینه Broker فقط Node/Plan نیست: Storage، replication، network/egress، schema/connector، observability، backup/DR، engineer/on-call، training، incident و exit را در سناریوی نرخ ارز Low/Likely/High بسنجید. Payload بزرگ و Fan-out می‌توانند هزینه انتقال را چند برابر کنند.

زمان و داده

Event time را UTC نگه دارید و نمایش تهران را در UI انجام دهید. PII و داده حساس را نزدیک Source کمینه کنید؛ Location، Retention و حذف را در معماری Event/Archive/Backup یکپارچه ببینید. الزام حقوقی را با متخصص مرتبط بررسی کنید.

TCO و Operating model

EDA TCO = broker/compute/storage/network
+ schema/connectors/platform/observability
+ build/test/migration/replay
+ security/privacy/audit/DR
+ on-call/incident/training
+ vendor/FX/exit reserve

برای هر Topic/Event/Consumer، Owner، Criticality، SLO، Runbook، Data class، Retention، Cost center و Lifecycle ثبت کنید. Platform team Golden path می‌سازد؛ Domain team Semantics و Outcome را مالک است؛ SRE ظرفیت/Incident و Security کنترل/Threat را همراهی می‌کنند. RACI مبهم، DLQ و Schemaهای بدون صاحب می‌سازد.

مهاجرت مرحله‌ای و نقشه ۹۰روزه

  1. روز ۱ تا ۱۵: Journey، Invariant، Failure، Volume/SLO و Architecture contract را بنویسید؛ EDA/no-EDA gate بگیرید.
  2. روز ۱۶ تا ۳۰: یک Aggregate و Fact کم‌خطر، Event contract، Schema/owner و Broker Pilot انتخاب کنید.
  3. روز ۳۱ تا ۴۵: Outbox/relay، یک Consumer idempotent و end-to-end Trace بسازید.
  4. روز ۴۶ تا ۶۰: Duplicate/out-of-order/crash/backlog/DLQ/replay و Security negative test اجرا کنید.
  5. روز ۶۱ تا ۷۵: Shadow/Canary، Reconciliation و business-outcome SLO را در Production محدود بسنجید.
  6. روز ۷۶ تا ۹۰: TCO/Incident/Skill/Gap را مرور و Scale/Hold/Stop کنید؛ فقط سپس Consumer دوم یا Journey حساس‌تر بیفزایید.

اولین Slice نباید پرداخت/Refund غیرقابل جبران باشد. Notification داخلی یا Projection کم‌خطر برای یادگیری Delivery، Schema و Operations مناسب‌تر است.

اشتباه‌های رایج

  • EDA برای هر درخواست و هر سیستم کوچک؛
  • Event نامیدن Command و پنهان‌کردن Coupling؛
  • نوشتن DB و Publish مستقیم بدون Outbox/CDC؛
  • ادعای Exactly-once بدون Scope و Idempotency تجاری؛
  • نیاز به Ordering جهانی و از دست دادن Parallelism؛
  • Retry نامحدود و DLQ بدون Owner/Replay safety؛
  • Autoscale Consumer بدون Capacity downstream؛
  • Schema Registry بدون Semantics/compatibility test؛
  • Event payload کامل با PII برای راحتی Consumer؛
  • Trace فنی بدون Funnel و Outcome تجاری؛
  • Event Sourcing/CQRS برای رزومه معماری؛
  • انتخاب Broker با محبوبیت، نه Failure/TCO/Pilot.

چک‌لیست Production

  • Use case و no-EDA alternative با SLO/TCO مقایسه شده‌اند.
  • Event/Command/Query، Domain/Integration و Source of truth روشن‌اند.
  • Producer/Consumer/Platform/SRE/Security Owner و RACI دارند.
  • Schema، Sample، Compatibility، Lifecycle و Consumer catalog ثبت شده است.
  • Dual write با Outbox/CDC یا راه اثبات‌شده کنترل می‌شود.
  • Delivery/Ordering scope و Duplicate/late/missing policy صریح‌اند.
  • Consumer با Inbox/Business key Idempotent و Reconcileپذیر است.
  • Retry/DLQ/Replay محدود، Auditشده و در Drill آزمایش شده‌اند.
  • Lag/Backpressure/Hot key/downstream saturation ظرفیت‌سنجی شده‌اند.
  • Saga State/Timeout/Compensation/Human intervention دارد.
  • Trace context و event→outcome funnel با Alert/Runbook برقرار است.
  • Identity/ACL/encryption/validation/tenant/PII/retention/replay کنترل شده‌اند.
  • Crash، duplicate، order، partition، restore و backlog drain تست شده‌اند.
  • Provider access/FX/network/data/exit ایران سناریو و جایگزین دارد.
  • Scale/Hold/Stop بر Outcome، SLO، Incident و TCO تصمیم‌گیری می‌شود.

جمع‌بندی: معماری رویدادمحور یک Broker و چند Consumer نیست؛ قرارداد عملیاتی یک Journey توزیع‌شده است. Event را واقعیت دقیق بنویسید، State و Publish را با Outbox متصل کنید، Duplicate و Ordering را در Business کنترل کنید، Failure و Replay را قبل از Production تمرین دهید و موفقیت را از Commit تا Outcome بسنجید. اگر این هزینه برای مسئله شما ارزش نمی‌سازد، API یا Modular monolith انتخاب بالغ‌تری است.

پرسش‌های متداول

معماری Event-Driven به زبان ساده چیست؟

سبکی است که در آن اجزا وقوع تغییرهای معنادار را به شکل Event منتشر می‌کنند و Consumerها ناهمزمان واکنش نشان می‌دهند. Producer معمولاً Consumer را مستقیم صدا نمی‌زند، اما همه طرف‌ها به Contract، تحویل و عملیات مشترک وابسته‌اند.

تفاوت Queue و Pub/Sub چیست؟

در Queue معمولاً یک Job میان Workerهای یک گروه تقسیم و یک‌بار توسط یکی از آن‌ها پردازش می‌شود؛ در Pub/Sub چند Subscription یا Consumer group مستقل هرکدام نسخه منطقی Event را می‌گیرند. جزئیات Durability، Ack و Replay به Broker بستگی دارد.

آیا Kafka برای هر معماری Event-Driven لازم است؟

خیر. Queue، Pub/Sub، Stream، Routing، Replay، Ordering، نرخ، Skill، عملیات و TCO تعیین می‌کنند چه Broker یا سرویس Managed مناسب است. حتی برای Use case ساده ممکن است Queue دیتابیس یا Job runner کافی باشد.

چگونه از پردازش تکراری Event جلوگیری کنیم؟

Consumer را Idempotent کنید: Event ID و Business key را در همان Transaction اثر محلی ثبت، Unique constraint بگذارید و پس از Commit ACK کنید. برای Side effect بیرونی Idempotency key و Reconciliation لازم است؛ وعده Broker به‌تنهایی کافی نیست.

چه زمانی Event Sourcing انتخاب مناسبی نیست؟

وقتی تاریخچه رویداد منبع حقیقت دامنه نیست و Audit log یا CRUD معمولی نیاز را حل می‌کند. Event Sourcing هزینه Schema evolution، Replay، Projection، Snapshot، Privacy، Debug و مهارت دارد و نباید صرفاً چون EDA استفاده می‌شود اضافه شود.

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *