رفتن به محتوای اصلی

معماری

مسیر دادهٔ تقویمم این است:

Android UI → OfflineRepository → Room + outbox ─┐
├→ sync API → Prisma → PostgreSQL
Web UI → local actions → Dexie + outbox ───────┘
  • apps/api: Express، احراز هویت و push/pull؛ مالک دادهٔ پذیرفته‌شدهٔ سرور.
  • apps/calendar-web: React/Vite؛ مالک تغییرات محلیِ ارسال‌نشده در Dexie.
  • apps/mobile: کلاینت اندروید Kotlin (هدف Wasm حذف شد؛ وبِ محصول React است)؛ ذخیرهٔ محلی Room و همان قرارداد wire.
  • packages/contracts: شکل wire با Zod؛ OpenAPI و SDK Kotlin از آن تولید می‌شوند.
  • packages/design-tokens: توکن‌های مشترک رابط؛ docs: سایت Docusaurus.

سه مفهوم این تصویر که در همهٔ تصمیم‌ها تکرار می‌شوند:

  • رابط کاربری همیشه از ذخیرهٔ محلی می‌خواند؛ «ویرایش محلی موفق شد» به معنی پذیرش سرور نیست.
  • outbox صف نیت‌های ارسال‌نشده است (توضیح کامل در همگام‌سازی).
  • canonical آخرین وضعیت شناخته‌شدهٔ سرور است و جدا از پیش‌نویس کاربر نگه داشته می‌شود؛ این دو می‌توانند متفاوت باشند.

کجا را تغییر بدهم؟​

مسیرمسئولیت
apps/mobile/featureصفحه‌ها و جریان‌های اندروید؛ برای auth، ویرایش و sync به core/data، برای Room به core/database بروید
apps/calendar-web/src/featuresرفتار محصول در وب؛ src/lib/sync و src/lib/db مخصوص sync و ذخیره‌سازی‌اند
apps/api/src/routes و src/servicesاحراز هویت، اعتبارسنجی و اعمال تغییرات؛ prisma/ برای schema و migrationها
packages/contractsقرارداد Zod؛ منبع تولید OpenAPI و SDK Kotlin
packages/design-tokensمنبع مشترک توکن‌های وب، اندروید و docs
docsهمین سایت؛ نصب و build مستقل از workspace اصلی

سه قاعدهٔ اصلی​

  1. همهٔ تغییرات از sync می‌گذرد. چهار موجودیت CRUD جداگانه ندارند و فقط از /api/sync/* خوانده/نوشته می‌شوند. JWT مالک داده را تعیین می‌کند؛ uid هویت رکورد است — نه شناسهٔ داخلی DB و نه شناسهٔ provider.

  2. سرور با baseVersion تصمیم می‌گیرد. پذیرش تغییر، تعارض نسخه و شکست شبکه سه چیز متفاوت‌اند؛ هر mutationId رسید مستقل خودش را دارد. تفکیک کامل این‌ها در قرارداد sync.

  3. ذخیره‌سازی اتمیک و جداسازی حساب اجباری است. تغییر محلی همراه نیت آن، و صفحهٔ pull همراه cursor، باید هر کدام در یک تراکنش ذخیره شوند؛ پاسخ دیررس حساب قبلی نباید وارد حساب جدید شود. هر دو موتور اندروید و وب این قواعد را پیاده کرده‌اند.

برای تغییر قرارداد wire: اول packages/contracts را عوض کنید، بعد خروجی‌ها را بازتولید کنید؛ فایل تولیدشده را دست‌نویس نکنید. برای تغییر DB: migration لازم است؛ db:push جایگزین triggerها و قیود sync نمی‌شود. ادامهٔ خواندن: روابط داده و فرمان‌های پروژه.