← Назад к вопросам

Как будут выглядеть запросы в твоем проекте

2.0 Middle🔥 241 комментариев
#Работа с данными#Сетевое взаимодействие

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Архитектура сетевых запросов в проекте

В моем проекте запросы реализуются через многоуровневую архитектуру с четким разделением ответственности. Я использую ретроспективный подход (Retrofit) в сочетании с Kotlin Coroutines для асинхронных операций.

1. Уровень определения API (Declarative Interface)

interface UserApiService {
    @GET("users/{id}")
    suspend fun getUserById(@Path("id") userId: Long): Response<UserDto>
    
    @POST("users")
    suspend fun createUser(@Body user: UserDto): Response<CreateUserResponse>
    
    @Multipart
    @POST("upload")
    suspend fun uploadFile(@Part file: MultipartBody.Part): Response<UploadResponse>
    
    @GET("users")
    suspend fun getUsers(
        @Query("page") page: Int,
        @Query("limit") limit: Int = 20
    ): Response<PagedResponse<UserDto>>
}

2. Слой репозитория (Repository Pattern)

class UserRepository @Inject constructor(
    private val apiService: UserApiService,
    private val networkHandler: NetworkHandler
) {
    suspend fun fetchUser(userId: Long): Result<User> = withContext(Dispatchers.IO) {
        if (!networkHandler.isConnected()) {
            return@withContext Result.failure(NetworkException.NoInternet)
        }
        
        return@withContext try {
            val response = apiService.getUserById(userId)
            
            when {
                response.isSuccessful -> {
                    val userDto = response.body()
                    Result.success(userDto.toDomainModel())
                }
                response.code() == 404 -> {
                    Result.failure(NetworkException.NotFound("User not found"))
                }
                else -> {
                    Result.failure(NetworkException.ServerError(response.message()))
                }
            }
        } catch (e: Exception) {
            Result.failure(NetworkException.Unknown(e.message ?: "Unknown error"))
        }
    }
}

3. Конфигурация Retrofit с зависимостями

@Module
@InstallIn(SingletonComponent::class)
object NetworkModule {
    
    @Provides
    @Singleton
    fun provideHttpClient(): OkHttpClient {
        return OkHttpClient.Builder()
            .connectTimeout(30, TimeUnit.SECONDS)
            .readTimeout(30, TimeUnit.SECONDS)
            .addInterceptor(LoggingInterceptor())
            .addInterceptor(AuthInterceptor())
            .addInterceptor(ConnectivityInterceptor())
            .build()
    }
    
    @Provides
    @Singleton
    fun provideRetrofit(okHttpClient: OkHttpClient): Retrofit {
        return Retrofit.Builder()
            .baseUrl(BuildConfig.BASE_URL)
            .client(okHttpClient)
            .addConverterFactory(MoshiConverterFactory.create())
            .addCallAdapterFactory(CoroutineCallAdapterFactory())
            .build()
    }
    
    @Provides
    @Singleton
    fun provideUserApiService(retrofit: Retrofit): UserApiService {
        return retrofit.create(UserApiService::class.java)
    }
}

4. Кастомные интерцепторы

class AuthInterceptor : Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        val request = chain.request().newBuilder()
            .addHeader("Authorization", "Bearer ${SessionManager.token}")
            .addHeader("Content-Type", "application/json")
            .addHeader("App-Version", BuildConfig.VERSION_NAME)
            .build()
        
        return chain.proceed(request)
    }
}

class LoggingInterceptor : Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        val request = chain.request()
        Timber.d("Request: ${request.method()} ${request.url()}")
        
        val response = chain.proceed(request)
        
        Timber.d("Response: ${response.code()} ${response.message()}")
        return response
    }
}

5. Обработка ошибок и состояний

sealed class NetworkResult<out T> {
    data class Success<T>(val data: T) : NetworkResult<T>()
    data class Error(val exception: NetworkException) : NetworkResult<Nothing>()
    object Loading : NetworkResult<Nothing>()
}

sealed class NetworkException : Exception() {
    object NoInternet : NetworkException()
    data class ServerError(val message: String) : NetworkException()
    data class NotFound(val details: String) : NetworkException()
    data class Unknown(val details: String) : NetworkException()
}

6. Использование в ViewModel

class UserViewModel @ViewModelInject constructor(
    private val userRepository: UserRepository
) : ViewModel() {
    
    private val _userState = MutableStateFlow<NetworkResult<User>>(NetworkResult.Loading)
    val userState: StateFlow<NetworkResult<User>> = _userState.asStateFlow()
    
    fun loadUser(userId: Long) {
        viewModelScope.launch {
            _userState.value = NetworkResult.Loading
            _userState.value = when (val result = userRepository.fetchUser(userId)) {
                is Result.Success -> NetworkResult.Success(result.data)
                is Result.Failure -> NetworkResult.Error(result.exception)
            }
        }
    }
}

Ключевые принципы:

  • Соблюдение SOLID - каждый слой отвечает за свою задачу
  • Реактивное программирование - StateFlow/LiveData для UI обновлений
  • Dependency Injection - чистая архитектура с Hilt/Dagger
  • Безопасность - обработка SSL, хранение токенов в SecurePreferences
  • Кэширование - стратегии кэширования через OkHttp Cache
  • Логирование - детальное логирование запросов и ответов
  • Тестирование - MockWebServer для unit-тестов

Такая архитектура обеспечивает масштабируемость, легкую поддержку и надежность сетевого слоя приложения.

Как будут выглядеть запросы в твоем проекте | PrepBro