پرش به محتویات

معماری اپلیکیشن Mobile

اپلیکیشن از معماری Feature-first با جریان داده یک‌طرفه و جداسازی Presentation و Data استفاده می‌کند. Domain Layer فقط برای منطق کسب‌وکاری واقعی، Invariant یا مدل مستقل از Transport ساخته می‌شود؛ Feature ساده با Use Case و Interface نمایشی پیچیده نمی‌شود.

جریان داده

User Event
  -> ViewModel / Notifier
  -> Repository Contract
  -> Repository Implementation
  -> Remote Service / Local Service
  -> Immutable State
  -> View

View فقط State را نمایش و Event را ارسال می‌کند. ViewModel رفتار Presentation و Coordination را مدیریت می‌کند. Repository منبع حقیقت Feature است و انتخاب Remote، Cache یا ترکیب آن‌ها را در یک مرز Testable نگه می‌دارد.

ساختار هدف

lib/
├── l10n/
└── src/
    ├── app/          # Bootstrap، Router و Theme
    ├── core/         # Primitive پایدار و بدون دانش Feature
    └── features/
        └── <feature>/
            ├── domain/         # در صورت منطق واقعی
            ├── data/           # Repository و Service
            └── presentation/   # View، State و ViewModel

نام Feature از مستند محصول و API می‌آید. Folder خالی، utils عمومی، Service Locator موازی یا abstraction بدون Consumer واقعی ساخته نمی‌شود.

Dependency Rule

  • Presentation به Contract Repository وابسته است و مستقیم API یا Storage را صدا نمی‌زند.
  • Data می‌تواند Domain Model را Map کند ولی Domain از Flutter، Dio، Database و Plugin مستقل است.
  • Feature دیگر به Internal این Feature Deep Import نمی‌کند.
  • core از Feature مستقل است و Business Rule را برای reuse در خود نمی‌کشد.

State و DI

Riverpod مالک State و Dependency Injection است. Providerها کوچک و Feature-scoped هستند، Rebuild با Selector محدود می‌شود و Global Mutable State خارج از Provider Graph مجاز نیست. State باید Immutable و حالت Loading، Data، Empty، Error و Offline روشن داشته باشد.

امنیت

  • Refresh Token فقط از طریق Keychain یا Android Keystore و flutter_secure_storage نگهداری می‌شود.
  • Access Token فقط در Memory است و Token، OTP، Authorization Header یا PII Log نمی‌شوند.
  • dart-define، Source و Asset محل Secret نیستند؛ Mobile یک Public Client است.
  • Validation Client برای UX است و تصمیم Authorization فقط در Backend انجام می‌شود.
  • Deep Link و Notification Input غیرقابل اعتماد و نیازمند Parse و Permission Check هستند.
  • Certificate Pinning، Analytics، Crash SDK و WebView بدون Threat Model و ADR اضافه نمی‌شوند.

شبکه ضعیف و Offline

  • Queryهای لازم می‌توانند آخرین پاسخ موفق را با TTL و نسخه Schema مشخص Cache کنند.
  • Command Offline فقط با تایید Product، Idempotency و Conflict Policy Queue می‌شود.
  • Retry خودکار فقط برای عملیات Safe یا Idempotent، محدود و با Backoff و Jitter است.
  • Push فقط Signal همگام‌سازی است و منبع حقیقت داده نیست.
  • با تغییر Building، Cache، Request و State Tenant قبلی Cancel یا Invalidate می‌شوند.

آزمون

Service و Repository با Fake محدود، ViewModel با Unit Test و View با Widget Test از دید کاربر پوشش داده می‌شوند. Flowهای Authentication، Tenant Switch، Offline Queue و Deep Link به Integration Test نیاز دارند.