Motoshub · Software Architecture

معماری نرم‌افزار — کامپوننت‌ها و ارتباط بین آن‌ها

وضعیت زنده و راستی‌آزمایی‌شده‌ی همین نقشه (۱۴۰۵/۰۵/۱۱) · شش ریپو، یک قرارداد. فرانت فقط با قراردادِ /api/v1 حرف می‌زند؛ Gateway تعیین می‌کند پاسخ از PHP بیاید یا Django (مهاجرت strangler-fig، بدون big-bang).

نقشه‌ی کامپوننت‌ها

motoshub-web-clientNext.js 16 / React 19 · فرانت محصول
rewrites: /api/v1/* → API
motoshub-prototypeReact/Vite · demo.shub.ir
مرجع UX و معیار پذیرش (کد محصول نیست)
motoshub-docsdocs.shub.ir · تسک‌ها/معماری/فرآیند
▼ HTTPS · JWT HS256 (راز مشترک OW_PASSWORD_PEPPER) ▼
motoshub-gatewayKong (DB-less) · مسیربندی /api/v1/*
سوییچ هر مسیر: PHP ⇄ Django
◀ مسیرهای legacy
مسیرهای مهاجرت‌کرده ▶
motoshub-web (PHP)Oxwall + Bridge · api/v1 با ۲۹۳ عملیات
motonext.shub.ir · پاسخ‌گوی فعلی production
motoshub-new-api (Django)Django 5 + DRF + SimpleJWT
بازسازی ماژول‌به‌ماژول همان قرارداد
▼ ▼
MariaDB (دیتابیس Oxwall)منبع واحد داده در دوره‌ی گذار — Django با مدل‌های unmanaged می‌خواند و به‌تدریج مالک جداول جدید می‌شود
Storage / Mediaفایل و رسانه (S3-سازگار در هدف)
Redis (هدف)کش، صف اعلان، blacklist توکن، realtime

■ توپر = موجود و فعال · ▨ خط‌چین = موجود ولی خارج از مسیر داده‌ی محصول · سبز = در حال ساخت · کهربایی = اسکلت، نیازمند تکمیل

قراردادهای الزام‌آور بین کامپوننت‌ها

قراردادشرحضامن
پاکت پاسخفهرست‌ها همیشه {data, links, meta}؛ خطاها ۴۰۱/۴۰۳/۴۰۴/۴۲۲ با بدنه‌ی یکسانتست قرارداد در CI هر دو بک‌اند
Auth واحدJWT HS256، claims {iat,exp,sub}، راز مشترک — توکن PHP در Django معتبر و برعکستست سازگاری متقابل (B2.4)
سوییچ مسیرهر ماژول پس از تایید برابری قرارداد، در Gateway به Django می‌رود؛ برگشت = برگرداندن همان مسیرDevOps + چک‌لیست B19.3
مرجع UXرفتار هر صفحه‌ی web-client = همان صفحه در demo.shub.irپذیرش PO روی staging
!

شکاف‌های فعلی معماری (وضعیت واقعی کد، نه اسناد)

Gateway وارد مدار شد — ولی فقط ۴ مسیر دارد. api.shub.ir بالاست و Kong پاسخ می‌دهد (هدرهای x-kong-*، TLS برقرار). مسیرهای فعال: auth/blogs/news→PHP و projects→Django؛ هر مسیر دیگر ۴۰۴ می‌گیرد چون catch-all تعریف نشده. کانفیگ همچنان در ایمیج پخته می‌شود (تغییر مسیر = rebuild). قدم بعدی: catch-all→PHP + مسیرهای سازمانی→Django + اتصال فرانت به Gateway به‌جای دو دامنه‌ی جدا.
Django دیگر اسکلت نیست — و با PHP هم‌پوشانی ندارد. api2.shub.ir با ۳۴۱ عملیات روی ۱۷۳ مسیر بالاست: contracts (۷۵)، funds (۱۰۲)، research (۶۳)، knowledge (۵۰)، projects (۳۴). این‌ها دقیقاً ماژول‌هایی‌اند که PHP ندارد. یعنی فرضِ «Django همان قرارداد PHP را بازمی‌سازد» درست نیست؛ دو نیمه‌ی مکملِ یک محصول‌اند و Gateway باید همین تقسیم را منعکس کند. تنها منبعِ مشترک: conversations.
Realtime هنوز طراحی اجرایی ندارد. پیام‌رسان محصول به WebSocket/SSE + Redis نیاز دارد (تسک B14.4)؛ جای آن در این نقشه رزرو شده است.
سطحِ مستقرِ PHP با آنچه در کد هست فاصله دارد. برنچ API شاملِ ۲۹۳ عملیات روی ۵۵ منبع (۶۲ کنترلر) است، اما api1.shub.ir فقط ۱۲۹ عملیات از ۱۷ منبع را سرو می‌کند: auth · users · blogs · news · groups · events · photos · albums · videos · forum · files. ماژول‌هایی مثل feed، conversations، notifications، search، friends، polls و tickets در کد آماده‌اند ولی ۴۰۴ می‌دهند. کوتاه‌ترین مسیرِ رفعِ انسداد = ادغام و استقرارِ همین برنچ. (اندازه‌گیریِ ۱۴۰۵/۰۵/۱۱ — جدولِ کامل)
«بدنه‌ی یکسانِ خطا» هنوز برقرار نیست. سرویسِ زنده دو شکلِ متفاوت برمی‌گرداند: ۴۰۱ → {"error":"…"} و ۴۰۴ → {"message":"…","request_id":"…"}. تا یکسان‌سازی (B1)، هندلرِ خطای فرانت باید هر دو را بپذیرد.
دو Swagger داریم، نه یک قرارداد. api1.shub.ir/api/v1/docs/index و api2.shub.ir/api/schema/ هر دو زنده‌اند — اما دو سندِ مستقل‌اند با پاکت پاسخ و سبکِ خطای متفاوت. تا انتشارِ یک Swaggerِ واحد پشت Gateway، «قراردادِ واحد» فقط روی کاغذ است.
یک محیط هنوز DNS ندارد. Django حالا روی api2.shub.ir بالاست، ولی motonextfront.shub.ir (staging فرانت) همچنان resolve نمی‌شود؛ گیتِ ۷ و ۸ پایپ‌لاین برای تیم Front مقصدی ندارد.
هلدینگ/شرکت در هیچ بک‌اندی وجود ندارد. جستجو در هر دو Swagger برای holding، company، tenant و scope صفر نتیجه دارد. سلسله‌مراتبِ سیستم ← هلدینگ ← شرکت و «دامنه‌ی انتشار» فقط در پروتوتایپ هست. چون این لایه روی هر موجودیت اثر می‌گذارد، افزودنش الان سه فیلد و یک فیلترِ مشترک است و بعد از پرشدنِ دیتابیس، مهاجرتِ داده روی همه‌ی جدول‌ها.
احرازِ هویت ناقص است. کلِ api/v1 فقط auth/login، auth/me و auth/permissions دارد؛ refresh، logout، register و change-password وجود ندارند. چون F3 به هر چهار وابسته است، B4 روی مسیرِ بحرانیِ کلِ برنامه قرار می‌گیرد.
امنیتی — بسته شد. توکن‌های oauth2ی که در URLِ ریموتِ چند مخزن ذخیره بودند چرخانده شده‌اند و دیگر معتبر نیستند. باقی‌مانده: ریموتِ محلیِ توسعه‌دهنده‌ها هنوز توکنِ مرده دارد و git fetch با آن شکست می‌خورد — یک‌بار git remote set-url لازم است.