0Pricing
Android Academy · Leçon

Gérer les dépendances entre modules

Gardez un graphe propre et acyclique.

Gérer les dépendances entre modules est une leçon Android Academy gratuite sur CoddyKit. Ceci est la leçon 3 sur 4. Tu peux lire la leçon complète ci-dessous gratuitement — puis la pratiquer en direct dans le navigateur avec un éditeur de code intégré et un tuteur IA 24/7. Elle fait partie du parcours d'apprentissage Android Academy, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours Android Academy comprend 4 leçons au total.

Le graphe de dépendances

Chaque module déclare les autres modules dont il dépend. Ensemble, ces relations forment un graphe de dépendances. Un graphe sain est un DAG (graphe orienté acyclique) : les dépendances vont dans une seule direction et ne forment jamais de boucle.

Dans cette leçon, vous apprendrez à déclarer proprement les dépendances, à choisir la bonne configuration Gradle, à partager les versions et à éviter les cycles.

Déclarer une dépendance de module

Vous ajoutez une dépendance vers un autre module avec project(":path:to:module") dans le bloc dependencies. Le chemin reflète l'arborescence des dossiers et correspond à ce que vous avez déclaré dans settings.gradle.kts.

// feature/profile/build.gradle.kts
dependencies {
    implementation(project(":core:data"))
    implementation(project(":core:designsystem"))
    implementation(project(":core:model"))
}

implementation ou api

La configuration que vous choisissez détermine ce qui est exposé aux consommateurs :

  • implementation : la dépendance est privée. Les modules qui dépendent du vôtre ne peuvent pas la voir. C'est le choix par défaut.
  • api : la dépendance est réexposée (de manière transitive). Utilisez cette option uniquement lorsque vos types publics proviennent de cette dépendance.

Privilégiez presque toujours implementation : cela accélère la compilation, car la modification d'une dépendance masquée n'oblige pas les consommateurs à recompiler.

// core/data/build.gradle.kts
dependencies {
    // Repository signatures return :core:model types,
    // so consumers need to SEE it -> api
    api(project(":core:model"))

    // Network is an internal detail -> implementation
    implementation(project(":core:network"))
}

Pourquoi implementation accélère les compilations

Avec implementation, Gradle sait qu'une modification d'une dépendance masquée ne peut pas affecter l'ABI publique du module. Les consommateurs n'ont donc pas besoin de recompiler. Avec api, une modification se propage à chaque consommateur transitif.

Règle générale : une dépendance va dans api uniquement si elle apparaît dans les types publics du module (types de retour, paramètres publics). Sinon, utilisez implementation.

// Public -> needs api
fun observeUser(): Flow<User>   // Flow and User leak out

// Internal -> implementation is enough
private val client: OkHttpClient // never exposed

Centraliser les versions : le catalogue de versions

Avec de nombreux modules, vous ne voulez pas répéter les versions des bibliothèques partout. Le catalogue de versions de Gradle (gradle/libs.versions.toml) définit une seule fois les versions et les alias. Chaque module fait référence au même alias.

# gradle/libs.versions.toml
[versions]
compose-bom = "2024.09.00"
retrofit = "2.11.0"

[libraries]
compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "compose-bom" }
retrofit = { group = "com.squareup.retrofit2", name = "retrofit", version.ref = "retrofit" }

Utiliser le catalogue dans un module

Les modules font ensuite référence aux bibliothèques via l'accesseur libs généré. L'absence de numéros de version dans le fichier de compilation signifie que les mises à niveau s'effectuent à un seul endroit.

// core/network/build.gradle.kts
dependencies {
    implementation(platform(libs.compose.bom))
    implementation(libs.retrofit)
}

Le péché capital : les cycles

Il y a un cycle lorsqu'un module A dépend de B et que B dépend à son tour de A (directement ou par l'intermédiaire d'une chaîne). Gradle refuse de compiler une dépendance circulaire. Cela révèle également un problème de conception : la frontière entre les deux modules n'est pas la bonne.

// :feature:cart  -> implementation(project(":feature:checkout"))
// :feature:checkout -> implementation(project(":feature:cart"))
//
// Gradle error:
// Circular dependency between the following tasks:
// :feature:cart:compile -> :feature:checkout:compile -> :feature:cart:compile

Briser un cycle

Pour briser un cycle, extrayez l'élément partagé dans un module inférieur dont les deux modules peuvent dépendre. Si deux fonctionnalités ont besoin des données l'une de l'autre, ce contrat partagé appartient au module central, et non à l'une ou l'autre fonctionnalité.

Le flux descendant est ainsi rétabli : les deux fonctionnalités pointent vers le module central, qui ne pointe jamais vers elles.

// Before: cart <-> checkout (cycle)
// After:  cart -> :core:order  <-  checkout

// core/order/OrderContract.kt
data class Order(val items: List<CartItem>, val total: Double)

// feature/cart    -> implementation(project(":core:order"))
// feature/checkout-> implementation(project(":core:order"))

Inversion : dépendre d'abstractions

Il arrive qu'un module de bas niveau ait besoin d'un comportement situé plus haut. Au lieu de dépendre d'un module supérieur, définissez une interface dans le module inférieur et laissez le module supérieur fournir l'implémentation par injection de dépendances. C'est l'inversion des dépendances.

// core/analytics defines the contract
interface AnalyticsLogger {
    fun log(event: String)
}

// :app provides the real implementation and injects it down
@Module
@InstallIn(SingletonComponent::class)
object AnalyticsModule {
    @Provides
    fun logger(impl: FirebaseAnalyticsLogger): AnalyticsLogger = impl
}

Visualiser et contrôler le graphe

Vous pouvez demander à Gradle d'afficher ou de générer le graphe des modules, et même ajouter un test qui fait échouer la compilation si une dépendance interdite apparaît (par exemple, un module central qui dépend d'une fonctionnalité). Des outils comme le module d'extension module-graph génèrent automatiquement un diagramme.

# Print the project structure
./gradlew projects

# Inspect why :feature:home pulls in a library
./gradlew :feature:home:dependencies --configuration debugRuntimeClasspath

Un exemple propre et acyclique

Voici un graphe sain. Lisez-le de haut en bas : aucune flèche ne remonte, et aucun module ne pointe vers un autre qui pointe en retour vers lui. C'est exactement le résultat que vous recherchez.

// :app
//   -> :feature:home   -> :core:data -> :core:network -> :core:model
//   -> :feature:profile -> :core:data -> :core:database -> :core:model
//   -> :core:designsystem
//
// Every path ends at :core:model. No cycles. Builds in parallel.

Vérification rapide

Votre module :core:network utilise OkHttpClient uniquement en interne : il n'apparaît jamais dans la signature d'une fonction publique. Quelle configuration Gradle devez-vous utiliser pour déclarer la dépendance à OkHttp ?

Récapitulatif : gérer les dépendances entre modules

Vous avez appris à garder le graphe des modules sain :

  • Déclarez les dépendances entre modules avec project(":path").
  • Utilisez implementation par défaut ; utilisez api uniquement pour les types présents dans votre interface publique.
  • Centralisez les versions dans un catalogue de versions (libs.versions.toml).
  • Ne créez jamais de cycles : Gradle les rejette ; brisez-les en déplaçant le code partagé vers le bas ou en inversant les dépendances avec des interfaces.

Ensuite, vous relierez les fonctionnalités entre elles par la navigation, sans les coupler.

Questions Fréquemment Posées

La leçon « Gérer les dépendances entre modules » est-elle gratuite ?

Oui — le texte complet de « Gérer les dépendances entre modules » est gratuit à lire ici sur le web. Pour la pratiquer de manière interactive (un éditeur de code intégré et un tuteur IA 24/7) et déverrouiller le reste du cours Android Academy, passe à CoddyKit PRO. Le cours Android Academy comprend 4 leçons au total.

Qu'est-ce que j'apprendrai dans « Gérer les dépendances entre modules » ?

Gardez un graphe propre et acyclique. Tu pratiques Android Academy avec du code pratique que tu exécutes directement dans le navigateur, et un tuteur IA 24/7 répond à tes questions au fur et à mesure que tu avances dans la leçon.

Dois-je avoir de l'expérience pour commencer Android Academy ?

Aucune expérience préalable n'est requise. Android Academy sur CoddyKit est structuré pour les débutants jusqu'aux apprenants avancés, donc tu peux commencer ici ou depuis le début et avancer à ton rythme. Ceci est la leçon 3 sur 4.

Combien de temps prend la leçon « Gérer les dépendances entre modules » ?

La plupart des leçons CoddyKit prennent environ 5–10 minutes. Chacune est courte et interactive, tu progresses régulièrement et tu repiques exactement où tu t'es arrêté sur le web et l'app.

Peux-tu écrire et exécuter du code dans cette leçon Android Academy ?

Oui. Chaque leçon Android Academy inclut un éditeur de code intégré, tu écris et exécutes du vrai code directement dans ton navigateur et tu reçois des retours IA instantanés — aucune configuration locale requise.

Toutes les leçons de ce cours

  1. Pourquoi modulariser
  2. Modules de fonctionnalités et modules centraux
  3. Gérer les dépendances entre modules
  4. Navigation entre modules
← Retour à Android Academy