SoC (Separation of Concerns) è l'abbreviazione del principio secondo cui un sistema software è suddiviso in aree di responsabilità isolate. Secondo Martin Fowler, la separazione delle preoccupazioni è un elemento chiave del codice manutenibile. Il principio SoC consente agli sviluppatori di modificare un livello di un'applicazione senza influenzare gli altri, cosa particolarmente importante nello sviluppo mobile in team.
Punti chiave
SoC sta per Separation of Concerns — «separazione delle responsabilità» o «separazione delle aree di interesse». Nel contesto dello sviluppo, il termine concern designa qualsiasi funzionalità separabile: rendering dell'interfaccia utente, gestione dei clic, validazione dei dati, comunicazione di rete o lavoro con il database. Il principio SoC prescrive di raggruppare il codice attorno a queste aree in modo che le modifiche in una non influenzino le altre.
L'abbreviazione SoC è ampiamente utilizzata nella letteratura tecnica, nelle discussioni architetturali e nella documentazione dei framework. Ad esempio, nella documentazione di Android Architecture Components, SoC viene menzionato ripetutamente come motivazione per separare ViewModel e View. Nella comunità iOS, il termine viene utilizzato quando si discute il problema del Massive View Controller, conseguenza diretta dell'assenza di SoC.
È importante capire che SoC non è un'azione una tantum, ma un processo continuo. Con la crescita dell'applicazione, emergono nuove aree di responsabilità e l'architettura deve essere rivista. Una buona base di codice attraversa diverse iterazioni di separazione prima di raggiungere uno stato stabile in cui ogni concern è isolato e gestibile.
Separation of Concerns e la sua abbreviazione SoC designano lo stesso principio. L'unica differenza è nel contesto d'uso: il nome completo viene utilizzato in documenti formali, materiali didattici e quando si spiega il concetto per la prima volta a nuovi sviluppatori. SoC è comodo nelle discussioni tecniche, nelle revisioni del codice e nella documentazione dove la brevità è importante.
In un ambiente professionale, entrambi i termini sono intercambiabili. Uno sviluppatore può dire «qui SoC è violato» o «questo viola Separation of Concerns» — il significato non cambia. Tuttavia, negli annunci di lavoro e nei requisiti architetturali, il nome completo è più spesso utilizzato, mentre nelle chat e nelle revisioni del codice si usa l'abbreviazione. Conoscere entrambe le varianti è necessario per un ingresso confortevole nel settore.
Esiste una confusione terminologica: l'abbreviazione SoC viene utilizzata anche nel contesto hardware per System-on-a-Chip (sistema su chip). Nello sviluppo mobile, il contesto è sempre chiaro dall'ambiente — se la discussione riguarda l'architettura del codice, ci si riferisce a Separation of Concerns. In questo articolo, SoC si riferisce sempre al principio di separazione delle responsabilità.
L'architettura a tre livelli è il modo più comune per implementare SoC nelle applicazioni mobili. Divide il codice in Presentation (UI), Domain (logica di business) e Data (fonti di dati). Ogni livello contiene tipi di classe strettamente definiti ed è isolato dai vicini tramite interfacce. Questo approccio è ugualmente efficace per progetti iOS, Android e Flutter.
View e ViewModel formano il livello di presentazione. La View è responsabile del rendering dell'interfaccia e dell'inoltro degli eventi utente. Il ViewModel memorizza lo stato dello schermo e trasforma i dati dal livello Domain in un formato pronto per la visualizzazione. Il ViewModel non ha riferimenti ad Activity, Fragment o UIViewController — questo garantisce SoC tra UI e logica.
Ad esempio, in Android Jetpack, il ViewModel sopravvive alla rotazione dello schermo mentre l'UI viene ricreata. Senza SoC, si dovrebbe salvare lo stato nell'Activity, mescolando la gestione del ciclo di vita con i dati. Il ViewModel risolve questo problema in modo isolato, dimostrando un'implementazione pulita del principio di separazione delle responsabilità.
Gli Use Case contengono regole di business indipendenti dalla piattaforma. Questo livello non importa Android SDK, iOS UIKit né Flutter framework. Un Use Case riceve i dati dal Repository, applica la logica di business e restituisce il risultato. Grazie a SoC, un singolo Use Case può essere riutilizzato su schermate e piattaforme diverse.
Un esempio classico è il ValidateAndSaveUseCase per un modulo di registrazione. Valida l'email e la password, chiama UserRepository per salvare e restituisce un ValidationResult. Né l'UI né il database conoscono le regole di validazione — sono concentrate in un unico punto, facilitandone la modifica.
Il Repository astrae le fonti di dati dal resto dell'applicazione. Il ViewModel non sa da dove provengono i dati — da REST API, GraphQL, database locale o cache. Il Repository decide quale fonte utilizzare e nasconde questa logica dietro un'interfaccia. Questo è SoC tra recupero e consumo dei dati.
DataSource fornisce una separazione ancora più profonda: RemoteDataSource è responsabile solo delle richieste HTTP, LocalDataSource del lavoro con Room, CoreData o SharedPreferences. Il Repository le combina applicando strategie di caching. Ogni DataSource può essere sostituito indipendentemente, cosa critica durante la migrazione tra server o database.
Questo sistema multi-livello di DataSources implementa SoC a livello di infrastruttura: comunicazione di rete, archiviazione locale e caching sono concerns separati, ciascuno con la propria logica e ciclo di vita. Quando si sostituisce un client HTTP, solo RemoteDataSource cambia, mentre il Repository e i livelli superiori rimangono intatti, confermando il valore pratico della separazione delle responsabilità.
MVP (Model-View-Presenter) è stato uno dei primi pattern a implementare esplicitamente SoC nello sviluppo mobile. Il Presenter contiene la logica e controlla la View tramite un'interfaccia. La View è passiva — visualizza solo ciò che il Presenter dice. La separazione semplifica i test: il Presenter viene testato senza emulatore e la View rimane così semplice che non c'è nulla da rompere.
MVVM ha aggiunto il binding reattivo: la View si abbona ai cambiamenti del ViewModel tramite Observable o StateFlow. Il ViewModel non conserva un riferimento alla View, eliminando il rischio di memory leak e separando ulteriormente i concerns. Su Android, MVVM è diventato lo standard grazie a Jetpack ViewModel e LiveData; su iOS, grazie a Combine e RxSwift.
Clean Architecture di Robert Martin porta SoC a una separazione radicale in anelli. L'anello esterno (framework e driver) dipende da quello interno (entità), ma non viceversa. In pratica, i progetti mobili raramente implementano tutti e quattro gli anelli — i livelli Domain e Data attorno a Presentation sono sufficienti. Ma il principio della «dipendenza verso l'interno» offre vantaggi significativi nel cambiare framework.
// View — solo visualizzazione, senza logica
final class LoginViewController: UIViewController {
let viewModel: LoginViewModel
func loginTapped() {
viewModel.login(emailField.text, passwordField.text)
}
}
// ViewModel — contiene la logica dello schermo, non conosce UIKit
final class LoginViewModel {
private let loginUseCase: LoginUseCase
func login(email: String?, password: String?) {
loginUseCase.execute(email, password)
}
}
// Use Case — logica di business, indipendente dalla piattaforma
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'esempio mostra tre livelli di SoC: LoginViewController trasmette solo eventi, LoginViewModel gestisce lo stato, LoginUseCase contiene le regole di business. Ogni classe viene testata indipendentemente e il cambio del framework UI non influisce sul Use Case.
Massive View Controller è la violazione più comune di SoC in iOS. Una classe che gestisce l'UI, elabora richieste di rete, analizza JSON e persiste dati viola il principio a tutti i livelli. La soluzione è estrarre ogni responsabilità in un componente separato: NetworkingService, JSONParser, CoreDataStack, lasciando al ViewController solo la gestione della View.
In Android, un problema simile è God Activity o God Fragment. Un'attività che carica dati, valida moduli, mostra dialoghi e aggiorna l'UI. Si risolve introducendo ViewModel e Repository, che si assumono la gestione dello stato e dei dati. Il ViewModel protegge anche dalla perdita di dati durante la rotazione dello schermo.
La terza violazione è mescolare codice di piattaforma e di business. Ad esempio, inserire una richiesta HTTP direttamente in una SwiftUI View o Android Composable. Questo rende il codice non portabile e difficile da testare. L'approccio corretto è spostare la richiesta in un Repository, chiamato tramite un Use Case, mentre la View si abbona solo al risultato. Ogni elemento del sistema risolve il proprio compito e non oltrepassa i propri confini.
Domande frequenti
No. SoC è un principio più generale di divisione di un sistema in aree di responsabilità. SOLID è un insieme di cinque regole specifiche per la progettazione orientata agli oggetti. Il primo principio di SOLID (Single Responsibility) è un caso particolare di SoC a livello di una singola classe.
Utilizzare la regola di un'unica ragione per cambiare (Single Responsibility). Se una classe cambia a causa di modifiche all'UI, al formato dei dati e alle regole di business — SoC è violato. Strumenti come ArchTest (Android) e StrictConcurrency (iOS) aiutano a rilevare automaticamente queste violazioni.
In teoria, livelli aggiuntivi aggiungono chiamate indirette, ma in pratica l'impatto sulle prestazioni di un'applicazione mobile è trascurabile. Il compilatore inline molte chiamate e le ottimizzazioni JIT e AOT eliminano l'overhead. La manutenibilità del codice guadagna molto più di quanto si perde in astrazioni.
Iniziare estraendo le richieste di rete dall'UI in un Repository. Quindi spostare la logica di business negli Use Case. Utilizzare l'iniezione delle dipendenze per collegare i livelli. Apportare le modifiche in modo iterativo, coprendo il nuovo codice con test — questo garantisce che il refactoring non rompa le funzionalità esistenti.
Nei prototipi, SoC può essere violato per velocità. Ma se un prototipo passa allo sviluppo di produzione, il costo del refactoring può superare il vantaggio di un avvio rapido. L'ideale è mantenere una separazione minima (UI e dati) anche in un prototipo per evitare di riscrivere tutto da zero al lancio.
Riepilogo
Svilupperemo un'applicazione mobile chiavi in mano
IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.
Leggi anche