{
  "title": "راهنمای تولید OpenAPI",
  "slug": "team/platform/api/openapi-guidelines",
  "url": "/docs/team/platform/api/openapi-guidelines",
  "frontmatter": {
    "layout": "doc",
    "title": "راهنمای تولید OpenAPI",
    "description": "استاندارد رسمی تولید، اعتبارسنجی و انتشار OpenAPI در پلتفرم نونز",
    "version": "1.2.0",
    "status": "APPROVED",
    "author": "Platform Team",
    "owner": "Platform Team",
    "created_at": "2026-07-02",
    "updated_at": "2026-07-08",
    "tags": "",
    "reviewers": ""
  },
  "sections": [
    {
      "level": 1,
      "heading": "راهنمای تولید OpenAPI",
      "content": "**OpenAPI Guidelines**\n\nنسخه ۱.۲ | الزامی برای تمام سرویس‌ها\n\n> این سند استاندارد تولید، اعتبارسنجی و انتشار OpenAPI در پلتفرم نونز را مشخص می‌کند. OpenAPI تنها منبع رسمی قرارداد API است که Registry و مصنوعات کلاینت از آن ساخته می‌شوند.\n\n---"
    },
    {
      "level": 2,
      "heading": "۱. نسخه OpenAPI",
      "content": "تمام سرویس‌ها باید OpenAPI **نسخه 3.1** منتشر کنند.\n\n```yaml\nopenapi: \"3.1.0\"\n```\n\nنسخه‌های قدیمی‌تر (3.0.x, 2.0) مجاز نیستند. OpenAPI 3.1 با JSON Schema Draft 2020-12 سازگار است.\n\n---"
    },
    {
      "level": 2,
      "heading": "۲. مسئولیت تولید",
      "content": "هر سرویس مسئول تولید OpenAPI خودش است.\n\n```\nهر سرویس → OpenAPI خود را تولید می‌کند\n```\n\n**قوانین:**\n- `nons` هیچ Contractی تولید نمی‌کند — `nons` مصرف‌کننده OpenAPI است\n- Registry از OpenAPI ساخته می‌شود — Registry تولیدکننده OpenAPI نیست\n- هر سرویس مالک OpenAPI خود است و مسئول صحت آن\n\n---"
    },
    {
      "level": 2,
      "heading": "۳. ابزار رسمی تولید (`nons-openapi`)",
      "content": "از نسخه ۱.۲ این استاندارد، تولید OpenAPI از طریق **ابزار واحد و مشترک `nons-openapi`** انجام می‌شود. این ابزار در مخزن مستقل [`github.com/NonsCore/openapi`](https://github.com/NonsCore/openapi) نگهداری شده و به‌صورت باینری منتشر می‌شود (GitHub Releases).\n\n> **چرا ابزار مشترک؟** پیش از این هر سرویس یک ژنراتور اختصاصی (`cmd/openapi-gen/main.go`) و اسکریپت‌های تکراری داشت. این رویکرد باعث تکرار ~۸۵٪ کد و ناهماهنگی خروجی می‌شد. `nons-openapi` این منطق را در یک ابزار واحد با معماری Adapter متمرکز می‌کند."
    },
    {
      "level": 3,
      "heading": "معماری Adapter",
      "content": "ابزار `nons-openapi` هسته‌ای زبان‌مستقل دارد و از طریق Adapterها با اکوسیستم‌های مختلف کار می‌کند:\n\n| زبان/اکوسیستم | Adapter | وضعیت |\n|----------------|---------|-------|\n| Go | `go-ast` (پارس annotationهای Handler + Struct Tags) | رسمی |\n| هر زبان (fallback) | `manifest-generic` (خواندن مانیفست OpenAPI از پیش نوشته‌شده) | رسمی |\n| Python / NestJS | Adapter اختصاصی (نقشه راه آینده) | برنامه‌ریزی‌شده |\n\nهر سرویس تنها یک فایل کانفیگ `openapi-config.yaml` دارد که Adapter و تنظیمات آن را مشخص می‌کند. جزئیات مدل `go-ast` در بخش ۱۳ آمده است."
    },
    {
      "level": 3,
      "heading": "نسخه‌بندی ابزار",
      "content": "- ابزار با SemVer نسخه‌گذاری می‌شود و با tag گیت (`vX.Y.Z`) منتشر می‌گردد.\n- هر پروژه می‌تواند نسخه ابزار را در `.nons-config.json` (فیلد `tool_version_pinned`) قفل کند.\n- دستور `nons-openapi version --check` هماهنگی نسخه نصب‌شده با نسخه قفل‌شده را بررسی می‌کند.\n\n---"
    },
    {
      "level": 2,
      "heading": "۴. پایپلاین اعتبارسنجی (Validation Pipeline)",
      "content": "OpenAPI باید در CI اعتبارسنجی شود. پایپلاین رسمی:\n\n```\nOpenAPI Source (سرویس)\n      │\n      ▼\nOpenAPI Lint (اعتبارسنجی ساختار)\n      │\n      ▼\nSchema Validation (بررسی هماهنگی با طرح‌های داده)\n      │\n      ▼\nBreaking Change Detection (شناسایی تغییرات مخرب)\n      │\n      ▼\nContract Test (تست‌های قرارداد)\n      │\n      ▼\nPublish (انتشار به Registry)\n```\n\n**توضیح هر مرحله:**\n\n| مرحله | ابزار | توضیح |\n|-------|----------------|--------|\n| Generate | `nons-openapi generate` | تولید `docs/openapi.yaml` از منبع (اعتبارسنجی ساختاری درون‌ساخت) |\n| Lint | `nons-openapi validate` | اعتبارسنجی ساختار OpenAPI 3.1 |\n| Breaking Change | `nons-openapi diff` (مقایسه با `openapi.prev.yaml`) | شناسایی تغییرات شکننده نسبت به نسخه قبلی |\n| Publish | CI Pipeline | انتشار به Registry پلتفرم |\n\n**قانون مهم:** هرگونه تغییر شکننده (حذف فیلد، تغییر نام فیلدهای اجباری، تغییر کد وضعیت HTTP) بدون افزایش نسخه API،Pipeline را متوقف می‌کند.\n\n---"
    },
    {
      "level": 2,
      "heading": "۵. فراداده استاندارد (Standard Metadata)",
      "content": "تمام سرویس‌ها باید فراداده استاندارد زیر را در OpenAPI خود داشته باشند:\n\n```yaml\nopenapi: \"3.1.0\"\ninfo:\n  title: \"<Service Name> API\"          # مثال: User Service API\n  description: \"<توضیح کوتاه سرویس>\"\n  version: \"1.0.0\"\n  contact:\n    name: Backend Team\n    url: https://github.com/orgs/nons-dev/teams/backend\n  license:\n    name: Proprietary\nservers:\n  - url: https://api.nons.io/v1\n    description: Production\n  - url: https://staging.api.nons.io/v1\n    description: Staging\nexternalDocs:\n  description: API Design Guidelines\n  url: https://docs.nons.dev/team/platform/api/api-design-guidelines\ntags:\n  - name: <DomainName>\n    description: <توضیح دامنه>\n```\n\n| فیلد | الزامی | توضیح |\n|------|--------|--------|\n| `info.title` | بله | نام سرویس — باید با `<Service Name> API` تطابق داشته باشد |\n| `info.version` | بله | نسخه سند OpenAPI (هماهنگ با نسخه API) |\n| `info.description` | توصیه | توضیح کوتاه |\n| `info.contact` | توصیه | تیم مسئول |\n| `info.license` | توصیه | Proprietary |\n| `servers` | بله | حداقل یک سرور — Production الزامی است |\n| `externalDocs` | توصیه | لینک به API Design Guidelines |\n| `tags` | بله | دسته‌بندی اندپوینت‌ها |\n\n---"
    },
    {
      "level": 2,
      "heading": "۶. قوانین مسیر (Path Rules)",
      "content": "```\n✅ تمام مسیرها باید با /v1/<domain> شروع شوند\n✅ مثال: /v1/users/me, /v1/orders/{orderId}\n❌ مسیر بدون پیشوند: /me, /profile\n❌ تکرار نسخه: /v1/v1/users\n```\n\nپیشوند مسیر و نگاشت آن به Tagها از طریق بخش `tag_rules` در `openapi-config.yaml` تنظیم می‌شود (قوانین به ترتیب تعریف، از خاص به عام، ارزیابی می‌شوند).\n\n---"
    },
    {
      "level": 2,
      "heading": "۷. طرح‌های امنیتی (Security Schemes)",
      "content": "طرح‌های امنیتی رسمی پلتفرم — **باید عیناً** در همه سرویس‌ها تعریف شوند:\n\n```yaml\ncomponents:\n  securitySchemes:\n    CookieSession:\n      type: apiKey\n      in: cookie\n      name: session\n      description: \"Admin panel default — Cookie-based authentication\"\n    BearerJWT:\n      type: http\n      scheme: bearer\n      bearerFormat: JWT\n      description: \"Services, CLI, mobile — JWT in Authorization header\"\n    ApiKey:\n      type: apiKey\n      in: header\n      name: X-API-Key\n      description: \"External integrations — X-API-Key header\"\n```\n\n**قوانین استفاده:**\n\n| نوع اندپوینت | Security Field | توضیح |\n|---|---|---|\n| Public (بدون احراز هویت) | `security: []` | آرایه خالی — فیلد باید وجود داشته باشد |\n| Protected (نیاز به JWT) | `- BearerJWT: []` | BearerJWT در Authorization header |\n| Admin (پنل مدیریت) | `- CookieSession: []` | Cookie-based |\n| External Integration | `- ApiKey: []` | X-API-Key header |\n\n---"
    },
    {
      "level": 2,
      "heading": "۸. ساختار `x-nons` (Extension Metadata)",
      "content": "هر operation باید بلوک `x-nons` داشته باشد. این فیلد اطلاعات Registry پلتفرم را حمل می‌کند:\n\n```yaml\nx-nons:\n  token: users.getProfile      # الزامی: domain.action\n  service: user-service        # الزامی: نام سرویس\n  visibility: Public           # الزامی: Public | Protected\n  timeout: 5s                  # اختیاری: حداکثر زمان پاسخ\n  retry: 3                     # اختیاری: تعداد Retry در خطای شبکه\n  cache: 60s                   # اختیاری: مدت Cache (فقط برای Public GET)\n```"
    },
    {
      "level": 3,
      "heading": "قوانین `token`",
      "content": "فرمت: `<domain>.<action>` — فقط حروف، اعداد و Underscore مجاز\n\n```\n✅ users.getProfile\n✅ users.updateUsername\n✅ orders.create_item\n❌ users.get-profile    (خط تیره مجاز نیست)\n❌ getProfile           (باید domain.action باشد)\n❌ users.              (action خالی)\n```\n\nRegex validator: `^[a-zA-Z0-9_]+\\.[a-zA-Z0-9_]+$`"
    },
    {
      "level": 3,
      "heading": "قوانین `visibility`",
      "content": "| مقدار | کاربرد |\n|-------|---------|\n| `Public` | اندپوینت‌های عمومی — نیاز به احراز هویت ندارند |\n| `Protected` | اندپوینت‌های احراز هویت‌شده — BearerJWT الزامی است |\n\n**ارتباط visibility با security:**\n- `visibility: Public` → `security: []`\n- `visibility: Protected` → `security: - BearerJWT: []`"
    },
    {
      "level": 3,
      "heading": "قوانین `cache`",
      "content": "فقط برای اندپوینت‌های Public GET که پاسخ‌شان قابل کش شدن است:\n\n```yaml"
    },
    {
      "level": 1,
      "heading": "Cache فعال - 60 ثانیه",
      "content": "x-nons:\n  visibility: Public\n  cache: 60s"
    },
    {
      "level": 1,
      "heading": "بدون Cache",
      "content": "x-nons:\n  visibility: Protected\n  cache: 0s\n```\n\n---"
    },
    {
      "level": 2,
      "heading": "۹. قوانین `operationId`",
      "content": "```\n✅ هر operation باید operationId داشته باشد\n✅ operationId باید با نام تابع Handler در کد منبع یکسان باشد\n✅ PascalCase — مثال: GetProfile, UpdateUsername\n❌ camelCase یا snake_case\n❌ تکرار operationId در یک سرویس\n```\n\n**فلسفه:** این تطابق به ژنراتور اجازه می‌دهد بدون کانفیگ اضافه، operationId را از نام تابع استخراج کند.\n\n---"
    },
    {
      "level": 2,
      "heading": "۱۰. Envelope استاندارد پاسخ",
      "content": ""
    },
    {
      "level": 3,
      "heading": "موفقیت (Success Response)",
      "content": "تمام پاسخ‌های موفق باید داخل envelope `data` قرار گیرند:\n\n```yaml\n\"200\":\n  description: \"...\"\n  content:\n    application/json:\n      schema:\n        type: object\n        required:\n          - data\n        properties:\n          data:\n            $ref: '#/components/schemas/YourResponseSchema'\n```\n\nدر کد Go:\n```go\nfunc sendSuccess(w http.ResponseWriter, data any) {\n    sendJSON(w, http.StatusOK, map[string]any{\"data\": data})\n}\n```"
    },
    {
      "level": 3,
      "heading": "خطا (Error Response)",
      "content": "تمام پاسخ‌های خطا باید از `ErrorResponse` استفاده کنند:\n\n```yaml\n\"400\":\n  description: \"...\"\n  content:\n    application/json:\n      schema:\n        $ref: '#/components/schemas/ErrorResponse'\n```\n\n**Schema های ثابت خطا** (در تمام سرویس‌ها باید عیناً وجود داشته باشند):\n\n```yaml\ncomponents:\n  schemas:\n    ValidationErrorDetail:\n      type: object\n      required:\n        - field\n        - message\n      properties:\n        field:\n          type: string\n        message:\n          type: string\n\n    ErrorDetails:\n      type: object\n      required:\n        - code\n        - message\n      properties:\n        code:\n          type: string        # مثال: USER_NOT_FOUND, VALIDATION_ERROR\n        message:\n          type: string        # پیام human-readable\n        details:\n          type: array\n          items:\n            $ref: '#/components/schemas/ValidationErrorDetail'\n\n    ErrorResponse:\n      type: object\n      required:\n        - error\n      properties:\n        error:\n          $ref: '#/components/schemas/ErrorDetails'\n```\n\n---"
    },
    {
      "level": 2,
      "heading": "۱۱. کدهای HTTP استاندارد",
      "content": "| کد | نام | کاربرد |\n|----|-----|----------|\n| `200` | OK | عملیات موفق |\n| `400` | Bad Request | ورودی نامعتبر، فرمت اشتباه |\n| `401` | Unauthorized | احراز هویت نشده |\n| `403` | Forbidden | احراز هویت شده ولی دسترسی ندارد |\n| `404` | Not Found | منبع یافت نشد |\n| `409` | Conflict | تعارض (مثال: username تکراری) |\n| `500` | Internal Server Error | خطای داخلی سرور |\n\n**قانون:** حذف هر کد HTTP که در نسخه قبلی وجود داشته، Breaking Change محسوب می‌شود.\n\n---"
    },
    {
      "level": 2,
      "heading": "۱۲. محدودیت محتوای OpenAPI",
      "content": "**هیچ Metadata مربوط به UI، Layout، Template، Frontend یا Builder داخل OpenAPI قرار نگیرد.**\n\n```\n✅ مجاز: description, summary, tags, operationId, parameters, schemas, x-nons\n❌ ممنوع: x-ui-layout, x-template, x-frontend-component, x-builder-config\n```\n\nOpenAPI فقط قرارداد Backend است. تصمیمات نمایشی (UI) در Registry مدیریت می‌شوند.\n\n---"
    },
    {
      "level": 2,
      "heading": "۱۳. مدل Adapter مربوط به Go (`go-ast`)",
      "content": "> این بخش مختص سرویس‌های Go است. Adapter `go-ast` بخشی از ابزار `nons-openapi` است؛ نیازی به نوشتن ژنراتور اختصاصی در هر سرویس نیست."
    },
    {
      "level": 3,
      "heading": "معماری کلی",
      "content": "Adapter `go-ast` از دو منبع در کد سرویس و یک فایل کانفیگ می‌خواند:\n\n```\ninternal/handler/handler.go   →  Endpoint definitions (Annotations)\ninternal/handler/models.go    →  Request/Response Schema (Struct Tags)\nopenapi-config.yaml           →  Adapter settings, tag_rules, schemas تزریقی\n        │                                │\n        └──────────────┬─────────────────┘\n                       ▼\n           nons-openapi (go-ast adapter)\n                       │\n                       ▼\n            docs/openapi.yaml (تولید خودکار)\n```\n\nمسیر فایل‌های Handler و Models از طریق `openapi-config.yaml` تنظیم می‌شود (فیلدهای `handler_paths` و `models_path` در بخش `go-ast`)."
    },
    {
      "level": 3,
      "heading": "منبع ۱: Annotation های Handler",
      "content": "هر تابع Handler در `internal/handler/handler.go` باید Comment‌های annotation‌دار داشته باشد:\n\n```go\n// UpdateUsername updates the current user's username.\n// @route PATCH /v1/users/me/username\n// @summary Update user username\n// @description Update username after validating uniqueness requirements\n// @security BearerJWT\n// @x-nons.token users.updateUsername\n// @x-nons.service user-service\n// @x-nons.visibility Protected\n// @x-nons.timeout 5s\n// @x-nons.retry 0\n// @request UpdateUsernameRequest\n// @response 200 SuccessResponse UpdateUsernameResponse \"Username updated successfully\"\n// @response 400 ErrorResponse - \"Invalid username format\"\n// @response 401 ErrorResponse - \"Unauthorized\"\n// @response 404 ErrorResponse - \"User not found\"\n// @response 409 ErrorResponse - \"Username already taken\"\n// @response 500 ErrorResponse - \"Internal server error\"\nfunc (h *UserHandler) UpdateUsername(w http.ResponseWriter, r *http.Request) {\n    // ...\n}\n```"
    },
    {
      "level": 4,
      "heading": "فرمت هر Annotation",
      "content": "| Annotation | فرمت | مثال |\n|---|---|---|\n| `@route` | `METHOD /path` | `@route PATCH /v1/users/me/username` |\n| `@summary` | متن | `@summary Update user username` |\n| `@description` | متن | `@description Update username after validating...` |\n| `@security` | نام scheme یا `Public` | `@security BearerJWT` |\n| `@x-nons.<key>` | `<key> <value>` | `@x-nons.token users.updateUsername` |\n| `@request` | نام Struct | `@request UpdateUsernameRequest` |\n| `@accepts` | Content-Type (اختیاری) | `@accepts application/x-www-form-urlencoded` |\n| `@param` | `name in type required \"desc\"` | `@param publicId path string true \"User Public ID\"` |\n| `@response` | `status envelope dataType \"desc\"` | `@response 200 SuccessResponse UpdateUsernameResponse \"...\"` |\n\n> **`@accepts`:** به‌صورت پیش‌فرض بدنه درخواست `application/json` است. اگر اندپوینتی فرم‌محور باشد (مثل جریان‌های احراز هویت مبتنی بر Kratos)، با `@accepts application/x-www-form-urlencoded` نوع محتوای همان اندپوینت را override کنید. مقدار پیش‌فرض کل سرویس از طریق `request_media_type` در `openapi-config.yaml` قابل تنظیم است."
    },
    {
      "level": 4,
      "heading": "فرمت `@response`",
      "content": "```\n@response <status> <envelope> <dataType> \"<description>\"\n```\n\n| مقدار `envelope` | رفتار در OpenAPI |\n|---|---|\n| `SuccessResponse` | داخل `{ data: $ref }` قرار می‌گیرد |\n| `ErrorResponse` | مستقیم `$ref: ErrorResponse` |\n\n| مقدار `dataType` | رفتار |\n|---|---|\n| نام Struct | `$ref: '#/components/schemas/StructName'` |\n| `-` | `type: object` (بدون schema مشخص) |"
    },
    {
      "level": 4,
      "heading": "فرمت `@param`",
      "content": "```\n@param <name> <in> <type> <required> \"<description>\"\n```\n\n| فیلد | مقادیر ممکن |\n|-------|-------------|\n| `in` | `path`, `query`, `header`, `cookie` |\n| `type` | `string`, `integer`, `boolean` |\n| `required` | `true`, `false` |\n\nمثال:\n```go\n// @param publicId path string true \"User Public ID\"\n// @param page query integer false \"Page number\"\n```"
    },
    {
      "level": 3,
      "heading": "منبع ۲: Struct Tags در models.go",
      "content": "Schema های OpenAPI از Struct‌های `internal/handler/models.go` استخراج می‌شوند:\n\n```go\n// فایل: internal/handler/models.go\n\ntype UpdateUsernameRequest struct {\n    Username string `json:\"username\" validate:\"required,min=3,max=30,alphanum_underscore\"`\n}\n```"
    },
    {
      "level": 4,
      "heading": "تبدیل Struct Tags به OpenAPI Schema",
      "content": "| Struct Tag | OpenAPI Property | مثال |\n|---|---|---|\n| `json:\"field_name\"` | نام فیلد در schema | `username` |\n| `json:\"-\"` | فیلد از schema حذف می‌شود | — |\n| `validate:\"required\"` | `required: [field_name]` | الزامی |\n| `validate:\"min=3\"` | `minLength: 3` | برای string |\n| `validate:\"max=30\"` | `maxLength: 30` | برای string |\n| `validate:\"alphanum_underscore\"` | `pattern: \"^[a-zA-Z0-9_]+$\"` | pattern ثابت |\n| `validate:\"enum=A\\|B\\|C\"` | `enum: [A, B, C]` | مقادیر مجاز |\n| نوع `*string` (pointer) | `nullable: true` | nullable field |\n| نوع `time.Time` | `type: string, format: date-time` | timestamp |"
    },
    {
      "level": 4,
      "heading": "مثال کامل تبدیل",
      "content": "```go\n// Go struct\ntype UpdateUsernameRequest struct {\n    Username string `json:\"username\" validate:\"required,min=3,max=30,alphanum_underscore\"`\n}\n\ntype UpdatePreferencesRequest struct {\n    Currency string `json:\"currency\" validate:\"enum=IRR|USD|TRY\"`\n    Theme    string `json:\"theme\"    validate:\"enum=light|dark|system\"`\n    Language string `json:\"language\" validate:\"enum=fa|en|tr\"`\n}\n\ntype UpdateProfileResponse struct {\n    PublicID    string    `json:\"public_id\"`\n    DisplayName *string   `json:\"display_name\"`   // pointer = nullable\n    UpdatedAt   time.Time `json:\"updated_at\"`\n}\n```\n\n```yaml"
    },
    {
      "level": 1,
      "heading": "OpenAPI خروجی",
      "content": "schemas:\n  UpdateUsernameRequest:\n    type: object\n    required:\n      - username\n    properties:\n      username:\n        type: string\n        minLength: 3\n        maxLength: 30\n        pattern: \"^[a-zA-Z0-9_]+$\"\n\n  UpdatePreferencesRequest:\n    type: object\n    properties:\n      currency:\n        type: string\n        enum:\n          - IRR\n          - USD\n          - TRY\n      theme:\n        type: string\n        enum:\n          - light\n          - dark\n          - system\n      language:\n        type: string\n        enum:\n          - fa\n          - en\n          - tr\n\n  UpdateProfileResponse:\n    type: object\n    properties:\n      public_id:\n        type: string\n      display_name:\n        type: string\n        nullable: true       # چون *string است\n      updated_at:\n        type: string\n        format: date-time    # چون time.Time است\n```"
    },
    {
      "level": 4,
      "heading": "تبدیل نوع داده Go به OpenAPI",
      "content": "| نوع Go | نوع OpenAPI | تذکر |\n|--------|-------------|------|\n| `string`, `*string` | `string` | pointer → nullable |\n| `int`, `int32`, `int64`, `uint`, `uint64` | `integer` | — |\n| `bool` | `boolean` | — |\n| `time.Time`, `*time.Time` | `string` + `format: date-time` | — |\n| سایر | `object` | nested schema |"
    },
    {
      "level": 4,
      "heading": "Schemaهای مشترک (تزریق از کانفیگ)",
      "content": "Schemaهای مشترک مانند `ErrorResponse`، `ErrorDetails`، `ValidationErrorItem` از پارس خودکار Structها **skip** شده و در عوض از بخش `schemas` در `openapi-config.yaml` تزریق می‌شوند. این کار تضمین می‌کند تعریف این Schemaها در همه سرویس‌ها یکسان است. اگر یک Schema هم در کد و هم در کانفیگ تعریف شود، ابزار با خطای conflict متوقف می‌شود (fail-fast).\n\n---"
    },
    {
      "level": 2,
      "heading": "۱۴. ساختار فایل‌های اتوماسیون سرویس",
      "content": "با ابزار مشترک `nons-openapi`، دیگر نیازی به `cmd/openapi-gen/` و اسکریپت‌های تکراری نیست. هر سرویس تنها به یک فایل کانفیگ نیاز دارد:\n\n```\nservices/{service-name}/\n├── openapi-config.yaml       # کانفیگ سرویس (adapter, tag_rules, schemas)\n├── internal/handler/\n│   ├── handler.go            # تابع‌های Handler با Annotations\n│   └── models.go             # Struct های Request/Response\n└── docs/\n    ├── openapi.yaml          # خروجی ابزار — هرگز دستی ویرایش نشود\n    └── openapi.prev.yaml     # نسخه قبلی برای Breaking Change Detection\n```"
    },
    {
      "level": 3,
      "heading": "نمونه `openapi-config.yaml`",
      "content": "```yaml\ninfo:\n  title: User Service API\n  description: User profile and account management API\n  version: \"1.0.0\"\n  x-service-id: user-service\n\nsource:\n  type: go-ast\n\ntag_rules:\n  - path_prefix: /v1/users\n    tag: Users\n    description: User profile management\n\nschemas:\n  ErrorResponse:\n    type: object\n    required: [error]\n    properties:\n      error:\n        $ref: '#/components/schemas/ErrorDetails'\n  # ... سایر Schemaهای مشترک\n\ngo-ast:\n  handler_paths:\n    - internal/handler/handler.go\n  models_path: internal/handler/models.go\n  receiver: UserHandler          # خالی (\"\") برای توابع سطح package\n  request_media_type: application/json\n  response_media_type: application/json\n```"
    },
    {
      "level": 3,
      "heading": "Makefile Targets",
      "content": "هر سرویس باید این target ها را در Makefile داشته باشد:\n\n```makefile\nopenapi-gen:\n\tnons-openapi generate\n\nopenapi-val:\n\tnons-openapi validate\n```\n\n**استفاده:**\n```bash\nmake openapi-gen   # تولید openapi.yaml از کد\nmake openapi-val   # اعتبارسنجی openapi.yaml"
    },
    {
      "level": 1,
      "heading": "یا مستقیم:",
      "content": "nons-openapi generate           # تولید (شامل اعتبارسنجی ساختاری)\nnons-openapi validate           # اعتبارسنجی مستقل\nnons-openapi diff               # بررسی تغییرات شکننده در برابر openapi.prev.yaml\nnons-openapi generate --dry-run # چاپ خروجی به stdout بدون نوشتن فایل\n```\n\n---"
    },
    {
      "level": 2,
      "heading": "۱۵. جزئیات پایپلاین اعتبارسنجی",
      "content": "ابزار `nons-openapi` اعتبارسنجی را در دو دستور مجزا انجام می‌دهد:"
    },
    {
      "level": 3,
      "heading": "`nons-openapi validate` — اعتبارسنجی ساختاری",
      "content": "- نسخه دقیق OpenAPI `3.1.0`\n- ساختار صحیح سند (paths، operations، components)\n- قابل حل بودن ارجاعات Schema (`$ref`)\n- وجود متادیتای الزامی در هر operation\n\n> اعتبارسنجی ساختاری به‌صورت خودکار درون `nons-openapi generate` نیز اجرا می‌شود؛ در صورت خطا، فایل خروجی نوشته نمی‌شود (fail-fast، بدون خراب‌کردن `openapi.yaml` موجود)."
    },
    {
      "level": 3,
      "heading": "`nons-openapi diff` — تشخیص تغییرات شکننده",
      "content": "اگر `docs/openapi.prev.yaml` وجود داشته باشد، تغییرات نسبت به آن بررسی می‌شود:\n\n| تغییر | وضعیت |\n|-------|--------|\n| حذف اندپوینت | ❌ Breaking |\n| حذف HTTP method | ❌ Breaking |\n| حذف response status code | ❌ Breaking |\n| حذف request body | ❌ Breaking |\n| حذف property از Request schema | ❌ Breaking |\n| تغییر type یک property | ❌ Breaking |\n| تبدیل optional field به required | ❌ Breaking |\n| اضافه کردن optional field | ✅ Non-Breaking |\n| اضافه کردن endpoint جدید | ✅ Non-Breaking |\n\n> قوانین کیفی مانند فرمت `token`، الزام `x-nons`، و طرح‌های امنیتی مجاز، بخشی از استانداردهای این سند هستند و در CI بررسی می‌شوند.\n\n---"
    },
    {
      "level": 2,
      "heading": "۱۶. فلسفه Single Source of Truth",
      "content": "```\nکد Go (handler.go + models.go) + openapi-config.yaml\n         │\n         │  nons-openapi generate\n         ▼\ndocs/openapi.yaml  ← هرگز دستی ویرایش نشود\n         │\n         │  nons-openapi validate / diff\n         ▼\nRegistry پلتفرم\n```\n\n**قوانین:**\n- **تغییر مستقیم `openapi.yaml` اکیداً ممنوع است.** هرگونه ویرایش باید در Annotation ها، Struct Tag ها یا `openapi-config.yaml` اعمال شده و مجدداً `make openapi-gen` اجرا شود.\n- مستندات OpenAPI به صورت خودکار از روی کد استخراج می‌شوند تا تضمین شود مستندات با رفتار واقعی سیستم ۱۰۰٪ همگام هستند.\n\n---"
    },
    {
      "level": 2,
      "heading": "۱۷. فلسفه Token-Based Registry",
      "content": "به منظور قطع وابستگی کلاینت‌ها به آدرس‌های اینترنتی مستقیم (URLs)، پلتفرم NONS از سیستم **توزیع مبتنی بر توکن (Token-Based)** استفاده می‌کند.\n\nکلاینت درخواست خود را با یک توکن معنایی (Semantic Token) نظیر `users.getProfile` ارسال کرده و سیستم رجیستری آن را به آدرس اندپوینت واقعی متصل می‌کند.\n\n**مزایا:**\n- **تغییر منعطف آدرس‌ها:** اگر مسیر اندپوینتی از `/v1/users` به `/v2/profile` تغییر یابد، کدهای فرانت‌اند بدون تغییر باقی می‌مانند — صرفاً فایل رجیستری بروز می‌شود.\n- **کنترل متمرکز سیاست‌ها:** سیاست‌هایی مانند Timeout، Retry و Caching مستقیماً در قرارداد ثبت شده و توسط `nons generate` بر روی مصنوعات تولیدشده اعمال می‌شوند.\n\n---"
    },
    {
      "level": 2,
      "heading": "۱۸. چک‌لیست پیاده‌سازی برای سرویس جدید",
      "content": "برای هر سرویس Go جدید، مراحل زیر را دنبال کنید:\n\n- [ ] فایل `openapi-config.yaml` ایجاد شده (بر اساس الگوی user-service — Adapter، tag_rules، schemas)\n- [ ] تمام Handler ها Annotation‌های `@route`, `@summary`, `@description` دارند\n- [ ] هر Handler دارای `@security` (Public یا نام Scheme) است\n- [ ] هر Handler دارای کامل‌ترین `@x-nons.*` است (token, service, visibility, timeout, retry)\n- [ ] هر request schema با `@request` مشخص شده (و در صورت نیاز `@accepts` برای فرم‌محورها)\n- [ ] تمام response codes با `@response` تعریف شده‌اند (شامل ۴۰۰، ۴۰۱، ۵۰۰)\n- [ ] فایل `internal/handler/models.go` با Struct Tag های صحیح موجود است\n- [ ] Schemaهای مشترک (`ErrorResponse` و…) در بخش `schemas` کانفیگ تعریف شده‌اند\n- [ ] `Makefile` دارای target های `openapi-gen` و `openapi-val` است (فراخوان `nons-openapi`)\n- [ ] `nons-openapi generate` بدون خطا اجرا می‌شود\n- [ ] `nons-openapi validate` بدون خطا اجرا می‌شود\n\n---"
    },
    {
      "level": 2,
      "heading": "مستندات مرتبط",
      "content": "| سند | توضیح |\n|-----|--------|\n| [API Design Guidelines](./api-design-guidelines) | استاندارد طراحی API |\n| [ADR-Platform-004](../ADR/ADR-Platform-004) | استراتژی مدیریت Registry و تولید مصنوعات |\n| [ساختار مخزن](../standards/repository-structure) | مسیر فایل‌ها در هر سرویس |\n| [user-service](../../backend/services/user-service) | پیاده‌سازی مرجع (Reference Implementation) |\n| [`github.com/NonsCore/openapi`](https://github.com/NonsCore/openapi) | مخزن ابزار `nons-openapi` (Adapterها، CHANGELOG، Releases) |"
    }
  ]
}