{
  "title": "سیاست رویداد ها",
  "slug": "team/platform/standards/event-standard",
  "url": "/docs/team/platform/standards/event-standard",
  "frontmatter": {},
  "sections": [
    {
      "level": 1,
      "heading": "سیاست رویداد ها",
      "content": "> Event Standard"
    },
    {
      "level": 2,
      "heading": "مفهوم کلیدی",
      "content": "```text\nEvent Catalog = فقط معنی و قرارداد\nNATS = فقط حمل‌کننده پیام\n```\n\nرویدادها در سطح **معنا و قرارداد** در کاتالوگ رویدادها تعریف می‌شوند.\nNATS JetStream صرفاً نقش **حمل‌کننده پیام** را دارد و هیچ اطلاعاتی از معنای رویداد ندارد.\n\n---"
    },
    {
      "level": 2,
      "heading": "۱. هدف سند",
      "content": "این سند استاندارد رسمی پلتفرم برای طراحی، انتشار، نسخه‌بندی و مصرف رویدادها است.\n\nهر رویدادی که توسط هر سرویس منتشر می‌شود باید از این استاندارد پیروی کند.\n\nاین سند منبع حقیقت (Source of Truth) برای:\n\n* تولیدکنندگان رویداد (Publishers)\n* مصرف‌کنندگان رویداد (Consumers)\n* بازبینان معماری\n* توسعه‌دهندگان سرویس‌های جدید\n\nاست.\n\n**توجه:** ساختار Event Envelope در `nons-api/contracts/envelope.proto` (Proto) تعریف می‌شود. این سند استانداردهای محتوایی و فرآیندی را مشخص می‌کند. این دو مکمل یکدیگر هستند.\n\n---"
    },
    {
      "level": 1,
      "heading": "۲. اصول طراحی رویداد",
      "content": ""
    },
    {
      "level": 2,
      "heading": "Event یک حقیقت گذشته است",
      "content": "رویداد باید بیان‌کننده اتفاقی باشد که قبلاً رخ داده است.\n\nدرست:\n\n```text\nnons.order.completed\n```\n\nنادرست:\n\n```text\nnons.marketplace.complete_order\n```\n\nیا\n\n```text\nnons.order.complete\n```\n\nزیرا این‌ها Command هستند نه Event.\n\n---"
    },
    {
      "level": 2,
      "heading": "Event باید Immutable باشد",
      "content": "پس از انتشار، هیچ Eventی نباید تغییر کند.\n\nاگر وضعیت جدیدی ایجاد شد، Event جدید منتشر می‌شود.\n\nدرست:\n\n```text\nnons.order.created\nnons.order.completed\nnons.order.refunded\n```\n\nنادرست:\n\n```text\nnons.order.updated\n```\n\nبرای هر تغییر وضعیت مهم.\n\n---"
    },
    {
      "level": 2,
      "heading": "Event باید Self-Contained باشد",
      "content": "مصرف‌کننده نباید برای فهم Event مجبور به فراخوانی Publisher شود.\n\nترجیح:\n\n```json\n{\n  \"sellerId\": \"usr_123\",\n  \"rating\": 2\n}\n```\n\nبه جای:\n\n```json\n{\n  \"reviewId\": \"rev_123\"\n}\n```\n\nکه مصرف‌کننده را مجبور به Query می‌کند.\n\n---"
    },
    {
      "level": 2,
      "heading": "Event نباید شامل منطق تجاری باشد",
      "content": "درست:\n\n```json\n{\n  \"status\": \"BANNED\"\n}\n```\n\nنادرست:\n\n```json\n{\n  \"shouldLogout\": true\n}\n```\n\nتصمیم‌گیری وظیفه Consumer است.\n\n---"
    },
    {
      "level": 1,
      "heading": "۳. قرارداد نام‌گذاری",
      "content": ""
    },
    {
      "level": 2,
      "heading": "Subject Convention",
      "content": "```text\nnons.<domain>.<entity>.<action>\n```"
    },
    {
      "level": 3,
      "heading": "Domain",
      "content": "نام bounded context یا سرویس مالک (مفرد).\n\nنمونه:\n\n```text\nauth\niam\nuser\nmarketplace\norder\npayment\nwallet\nsettlement\ncurrency\nchat\ndispute\nreview\nmoderation\nzone\nboost\nsearch\nnotification\nstorage\nanalytics\nkyc\n```\n\n> **توجه:** لیست کامل ۲۰ دامنه مصوب در `ADR-EVENT-001` ثبت شده است. domain جدید فقط با ADR جدید قابل اضافه شدن است.\n\n> **نکته دامنه `iam`:** رویدادهای IAM از بخش `<past_action>` با underscore برای کلمات چندبخشی استفاده می‌کنند (مثلاً `nons.iam.user.status_changed`). کاتالوگ کامل در `nons-api/catalog/events/iam/events.yaml`.\n\n---"
    },
    {
      "level": 3,
      "heading": "Entity",
      "content": "موجودیت اصلی.\n\nنمونه:\n\n```text\nuser\norder\nproduct\nreview\nmessage\ntransaction\n```\n\n---"
    },
    {
      "level": 3,
      "heading": "Action",
      "content": "باید فعل گذشته ساده (Past Simple) باشد — رویداد حقیقتی از گذشته را بیان می‌کند.\n\nنمونه:\n\n```text\ncreated\nregistered\ncompleted\npublished\nresolved\nverified\nsuspended\n```\n\n> ⚠️ `updated` ممنوع است — رویدادها باید تغییر وضعیت‌های مشخص را نشان دهند. از رویدادهای مشخص‌تر مانند `status_changed` استفاده کنید.\n\n---"
    },
    {
      "level": 3,
      "heading": "قوانین",
      "content": "مجاز:\n\n```text\nnons.order.completed\n```\n\nغیرمجاز:\n\n```text\nnons.orders.completed\n```\n\n```text\nnons.order.complete\n```\n\n```text\nnons.Marketplace.Order.Completed\n```\n\n---"
    },
    {
      "level": 1,
      "heading": "۴. قرارداد پاکت رویداد",
      "content": "تمام Eventها بدون استثنا باید از Envelope استاندارد استفاده کنند.\n\n```json\n{\n  \"id\": \"evt_01j2k3m4n5p6q7r8\",\n  \"subject\": \"nons.order.completed\",\n  \"version\": \"1.0\",\n  \"timestamp\": \"2026-06-03T10:54:00.000Z\",\n  \"source\": \"order-service\",\n  \"traceId\": \"trace_abc123\",\n  \"payload\": {}\n}\n```\n\n---"
    },
    {
      "level": 2,
      "heading": "الزامات فیلدها",
      "content": ""
    },
    {
      "level": 3,
      "heading": "id",
      "content": "* یکتا در کل پلتفرم\n* فرمت:\n\n```text\nevt_<ulid>\n```\n\n* هرگز نباید دوباره استفاده شود\n\n---"
    },
    {
      "level": 3,
      "heading": "subject",
      "content": "باید دقیقاً با Subject منتشر شده در NATS یکسان باشد.\n\n---"
    },
    {
      "level": 3,
      "heading": "version",
      "content": "نسخه Payload.\n\nفرمت:\n\n```text\nmajor.minor\n```\n\nنمونه:\n\n```text\n1.0\n1.1\n2.0\n```\n\n---"
    },
    {
      "level": 3,
      "heading": "timestamp",
      "content": "* UTC\n* ISO-8601\n* زمان انتشار Event\n\nنه زمان پردازش.\n\n---"
    },
    {
      "level": 3,
      "heading": "source",
      "content": "نام سرویس منتشرکننده.\n\nنمونه:\n\n```text\niam-service\npayment-service\norder-service\n```\n\n---"
    },
    {
      "level": 3,
      "heading": "traceId",
      "content": "شناسه Correlation بین سرویس‌ها.\n\nباید از درخواست اولیه propagate شود.\n\n---"
    },
    {
      "level": 1,
      "heading": "۵. طراحی Payload",
      "content": ""
    },
    {
      "level": 2,
      "heading": "قواعد عمومی",
      "content": "Payload باید:\n\n* حداقلی باشد\n* کافی باشد\n* قابل نسخه‌بندی باشد\n* بدون داده اضافی باشد\n\n---"
    },
    {
      "level": 2,
      "heading": "فیلدهای ممنوع",
      "content": "نباید موارد زیر داخل Event قرار گیرند:\n\n```json\n{\n  \"password\": \"...\",\n  \"token\": \"...\",\n  \"refreshToken\": \"...\",\n  \"privateKey\": \"...\"\n}\n```\n\n---"
    },
    {
      "level": 2,
      "heading": "داده‌های PII",
      "content": "فقط در صورت نیاز واقعی.\n\nنمونه:\n\n```json\n{\n  \"body\": \"chat message\"\n}\n```\n\nدر `chat.message.sent`\n\nمجاز است زیرا Moderation به آن نیاز دارد.\n\n---"
    },
    {
      "level": 2,
      "heading": "داده‌های مشتق‌شده",
      "content": "از انتشار داده‌ای که Consumer می‌تواند خودش محاسبه کند خودداری شود.\n\nنادرست:\n\n```json\n{\n  \"sellerLevel\": \"gold\"\n}\n```\n\nاگر از سایر داده‌ها قابل محاسبه است.\n\n---"
    },
    {
      "level": 1,
      "heading": "۶. Versioning",
      "content": ""
    },
    {
      "level": 2,
      "heading": "Minor Version",
      "content": "سازگار با عقب.\n\nنمونه:\n\n```json\n{\n  \"userId\": \"usr_1\",\n  \"status\": \"ACTIVE\",\n  \"country\": \"DE\"\n}\n```\n\nافزودن:\n\n```json\n{\n  \"country\": \"DE\"\n}\n```\n\nنسخه:\n\n```text\n1.0 -> 1.1\n```\n\n---"
    },
    {
      "level": 2,
      "heading": "Major Version",
      "content": "Breaking Change.\n\nنمونه:\n\nحذف:\n\n```json\n\"userId\"\n```\n\nیا\n\nتغییر نوع:\n\n```json\n\"userId\": 123\n```\n\nبه جای\n\n```json\n\"userId\": \"123\"\n```\n\nنسخه:\n\n```text\n1.x -> 2.0\n```\n\n---"
    },
    {
      "level": 2,
      "heading": "قوانین سازگاری",
      "content": "Publisher نباید Consumerهای موجود را بشکند.\n\nتا زمان حذف کامل نسخه قبلی باید هر دو نسخه پشتیبانی شوند.\n\n---"
    },
    {
      "level": 1,
      "heading": "۷. مسئولیت Publisher",
      "content": "Publisher موظف است:\n\n* Schema را اعتبارسنجی کند.\n* Event را فقط پس از Commit موفق منتشر کند.\n* از انتشار Event تکراری جلوگیری کند.\n* TraceId را حفظ کند.\n* Version صحیح را ارسال کند.\n\n---"
    },
    {
      "level": 1,
      "heading": "۸. مسئولیت Consumer",
      "content": "Consumer موظف است:\n\n* Idempotent باشد.\n* Version را بررسی کند.\n* Eventهای ناشناخته را نادیده بگیرد.\n* خطاها را Retry کند.\n* وابسته به Ordering بین Subjectها نباشد.\n\n---"
    },
    {
      "level": 1,
      "heading": "۹. Ordering و Delivery",
      "content": ""
    },
    {
      "level": 2,
      "heading": "Ordering",
      "content": "تضمین فقط در سطح:\n\n```text\nsubject + publisher\n```\n\nوجود دارد.\n\nبین Subjectهای مختلف هیچ تضمینی وجود ندارد.\n\n---"
    },
    {
      "level": 2,
      "heading": "Delivery",
      "content": "تحویل حداقل یک بار:\n\n```text\nAt-Least-Once\n```\n\nبنابراین:\n\n```text\nDuplicate Delivery Possible\n```\n\n---"
    },
    {
      "level": 2,
      "heading": "Idempotency",
      "content": "شناسه Event باید کلید Idempotency باشد.\n\n```text\nevent.id\n```\n\n---"
    },
    {
      "level": 1,
      "heading": "۱۰. مدیریت خطا و DLQ",
      "content": "(محتوای فعلی DLQ تقریباً بدون تغییر منتقل شود.)\n\nزیرا این بخش استاندارد پلتفرم است نه وابسته به نوع Event.\n\n---"
    },
    {
      "level": 1,
      "heading": "۱۱. فرآیند افزودن Event جدید",
      "content": "قبل از ایجاد Event جدید باید بررسی شود:\n\n1. آیا Event موجود نیاز را پوشش می‌دهد؟\n2. آیا این تغییر واقعاً یک Event است یا Command؟\n3. آیا Payload حداقلی است؟\n4. آیا داده حساس در Payload وجود دارد؟\n5. آیا Version مشخص شده است؟\n6. آیا Consumerهای مورد انتظار شناسایی شده‌اند؟\n7. آیا قرارداد در Catalog ثبت شده است؟\n\n---"
    },
    {
      "level": 1,
      "heading": "۱۲. رجیستری قراردادها",
      "content": "در انتهای سند، به جای بدنه اصلی فعلی، فقط یک رجیستری قرار می‌گیرد:\n\n| Subject                                | Version | Owner               | Schema |\n| -------------------------------------- | ------- | ------------------- | ------ |\n| `nons.iam.user.status_changed`         | 1.0     | iam-service         | لینک   |\n| `nons.iam.user.role_assigned`          | 1.0     | iam-service         | لینک   |\n| `nons.order.completed`     | 1.0     | order-service | لینک   |\n| ...                           | ...     | ...                 | ...    |\n\nو سپس برای هر Event یک بخش مستقل:\n\n```text\nContract Reference\n```\n\nشامل:\n\n* Subject\n* Publisher\n* Consumers\n* Schema\n* Business Meaning\n* Version History\n* Examples"
    }
  ]
}