Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Modern Android Architecture

Sponsored · SiteGround - Reliable hosting with speed, security, and support you can count on. →
Yves Kalume
February 16, 2023

Modern Android Architecture

This talk will cover how to think about Architecture, some recommended principles.

Yves Kalume

February 16, 2023

Transcript

41 slides
  1. 🍝 Un code spaghetti Quand on a une mauvaise architecture

    🥵 Non évolutif 💸 Dettes techniques ...
  2. 󰞆 Améliorer la maintenabilité, la qualité et la robustesse Avantages

    d’une bonne architecture 🧱 Scalabilité 🧪 Testabilité 󰞅 L'intégration de nouveau membre dans l'équipe
  3. Séparation des responsabilités (Separation of concerns) • UI gère les

    interactions entre l'interface utilisateur et le système d'exploitation. • UI est piloté par les données, UI Couche de données
  4. Une seule source de vérité (Single Source of Truth) Couche

    Données Données Immuables • Privilégier l’immutabilité • Centraliser toutes les modifications en un seul endroit. Évenements
  5. Couche UI Repositories Data Sources Couche donnée Data Source (Sources

    des données) • Fournit des données • Travaille avec une seule source des données
  6. Couche UI Repositories Data Sources Couche donnée Repositories • Expose

    les données aux couches supérieures • Manage les data sources • Choisi une source de vérité (single source of truth) • Une Repository pour une seule entité
  7. class TasksLocalDataSource ( val tasksDao: TasksDao, ) { fun getTasksStream():

    Flow<List<Task.> {...} suspend fun saveTasks(tasks: List<Task>) {...} } TasksLocalDataSource.kt
  8. class TasksRemoteDataSource ( val apiClient: ApiClient, ) { suspend fun

    getTasks(): List<Task.> {...} } TasksRemoteDataSource.kt
  9. class TasksRepository( private val tasksRemoteDataSource: TasksDataSource, private val tasksLocalDataSource: TasksDataSource,

    ) { fun getTaskStream(): Flow<List<Task.> = tasksLocalDataSource.getTaskStream() private suspend fun updateTaskFromRemoteDataSource() { val remoteTasks = tasksRemoteDataSource.getTasks() tasksLocalDataSource.saveTask(remoteTasks) } } TasksRepository.kt
  10. data class TasksUiState( val items: List<Task> = emptyList(), val isLoading:

    Boolean = false, val userMessage: String? = null ) TasksUiState.kt
  11. sealed interface TasksUiState { object Loading : TasksUiState object Empty

    : TasksUiState data class Success(val items: List<Task>) : TasksUiState data class Error(val errorMessage: String) : TasksUiState } Ou…
  12. @HiltViewModel class TasksViewModel @Inject constructor( private val tasksRepository: TasksRepository, )

    : ViewModel() { val uiState: StateFlow<TasksUiState> } TasksViewModel.kt
  13. @HiltViewModel class TasksViewModel @Inject constructor( private val tasksRepository: TasksRepository, )

    : ViewModel() { val uiState: StateFlow<TasksUiState> = tasksRepository.getTasksStream().map { TasksUiState.Success(...) }.stateIn( scope = viewModelScope, started = SharingStarted.WhileSubscribed(5_000), initialValue = TasksUiState.Loading ) } TasksViewModel.kt
  14. @Composable fun TasksScreen( modifier: Modifier = Modifier, viewModel: TasksViewModel =

    hiltViewModel(), ) { val uiState by viewModel.uiState.collectAsStateWithLifecycle() ./ Mettre à jour l’écran when(uiState) { TasksUiState.Empty .> .*Afficher un ecran vide./ TasksUiState.Error .> .*Afficher un message d'erreur./ TasksUiState.Loading .> .*Afficher un Spinner./ TasksUiState.Success .> .*Afficher les données./ } } TasksScreen.kt
  15. Couche domaine (Optionnelle) Couche UI Couche Domaine Couche Données •

    l'encapsulation des codes métiers complexes ou réutilisables dans plusieurs viewmodels • Vous ne devez l'utiliser qu'en cas de besoin • Représente généralement une seule action de l’utilisateur dans L’application
  16. @HiltViewModel class TasksViewModel @Inject constructor( private val getFilteredTasksUseCase: GetFilteredTasksUseCase )

    : ViewModel() { val uiState: StateFlow<TasksUiState> = getFilteredTasksUseCase.execute().map { TasksUiState.Success(...) }.stateIn( scope = viewModelScope, started = SharingStarted.WhileSubscribed(5_000), initialValue = TasksUiState.Loading ) } TasksViewModel.kt
  17. @HiltViewModel class TasksViewModel @Inject constructor( private val getFilteredTasksUseCase: GetFilteredTasksUseCase )

    : ViewModel() { val uiState: StateFlow<TasksUiState> = getFilteredTasksUseCase().map { TasksUiState.Success(...) }.stateIn( scope = viewModelScope, started = SharingStarted.WhileSubscribed(5_000), initialValue = TasksUiState.Loading ) } TasksViewModel.kt
  18. Quelques bonnes pratiques • Exposez le moins possible de chaque

    partie. • Chaque partie doit être testable de manière isolée. • Ne réinventez pas la roue • Chaque composant est responsable de sa politique de concurrence (Threading).

More Decks by Yves Kalume

Other Decks in Programming