{
  "title": "معماری لایه قراردادها",
  "slug": "team/platform/package/shared-packages-architecture",
  "url": "/docs/team/platform/package/shared-packages-architecture",
  "frontmatter": {
    "layout": "doc",
    "title": "معماری لایه قراردادها",
    "description": "تعریف رویکرد و مسئولیت قراردادهای مشترک پروژه — تفکیک قرارداد از پیاده‌سازی با Protocol Buffers",
    "version": "2.0.0",
    "status": "APPROVED",
    "author": "xoxxel",
    "owner": "xoxxel",
    "created_at": "2026-06-10",
    "updated_at": "2026-06-12",
    "tags": "",
    "reviewers": ""
  },
  "sections": [
    {
      "level": 1,
      "heading": "معماری لایه قراردادها",
      "content": "---"
    },
    {
      "level": 2,
      "heading": "معماری دو لایه‌ای",
      "content": "پروژه NONS از دو لایه مجزا برای مدیریت قراردادها استفاده می‌کند:\n\n```text\nلایه قراردادهای پلتفرم (Platform Contracts)\n        ↓\n    Proto Definition — منبع حقیقت\n        ↓\n    Code Generation (Buf)\n        ↓\n    Bindingهای زبان‌مخصوص (TS, Go, Python, ...)\n\n────────────────────────────────────\n\nلایه قراردادهای دامنه (Domain Contracts)\n        ↓\n    داخل هر سرویس — مالکیت آن سرویس\n```\n\n---"
    },
    {
      "level": 2,
      "heading": "Contract Layer (Proto)",
      "content": "تمامی قراردادهای مشترک پلتفرم در قالب **Protocol Buffers** تعریف می‌شوند. هیچ زبانی مالک قراردادها نیست — Proto منبع حقیقت است.\n\n**ساختار:**\n\n```text\nnons-api/contracts/\n├── envelope.proto      # Event Envelope — ساختار پیام رویدادها\n├── registry.proto      # Service Registry — ثبت سرویس‌ها\n├── errors.proto        # Platform Error Codes — کدهای خطای مشترک\n└── permissions.proto   # Permissions — مجوزهای سطح پلتفرم\n```\n\n**فرآیند:**\n\n```text\nProto (.proto)\n    ↓\nBuf (lint, break check, generate)\n    ↓\nGo bindings  →  nons-api/core/\nTS bindings  →  nons-api/packages/contracts (generated)\nPython       →  (future)\n```\n\n**مزایا:**\n\n- استقلال کامل از زبان\n- حذف Drift بین پیاده‌سازی‌ها\n- یک منبع حقیقت واحد\n- پشتیبانی از معماری چندزبانه\n\n---"
    },
    {
      "level": 2,
      "heading": "قراردادهای دامنه (در سطح سرویس)",
      "content": "قراردادهای مختص هر دامنه (مانند `Order`, `Payment`, `Wallet`) در داخل همان سرویس تعریف می‌شوند و به لایه پلتفرم منتقل نمی‌شوند.\n\nمثال:\n\n```text\nnons-api/services/order-service/contracts/\nnons-api/services/payment-service/contracts/\nnons-api/services/wallet-service/contracts/\n```\n\nمالک هر Domain Contract همان سرویس است. پلتفرم فقط مالک Platform Contracts است.\n\n---"
    },
    {
      "level": 2,
      "heading": "تفاوت قرارداد و پیاده‌سازی",
      "content": ""
    },
    {
      "level": 3,
      "heading": "پکیج مرکزی با پیاده‌سازی ❌",
      "content": "```ts\n// packages/logger — این رویکرد انتخاب نشده\nimport winston from \"winston\";\n\nexport const logger = winston.createLogger({\n  format: winston.format.json(),\n});\n```\n\n**مشکل این رویکرد:**\n\n- همه سرویس‌های TypeScript به `winston` قفل می‌شوند\n- Go core نمی‌تواند از یک پکیج TypeScript استفاده کند\n- تغییر کتابخانه در آینده به معنای تغییر در همه سرویس‌ها است\n- سرویس‌هایی که نیاز متفاوتی دارند نمی‌توانند انعطاف داشته باشند\n\n---"
    },
    {
      "level": 3,
      "heading": "قرارداد خالص ✅",
      "content": "قراردادها فقط **شکل داده** را تعریف می‌کنند — نه پیاده‌سازی.\n\n**Contract Layer (Proto):**\n\n```protobuf\n// nons-api/contracts/envelope.proto\nmessage EventEnvelope {\n  string id = 1;\n  string subject = 2;\n  string version = 3;\n  string timestamp = 4;\n  string source = 5;\n  string trace_id = 6;\n  bytes payload = 7;\n}\n```\n\n**Logging Contract (TypeScript — استثنا):**\n\n```ts\n// packages/logging — این رویکرد انتخاب شده\n// هیچ import خارجی ندارد — فقط TypeScript خالص\n\nexport interface Logger {\n  info(message: string, meta?: Record<string, unknown>): void;\n  warn(message: string, meta?: Record<string, unknown>): void;\n  error(message: string, meta?: Record<string, unknown>): void;\n  debug(message: string, meta?: Record<string, unknown>): void;\n}\n\nexport interface LogEntry {\n  level: \"debug\" | \"info\" | \"warn\" | \"error\";\n  service: string;\n  traceId: string;\n  message: string;\n  timestamp: string;\n  meta?: Record<string, unknown>;\n}\n```\n\nهر سرویس این interface را با هر کتابخانه‌ای که مناسب بداند پیاده‌سازی می‌کند — ولی **خروجی نهایی همیشه همین شکل** را دارد.\n\nLogging Contract یک قرارداد انتقال داده بین سرویس‌ها نیست — فقط استاندارد خروجی Log را تعریف می‌کند. به همین دلیل در Proto تعریف نمی‌شود.\n\n---"
    },
    {
      "level": 2,
      "heading": "اجزای فعلی",
      "content": ""
    },
    {
      "level": 3,
      "heading": "`nons-api/contracts/` — Proto Source of Truth",
      "content": "**نوع:** مرکزی — قراردادهای پلتفرم\n\n**مسئولیت:** تعریف قراردادهای مشترک پلتفرم (Event Envelope, Error Codes, Registry, Permissions).\n\n**فرمت:** Protocol Buffers\n\n**ابزار:** Buf — linting, breaking-change detection, code generation\n\n**چه کسی استفاده می‌کند:** Core (Go bindings), سرویس‌های TypeScript (TS bindings), سرویس‌های آینده\n\n---"
    },
    {
      "level": 3,
      "heading": "`nons-api/packages/logging` — `@nons/logging`",
      "content": "**نوع:** فقط قرارداد — **بدون پیاده‌سازی** — خارج از محدوده Proto\n\n**مسئولیت:** تعریف فرمت یکسان لاگ و interface که هر سرویس باید implement کند. این پکیج هیچ کتابخانه‌ای import نمی‌کند و هیچ لاگی نمی‌زند.\n\n```ts\nexport type LogLevel = \"debug\" | \"info\" | \"warn\" | \"error\";\n\nexport interface LogEntry {\n  level: LogLevel;\n  service: string;\n  traceId: string;\n  message: string;\n  timestamp: string;\n  meta?: Record<string, unknown>;\n}\n\nexport interface Logger {\n  debug(message: string, meta?: Record<string, unknown>): void;\n  info(message: string, meta?: Record<string, unknown>): void;\n  warn(message: string, meta?: Record<string, unknown>): void;\n  error(message: string, meta?: Record<string, unknown>): void;\n}\n```\n\n**Go core** همان فرمت `LogEntry` را به شکل JSON تولید می‌کند:\n\n```go\ntype LogEntry struct {\n    Level     string                 `json:\"level\"`\n    Service   string                 `json:\"service\"`\n    TraceID   string                 `json:\"traceId\"`\n    Message   string                 `json:\"message\"`\n    Timestamp string                 `json:\"timestamp\"`\n    Meta      map[string]interface{} `json:\"meta,omitempty\"`\n}\n```\n\n**نتیجه:** خروجی لاگ از `auth-service` با TypeScript/pino و از Go core با zap، **دقیقاً یک شکل** دارد. سیستم مانیتورینگ هر دو را یکسان می‌خواند.\n\n---"
    },
    {
      "level": 3,
      "heading": "`.nons/generated/` — مصنوعات تولیدشده توسط CLI",
      "content": "طبق [ADR-Platform-004](../ADR/ADR-Platform-004)، مصنوعات فرانت‌اند توسط **Platform CLI** تولید می‌شوند. کدهای Types، API Client و Hooks توسط `nons generate` در `.nons/generated/` در پروژه کلاینت تولید می‌شوند. فرانت‌اند هیچ وابستگی NPM ندارد — تنها وابستگی، ابزار CLI `nons` است.\n\n---"
    },
    {
      "level": 2,
      "heading": "خلاصه تصمیم",
      "content": "| مؤلفه                                  | نوع                      | منبع حقیقت      | وابستگی خارجی |\n| -------------------------------------- | ------------------------ | --------------- | ------------- |\n| `nons-api/contracts/` (Proto)          | مرکزی — پلتفرم           | Proto           | Buf (ابزار)   |\n| `nons-api/packages/contracts` (generated) | Binding تولیدشده      | Proto           | هیچ           |\n| `nons-api/packages/events` (generated) | Binding تولیدشده         | Proto + Catalog | هیچ           |\n| `nons-api/packages/logging`            | قرارداد — فقط TypeScript | خود فایل        | هیچ           |\n| `.nons/generated/` (فرانت‌اند)         | مصنوعات تولیدشده توسط CLI  | Service Manifest | CLI (nons) |\n\n---"
    },
    {
      "level": 2,
      "heading": "چرا این رویکرد",
      "content": "**۱. زبان‌آگنوستیک واقعی**\n\nرویکرد قبلی (TypeScript packages) ادعای زبان‌آگنوستیک بودن داشت اما در عمل TypeScript مالک قراردادها بود و Go core مجبور بود structها را دستی بازنویسی کند. با Proto، هیچ زبانی مالک نیست — همه مصرف‌کننده.\n\n**۲. حذف Drift**\n\nCode Generation تضمین می‌کند Bindingهای Go و TypeScript همیشه هماهنگ هستند. دیگر خبری از Desynchronization بین Core و سرویس‌ها نیست.\n\n**۳. تغییر آزاد است**\n\nاگر `auth-service` بخواهد از `pino` به `winston` مهاجرت کند، هیچ سرویس دیگری تأثیر نمی‌گیرد — چون پیاده‌سازی داخل خود سرویس است.\n\n**۴. خروجی یکسان است**\n\nمهم نیست هر سرویس چه کتابخانه‌ای استفاده می‌کند — چون همه باید `LogEntry` یکسان تولید کنند، سیستم مانیتورینگ و tracing بدون مشکل کار می‌کند.\n\n**۵. آماده برای آینده**\n\nProto از Rust, Python, Java, Go, TypeScript و هر زبان دیگری پشتیبانی می‌کند. افزودن سرویس با زبان جدید نیاز به Parser اختصاصی ندارد.\n\n---"
    },
    {
      "level": 2,
      "heading": "ارتباط با NATS",
      "content": "استفاده از Protobuf به معنای استفاده از gRPC نیست. پیام‌ها همچنان از طریق NATS با Payload JSON منتقل می‌شوند. Proto فقط منبع حقیقت قراردادها است.\n\n```text\nProto\n  ↓\nGenerate Types\n  ↓\nJSON Payload\n  ↓\nNATS\n```\n\n---"
    },
    {
      "level": 2,
      "heading": "ابهامات رفع‌شده",
      "content": "تمامی ابهامات معماری در [ADR-Platform-001](../ADR/ADR-Platform-001) و پیوست‌های آن تصمیم‌گیری شده‌اند. برای جزئیات بیشتر به آن سند مراجعه کنید.\n\n> **نکته:** با تصویب [ADR-Platform-004](../ADR/ADR-Platform-004)، مصنوعات فرانت‌اند توسط `nons generate` در `.nons/generated/` تولید می‌شوند. بک‌اند Bindingهای Proto را از `nons-api/packages/contracts` و `nons-api/packages/events` مصرف می‌کند که از طریق Buf در CI تولید می‌شوند."
    }
  ]
}