Architecture Modulaire Swift : Le Guide Complet

Publié le · Mis à jour le · 32 min

Wlad
Wlad
Fondateur & Tech Lead Swift

La modularisation d'un projet Swift transforme un monolithe difficile à maintenir en un écosystème de modules autonomes, testables et réutilisables. Ce guide couvre les fondamentaux, les patterns avancés, l'intégration avec Swift 6.2 et les meilleures pratiques pour construire des applications iOS, macOS, visionOS et serveur à l'échelle industrielle.

Pourquoi modulariser ?

Un projet Swift monolithique fonctionne bien au départ. Mais à mesure qu'il grandit, les problèmes s'accumulent : temps de compilation exponentiels, conflits de merge constants, difficultés à tester, couplage invisible entre composants.

Les symptômes d'un monolithe en souffrance

SymptômeImpactSeuil critique

Temps de build > 5 min

Productivité dégradée

10 min

Conflits de merge fréquents

Friction équipe

3/semaine

Tests lents ou instables

CI/CD inefficace

15 min

Difficile d'onboarder

Courbe d'apprentissage

2 semaines

Tout casse quand on touche X

Couplage caché

Critique

Les bénéfices concrets de la modularisation

La modularisation apporte des gains mesurables :

  • Compilation incrémentale : seuls les modules modifiés sont recompilés

  • Parallélisation du build : les modules indépendants compilent en parallèle

  • Isolation des tests : chaque module a ses propres tests unitaires

  • Ownership clair : une équipe = un ou plusieurs modules

  • Réutilisation : les modules Core sont partagés entre apps

  • Boundaries explicites : les dépendances sont déclarées, pas implicites

Fondamentaux de Swift Package Manager

Swift Package Manager (SPM) est l'outil standard pour la modularisation depuis Swift 5.3. Il offre une intégration native avec Xcode et supporte toutes les plateformes Apple.

Anatomie d'un Package.swift

Products : Library vs Executable

TypeUsageQuand l'utiliser

.library (static)

Lié au binaire final

Défaut, meilleure performance

.library (dynamic)

Chargé au runtime

Partagé entre plusieurs targets

.executable

Binaire standalone

CLI, scripts, serveur

.plugin

Extension Xcode/SPM

Build tools, linters

Targets et dépendances

Un target est une unité de compilation. Les dépendances entre targets définissent le graphe de build :

Patterns de modularisation

Pattern 1 : Feature Modules

Chaque fonctionnalité métier est un module autonome contenant UI, logique et tests :

Implémentation du point d'entrée public :

Pattern 2 : Core Modules (Infrastructure)

Les modules Core fournissent les fondations partagées :

Exemple de NetworkKit avec Swift Concurrency :

Pattern 3 : Domain Modules (Clean Architecture)

Pour les projets complexes, séparer le domaine métier des détails d'implémentation :

Implémentation avec inversion de dépendances :

Structure d'un projet modulaire

Monorepo avec packages locaux

Structure recommandée pour un projet iOS/macOS modulaire :

Root Package.swift (Umbrella)

Injection de dépendances modulaire

Pattern : Dependency Container

Pattern : Protocol Witness avec @Dependencies

Pour une approche plus testable avec swift-dependencies de Point-Free :

Swift 6.2 et modularisation

Strict Concurrency par module

Swift 6.2 permet de configurer le niveau de concurrency par target :

MainActor par défaut (defaultIsolation)

Avec Swift 6.2, les nouveaux modules UI peuvent opter pour MainActor par défaut :

Boundaries Sendable entre modules

Les frontières entre modules doivent respecter Sendable :

Optimisation des builds

Parallélisation maximale

SPM compile les modules indépendants en parallèle. Pour maximiser le parallélisme :

    • Minimiser les dépendances : chaque dépendance ajoute une contrainte de séquencement
    • Extraire les modules "feuilles" : modules sans dépendances internes
    • Éviter les cycles : un cycle force la compilation séquentielle

Build conditionnel avec Feature Flags

Métriques de build

Script pour mesurer les temps de compilation par module :

Testing modulaire

Structure des tests par module

TestSupport module partagé

Tests avec dépendances injectées

CI/CD modulaire

GitHub Actions optimisé

Migration depuis un monolithe

Stratégie "Strangler Fig"

Migrer progressivement sans big-bang :

    • Identifier les boundaries : mapper les dépendances existantes
    • Extraire CoreKit : commencer par les utilities sans dépendances
    • Créer les protocoles : définir les interfaces avant d'extraire
    • Extraire module par module : un feature à la fois
    • Nettoyer : supprimer le code dupliqué

Pièges et anti-patterns

1. Sur-modularisation

Créer trop de modules ajoute de la complexité sans bénéfice :

Règle : Si un module a moins de 5 fichiers ou < 500 lignes, il devrait probablement être fusionné.

2. Dépendances circulaires

3. Leaky abstractions

4. God Module

5. Import public transitif

Checklist modularisation

Avant de créer un nouveau module :

  • Le module a une responsabilité claire et unique

  • Le module a au moins 5 fichiers / 500 lignes de code

  • Les dépendances sont explicites et minimales

  • Pas de dépendance circulaire

  • L'API publique est documentée

  • Un target de test existe

  • Le module est Sendable-safe (Swift 6)

Avant de merger un PR touchant aux modules :

  • Le graphe de dépendances reste acyclique

  • Les temps de build n'ont pas augmenté significativement

  • Tous les tests passent

  • La documentation est à jour

Pour aller plus loin

Une architecture modulaire bien pensée est un investissement qui paie sur le long terme. Elle permet aux équipes de travailler en parallèle, réduit les temps de build, et rend le code plus maintenable.

Ressources officielles