معماری نرمافزار چیست؟
معماری نرمافزار ساختار کلی و سازماندهی کد است که تعیین میکند چگونه اجزای مختلف اپلیکیشن با هم ارتباط برقرار کنند. انتخاب معماری مناسب برای موفقیت پروژه بسیار مهم است.
چرا معماری مهم است؟
- قابلیت نگهداری: کد آسانتر قابل تغییر و بهبود است
- قابلیت تست: هر بخش به طور مستقل قابل تست است
- قابلیت توسعه: اضافه کردن ویژگیهای جدید آسانتر است
- قابلیت درک: کد برای سایر توسعهدهندگان قابل فهمتر است
- عملکرد: معماری خوب عملکرد بهتری دارد
MVVM (Model-View-ViewModel)
معماری پیشنهادی گوگل
MVVM یکی از محبوبترین الگوهای معماری در اندروید است که توسط گوگل توصیه میشود. این معماری جداسازی منطق کسبوکار از رابط کاربری را فراهم میکند.
Model
دادهها و منطق کسبوکار
- Entity Classes
- Repository
- Data Sources
View
رابط کاربری
- Activity/Fragment
- Composable Functions
- XML Layouts
ViewModel
لایه میانی
- State Management
- Business Logic
- Data Processing
- > {
return flow {
// ابتدا از کش بخوان
emit(userDao.getAllUsers())
// سپس از سرور دریافت کن
val users = apiService.getUsers()
userDao.insertUsers(users)
emit(users)
}
}
}
// ViewModel
@HiltViewModel
class UserViewModel @Inject constructor(
private val userRepository: UserRepository
) : ViewModel() {
private val _users = MutableStateFlow
- >(emptyList())
val users: StateFlow
- > = _users.asStateFlow()
private val _isLoading = MutableStateFlow(false)
val isLoading: StateFlow
Clean Architecture
معماری تمیز و مقیاسپذیر
Clean Architecture یک الگوی معماری است که توسط Robert C. Martin معرفی شده و هدف آن ایجاد نرمافزاری قابل تست، قابل نگهداری و مستقل از فریمورک است.
لایههای Clean Architecture
UI, ViewModels, Activities
Use Cases, Entities, Repository Interfaces
Repository Implementation, Data Sources, API
- > {
return userRepository.getUsers()
}
}
// Domain Layer - Repository Interface
interface UserRepository {
suspend fun getUsers(): Flow
- >
suspend fun getUserById(id: String): User?
suspend fun saveUser(user: User)
}
// Data Layer - Repository Implementation
class UserRepositoryImpl @Inject constructor(
private val apiService: ApiService,
private val userDao: UserDao
) : UserRepository {
override suspend fun getUsers(): Flow
- > {
return flow {
// ابتدا از کش بخوان
emit(userDao.getAllUsers())
// سپس از سرور دریافت کن
try {
val users = apiService.getUsers()
userDao.insertUsers(users)
emit(users)
} catch (e: Exception) {
// در صورت خطا، دادههای کش را برگردان
emit(userDao.getAllUsers())
}
}
}
override suspend fun getUserById(id: String): User? {
return userDao.getUserById(id)
}
override suspend fun saveUser(user: User) {
userDao.insertUser(user)
}
}
// Presentation Layer - ViewModel
@HiltViewModel
class UserViewModel @Inject constructor(
private val getUsersUseCase: GetUsersUseCase
) : ViewModel() {
private val _users = MutableStateFlow
- >(emptyList())
val users: StateFlow
- > = _users.asStateFlow()
fun loadUsers() {
viewModelScope.launch {
getUsersUseCase()
.collect { users ->
_users.value = users
}
}
}
}
مزایای Clean Architecture
- استقلال از فریمورک: کد مستقل از Android است
- قابلیت تست: هر لایه به طور مستقل قابل تست است
- قابلیت نگهداری: تغییرات در یک لایه بر لایههای دیگر تأثیر نمیگذارد
- قابلیت توسعه: اضافه کردن ویژگیهای جدید آسان است
- قابلیت درک: ساختار کد واضح و قابل فهم است
مقایسه MVP و MVVM
MVP (Model-View-Presenter)
MVP الگوی قدیمیتری است که هنوز در برخی پروژهها استفاده میشود، اما MVVM جایگزین بهتری است.
ویژگی | MVP | MVVM |
---|---|---|
ارتباط View و Presenter/ViewModel | مستقیم (Interface) | غیرمستقیم (Data Binding) |
قابلیت تست | خوب | عالی |
کد اضافی | زیاد (Interface ها) | کم |
سازگاری با Compose | مشکل | عالی |
توصیه گوگل | خیر | بله |
Dependency Injection
مدیریت وابستگیها
Dependency Injection الگویی است که به شما کمک میکند وابستگیهای کلاسها را مدیریت کنید و کد قابل تستتری بنویسید.
بهترین شیوهها
نکات مهم برای معماری خوب
- جداسازی مسئولیتها: هر کلاس یک وظیفه مشخص داشته باشد
- وابستگیهای معکوس: کلاسهای سطح بالا به کلاسهای سطح پایین وابسته نباشند
- قابلیت تست: هر بخش به طور مستقل قابل تست باشد
- کد تمیز: از نامگذاری مناسب و کد خوانا استفاده کنید
- مستندات: معماری پروژه را مستند کنید
اشتباهات رایج
- نوشتن همه چیز در Activity یا Fragment
- عدم استفاده از Repository Pattern
- نادیده گرفتن قابلیت تست
- وابستگی مستقیم به فریمورکها
- عدم استفاده از Dependency Injection
راهنمای مهاجرت
چگونه به معماری مدرن مهاجرت کنیم؟
- شروع تدریجی: ابتدا یک بخش کوچک را تغییر دهید
- Repository Pattern: دسترسی به دادهها را از طریق Repository انجام دهید
- ViewModel: منطق کسبوکار را به ViewModel منتقل کنید
- Dependency Injection: از Hilt برای مدیریت وابستگیها استفاده کنید
- Testing: برای هر بخش تست بنویسید
- Clean Architecture: در پروژههای جدید از ابتدا استفاده کنید
مراحل بعدی
حالا که معماریهای پیشرفته را یاد گرفتید، آماده یادگیری موضوعات پیشرفته هستید:
موضوعات پیشرفته شروع دورهها