SoC (Separation of Concerns) est l'abréviation du principe par lequel un système logiciel est divisé en domaines de responsabilité isolés. Selon Martin Fowler, la séparation des préoccupations est un élément clé d'un code maintenable. Le principe SoC permet aux développeurs de modifier une couche d'une application sans affecter les autres, ce qui est particulièrement important dans le développement mobile en équipe.
Points clés
SoC signifie Separation of Concerns — « séparation des responsabilités » ou « séparation des domaines d'intérêt ». Dans le contexte du développement, le terme concern désigne toute fonctionnalité séparable : rendu de l'interface utilisateur, traitement des clics, validation des données, communication réseau ou travail avec la base de données. Le principe SoC prescrit de regrouper le code autour de ces domaines afin que les modifications dans l'un n'affectent pas les autres.
L'abréviation SoC est largement utilisée dans la littérature technique, les discussions architecturales et la documentation des frameworks. Par exemple, dans la documentation d'Android Architecture Components, SoC est mentionné à plusieurs reprises comme motivation pour séparer ViewModel et View. Dans la communauté iOS, le terme est utilisé pour discuter du problème du Massive View Controller, conséquence directe de l'absence de SoC.
Il est important de comprendre que SoC n'est pas une action ponctuelle, mais un processus continu. Au fur et à mesure que l'application grandit, de nouveaux domaines de responsabilité apparaissent et l'architecture doit être revue. Une bonne base de code passe par plusieurs itérations de séparation avant d'atteindre un état stable où chaque concern est isolé et gérable.
Separation of Concerns et son abréviation SoC désignent le même principe. La seule différence réside dans le contexte d'utilisation : le nom complet est utilisé dans les documents formels, les supports pédagogiques et lors de l'explication du concept aux nouveaux développeurs. SoC est pratique dans les discussions techniques, les revues de code et la documentation où la brièveté est importante.
Dans un environnement professionnel, les deux termes sont interchangeables. Un développeur peut dire « ici SoC est violé » ou « cela viole Separation of Concerns » — le sens ne change pas. Cependant, dans les offres d'emploi et les exigences architecturales, le nom complet est plus souvent utilisé, tandis que dans les chats et les revues de code, l'abréviation est employée. La connaissance des deux variantes est nécessaire pour une entrée confortable dans l'industrie.
Il existe une confusion terminologique : l'abréviation SoC est également utilisée dans le contexte matériel pour System-on-a-Chip (système sur une puce). Dans le développement mobile, le contexte est toujours clair d'après l'environnement — si la discussion porte sur l'architecture du code, il s'agit de Separation of Concerns. Dans cet article, SoC fait toujours référence au principe de séparation des responsabilités.
L'architecture à trois couches est la manière la plus courante d'implémenter SoC dans les applications mobiles. Elle divise le code en Presentation (UI), Domain (logique métier) et Data (sources de données). Chaque couche contient des types de classes strictement définis et est isolée des voisines via des interfaces. Cette approche est aussi efficace pour les projets iOS, Android que Flutter.
View et ViewModel forment la couche de présentation. La View est responsable du rendu de l'interface et de la transmission des événements utilisateur. Le ViewModel stocke l'état de l'écran et transforme les données de la couche Domain en un format prêt à être affiché. Le ViewModel ne contient pas de références vers Activity, Fragment ou UIViewController — cela garantit SoC entre l'UI et la logique.
Par exemple, dans Android Jetpack, le ViewModel survit à la rotation de l'écran tandis que l'UI est recréée. Sans SoC, il faudrait sauvegarder l'état dans l'Activity, mélangeant la gestion du cycle de vie avec les données. Le ViewModel résout ce problème de manière isolée, démontrant une implémentation propre du principe de séparation des responsabilités.
Les Use Cases contiennent des règles métier indépendantes de la plateforme. Cette couche n'importe pas Android SDK, iOS UIKit ni Flutter framework. Un Use Case reçoit des données du Repository, applique la logique métier et retourne le résultat. Grâce à SoC, un même Use Case peut être réutilisé sur différents écrans et plateformes.
Un exemple classique est le ValidateAndSaveUseCase pour un formulaire d'inscription. Il valide l'email et le mot de passe, appelle le UserRepository pour sauvegarder et retourne un ValidationResult. Ni l'UI ni la base de données ne connaissent les règles de validation — elles sont concentrées en un seul endroit, ce qui facilite leur modification.
Le Repository abstrait les sources de données du reste de l'application. Le ViewModel ne sait pas d'où viennent les données — de REST API, GraphQL, base de données locale ou cache. Le Repository décide quelle source utiliser et cache cette logique derrière une interface. C'est SoC entre l'obtention et la consommation des données.
DataSource offre une séparation encore plus profonde : RemoteDataSource est responsable uniquement des requêtes HTTP, LocalDataSource du travail avec Room, CoreData ou SharedPreferences. Le Repository les combine en appliquant des stratégies de cache. Chaque DataSource peut être remplacé indépendamment, ce qui est critique lors de la migration entre serveurs ou bases de données.
Ce système multiniveau de DataSources implémente SoC au niveau de l'infrastructure : la communication réseau, le stockage local et la mise en cache sont des concerns séparés, chacun avec sa propre logique et cycle de vie. Lors du remplacement d'un client HTTP, seul RemoteDataSource change, tandis que le Repository et les couches supérieures restent intacts, confirmant la valeur pratique de la séparation des responsabilités.
MVP (Model-View-Presenter) a été l'un des premiers patrons à implémenter explicitement SoC dans le développement mobile. Le Presenter contient la logique et contrôle la View via une interface. La View est passive — elle affiche uniquement ce que le Presenter lui dit. La séparation simplifie les tests : le Presenter est testé sans émulateur et la View reste si simple qu'il n'y a rien à casser.
MVVM a ajouté la liaison réactive : la View s'abonne aux changements du ViewModel via Observable ou StateFlow. Le ViewModel ne conserve pas de référence vers la View, éliminant le risque de fuites mémoire et séparant encore plus les concerns. Sous Android, MVVM est devenu le standard grâce à Jetpack ViewModel et LiveData ; sous iOS, grâce à Combine et RxSwift.
Clean Architecture de Robert Martin pousse SoC à une séparation radicale en anneaux. L'anneau externe (frameworks et pilotes) dépend de l'interne (entités), mais pas l'inverse. En pratique, les projets mobiles implémentent rarement les quatre anneaux — les couches Domain et Data autour de Presentation suffisent. Mais le principe de « dépendance vers l'intérieur » offre des avantages significatifs lors du changement de frameworks.
// View — affichage uniquement, sans logique
final class LoginViewController: UIViewController {
let viewModel: LoginViewModel
func loginTapped() {
viewModel.login(emailField.text, passwordField.text)
}
}
// ViewModel — contient la logique d'écran, ne connaît pas UIKit
final class LoginViewModel {
private let loginUseCase: LoginUseCase
func login(email: String?, password: String?) {
loginUseCase.execute(email, password)
}
}
// Use Case — logique métier, indépendante de la plateforme
final class LoginUseCase {
private let repo: AuthRepository
func execute(email: String?, password: String?) {
guard let e = email, let p = password else { return }
repo.authenticate(e, p)
}
}
L'exemple montre trois niveaux de SoC : LoginViewController transmet seulement les événements, LoginViewModel gère l'état, LoginUseCase contient les règles métier. Chaque classe est testée indépendamment et le changement du framework d'UI n'affecte pas le Use Case.
Massive View Controller est la violation la plus fréquente de SoC sous iOS. Une classe qui gère l'UI, traite les requêtes réseau, analyse le JSON et persiste les données viole le principe à tous les niveaux. La solution consiste à extraire chaque responsabilité dans un composant séparé : NetworkingService, JSONParser, CoreDataStack, en laissant au ViewController uniquement la gestion de la View.
Sous Android, un problème similaire est la God Activity ou God Fragment. Une activité qui charge des données, valide des formulaires, affiche des dialogues et met à jour l'UI. On y remédie en introduisant ViewModel et Repository, qui prennent en charge la gestion de l'état et des données. Le ViewModel protège également contre la perte de données lors de la rotation de l'écran.
La troisième violation est le mélange du code plateforme et métier. Par exemple, placer une requête HTTP directement dans une SwiftUI View ou un Android Composable. Cela rend le code non portable et difficile à tester. L'approche correcte est de déplacer la requête vers un Repository, appelé via un Use Case, tandis que la View ne fait que s'abonner au résultat. Chaque élément du système résout sa propre tâche et ne dépasse pas ses limites.
Questions fréquentes
Non. SoC est un principe plus général de division d'un système en domaines de responsabilité. SOLID est un ensemble de cinq règles spécifiques pour la conception orientée objet. Le premier principe de SOLID (Single Responsibility) est un cas particulier de SoC au niveau d'une seule classe.
Utilisez la règle d'une seule raison de changer (Single Responsibility). Si une classe change à cause de modifications de l'UI, du format des données et des règles métier — SoC est violé. Des outils comme ArchTest (Android) et StrictConcurrency (iOS) aident à détecter ces violations automatiquement.
En théorie, les couches supplémentaires ajoutent des appels indirects, mais dans la pratique, l'impact sur les performances d'une application mobile est négligeable. Le compilateur inline de nombreux appels et les optimisations JIT et AOT éliminent la surcharge. La maintenabilité du code gagne bien plus que ce qui est perdu dans les abstractions.
Commencez par extraire les requêtes réseau de l'UI vers un Repository. Ensuite, déplacez la logique métier dans des Use Cases. Utilisez l'injection de dépendances pour relier les couches. Effectuez les modifications de manière itérative, en couvrant le nouveau code de tests — cela garantit que le refactoring ne casse pas les fonctionnalités existantes.
Dans les prototypes, SoC peut être violé pour la rapidité. Mais si un prototype passe en développement de production, le coût du refactoring peut dépasser le bénéfice d'un démarrage rapide. L'idéal est de maintenir une séparation minimale (UI et données) même dans un prototype pour éviter de tout réécrire depuis le début au lancement.
Résumé
Nous développerons une application mobile clé en main
IT Sectr crée des applications iOS et Android pour les startups et les entreprises depuis 2017. Nous vous conseillerons et vous proposerons la meilleure solution.