ماموریت ۰۱ Android · معماری

اپ دنیای
دیجیمون

یک اپ دو صفحه‌ای بسازید، لیست دیجیمون‌ها را از اینترنت بگیرید و با کلیک روی هر شخصیت، وارد صفحه جزئیاتش شوید.

۲ صفحه۱۰ ابزار۱ Repository
سه موجود دیجیتالی کارتونی و کاملاً خیالی که روی جزیره‌ای مداری، ساختار یک اپ را بررسی می‌کنندMVIStateFlow

حس نوستالژی، تمرین واقعی! تمرکز اصلی روی معماری و جریان داده است؛ ظاهر اپ لازم نیست پیچیده باشد.

Level یعنی چی؟

Level مرحله رشد و قدرت هر Digimon را نشان می‌دهد.

1Fresh

مرحله ابتدایی

2In Training

در حال رشد

3Rookie

مرحله پایه و شناخته‌شده

4Champion

قوی‌تر از Rookie

5Ultimate

مرحله پیشرفته

6Mega

یکی از قوی‌ترین مراحل

مثلاً Agumon معمولاً یک Digimon در سطح Rookie محسوب می‌شود و می‌تواند به مراحل قوی‌تری تکامل پیدا کند.

هدف پروژه

در پایان، یک اپ کوچک اما معماری‌شده خواهید داشت.

جعبه ابزار ماموریت

همه چیزهایی که باید کنار هم قرار بگیرند

این تمرین فقط نمایش یک لیست نیست؛ هدفش ساختن یک مسیر داده واضح از شبکه تا UI و تمرین مسئولیت‌های جداست.

Jetpack ComposeMVIViewModelStateFlowSharedFlowRepositoryRetrofitNavigation ComposeType-safe routesCoil برای نمایش تصاویر اینترنتی

API مورد استفاده

داده‌ها از Digimon API دریافت می‌شوند.

GET
دریافت لیست تمام Digimonها/api/digimon
GET
دریافت Digimon با نام مشخص/api/digimon/name/Agumon
https://digimon-api.vercel.app
ساختار تقریبی هر آیتم
{
  "name": "Agumon",
  "img": "https://...",
  "level": "Rookie"
}

معماری مورد انتظار

دو مسیر مستقل UI، یک منبع داده مشترک.

مسیر لیستGET all Digimon
ListScreenنمایش UI
ListViewModelمدیریت state
DigimonRepositoryمنبع مشترک
GET all Digimonدرخواست شبکه
مسیر جزئیاتGET Digimon by name
DetailsScreenنمایش UI
DetailsViewModelمدیریت state
DigimonRepositoryمنبع مشترک
GET by nameدرخواست شبکه
مرز مسئولیت را حفظ کنید

هر دو ViewModel از یک Repository استفاده می‌کنند، اما هرکدام مسئول state صفحه خودشان هستند.

مدل، API و Repository

سه لایه را جدا، کوچک و قابل فهم نگه دارید.

بخش اولمدل Digimon

یک مدل برای اطلاعات Digimon بسازید؛ حداقل نام، آدرس تصویر و level را نگه دارد.

Digimon.kt
data class Digimon(
    val name: String,
    val img: String,
    val level: String,
)
بخش دومAPI با Retrofit

یک interface بسازید که دریافت همه Digimonها و دریافت براساس نام را پوشش دهد. خروجی endpoint نام هم به شکل لیست برمی‌گردد.

DigimonApi.kt
interface DigimonApi {
    @GET("api/digimon")
    suspend fun getAllDigimons(): List<Digimon>

    @GET("api/digimon/name/{name}")
    suspend fun getDigimonByName(
        @Path("name") name: String,
    ): List<Digimon>
}
بخش سومDigimonRepository

Repository تمام Digimonها یا یک Digimon براساس نام را می‌گیرد و جزئیات Retrofit را از ViewModel پنهان می‌کند.

getAllDigimons()getDigimonByName(name)
ViewModel نباید مستقیماً DigimonApi را صدا بزند.

صفحه اول: Digimon List

لیست قابل پیمایش با سه state مشخص.

هر آیتم حداقل باید تصویر Digimon، نام و Level را نشان دهد. با کلیک روی هر آیتم، کاربر وارد صفحه جزئیات همان Digimon می‌شود.

LoadingSuccessError
09:41● ● ●
Digimon World
A
AgumonIn Training
G
GabumonRookie
P
PatamonIn Training

صفحه دوم: Digimon Details

نام را بگیرید؛ اطلاعات کامل را دوباره واکشی کنید.

نام Digimon از Navigation دریافت می‌شود. سپس DetailsViewModel با استفاده از Repository اطلاعات کامل همان Digimon را از API می‌گیرد.

  • تصویر بزرگ‌تر
  • نام
  • Level
  • دکمه Back
A
LEVELRookie

ساختار فایل پیشنهادی

نام‌ها می‌توانند کمی متفاوت باشند؛ مسئولیت‌ها باید جدا و واضح بمانند.

📦digimon/
Digimon.ktModel
DigimonApi.ktNetwork
RetrofitProvider.ktNetwork
DigimonRepository.ktData
DigimonListContract.ktList
DigimonListViewModel.ktList
DigimonListScreen.ktList
DigimonDetailsContract.ktDetails
DigimonDetailsViewModel.ktDetails
DigimonDetailsScreen.ktDetails
📁navigation/
AppNavigation.ktNavigation
Routes.ktNavigation

خروجی نهایی مورد انتظار

هر مورد را که پیاده‌سازی کردید علامت بزنید؛ وضعیت روی همین دستگاه می‌ماند.

0 از 8 انجام شده
ایستگاه مرور

قبل از شروع کدنویسی، یک سؤال!

برای دریافت اطلاعات صفحه جزئیات، ViewModel باید مستقیماً کدام لایه را صدا بزند؟