Separation of Concerns je princip, podle kterého každý modul nebo vrstva aplikace odpovídá za jednu oblast odpovědnosti. Podle Wikipedia termín zavedl Edsger Dijkstra v roce 1974 a od té doby se stal základem architektury softwaru. Oddělení odpovědnosti umožňuje vývojářům měnit jednu vrstvu kódu, aniž by ovlivnili ostatní, což je kriticky důležité v mobilních projektech s dlouhým cyklem podpory.
Hlavní body
Separation of Concerns je princip dekompozice softwarového systému na nezávislé části, z nichž každá řeší jeden úkol. Termín concern (oblast odpovědnosti) označuje jakoukoli oddělitelnou část funkcionality: zobrazení obrazovky, zpracování kliknutí, validaci dat nebo síťovou komunikaci. Princip nařizuje seskupování kódu tak, aby změny v jedné oblasti nevyžadovaly změny v jiných.
V mobilním vývoji se SoC projevuje na několika úrovních: od rozdělení aplikace na obrazovky až po organizaci kódu uvnitř jedné třídy. Activity nebo ViewController, který současně načítá data ze sítě, parsuje JSON a vykresluje UI, porušuje Separation of Concerns — takový kód je obtížné udržovat, testovat a rozšiřovat. Alternativou je přemístit každý typ odpovědnosti do samostatné komponenty.
Princip úzce souvisí s pojmem abstrakce: každá vrstva poskytuje striktně definované rozhraní a skrývá detaily implementace. Díky tomu může vývojář vyměnit knihovnu pro síťovou komunikaci nebo databázi, aniž by přepisoval UI logiku. To je obzvláště cenné v dlouhodobých projektech, kde se požadavky a technologie v čase mění.
Edsger Dijkstra poprvé formuloval myšlenku Separation of Concerns v článku z roku 1974 «On the Role of Scientific Thought». Argumentoval, že složitost softwarových systémů lze řídit jejich rozdělením na části, které jsou analyzovány izolovaně. Tento přístup kontrastoval s monolitickými programy té doby, kde kód mísil výpočty, vstup-výstup a uživatelské rozhraní.
V 80. letech 20. století myšlenku rozvinuli zastánci strukturovaného programování a poté objektově orientovaného přístupu. Jazyky jako Smalltalk a C++ poskytly mechanismy zapouzdření a modularity, které učinily SoC praktickým nástrojem. Moderní architektonické vzory — MVC, MVP, MVVM a Clean Architecture — jsou přímým ztělesněním principu Separation of Concerns.
Ve světě mobilního vývoje Apple prosazoval MVC jako standard pro iOS, kde Model-View-Controller odděluje data, zobrazení a logiku řízení. Google pro Android nabídl architektonická doporučení založená na ViewModel a Repository — každá komponenta řeší svůj specifický úkol. Bez SoC se mobilní aplikace mění v Massive View Controller — třídy s tisíci řádky, kde každá změna riskuje rozbití celé funkcionality.
Čtyři hlavní vrstvy tvoří typickou architekturu mobilní aplikace implementující Separation of Concerns. Každá vrstva odpovídá pouze za svou doménu a komunikuje se sousedními vrstvami prostřednictvím rozhraní.
View odpovídá výhradně za zobrazení dat a zpracování událostí uživatele. V iOS je to UIViewController a UIView, v Androidu — Fragment nebo Activity. ViewModel obsahuje stav obrazovky a logiku převodu dat do formátu připraveného k zobrazení. Oddělení zaručuje, že nahrazení UIKit za SwiftUI nebo přepsání obrazovky do Jetpack Compose neovlivní obchodní logiku.
Testování ViewModel nevyžaduje spuštění emulátoru nebo simulátoru — stačí unit testy, které kontrolují transformaci dat a reakci na akce uživatele. To je přímý důsledek Separation of Concerns: UI se nemíchá s obchodními pravidly a každá komponenta je testována izolovaně.
Use Case (nebo Interactor) obsahuje obchodní pravidla aplikace — výpočty, validace, orchestraci volání k datům. Tato vrstva neví o existenci UI a frameworků platformy. Use Case získává data z Repository, aplikuje na ně logiku a vrací hotový výsledek ViewModel. Oddělení umožňuje opětovné použití jednoho Use Case na různých obrazovkách.
Například LoginUseCase kontroluje platnost emailu, volá AuthRepository pro autentizaci a vrací výsledek. Není závislý na tom, jak vypadá přihlašovací obrazovka — SwiftUI, UIKit nebo Compose. Pokud se obchodní pravidla změní, stačí upravit jeden Use Case, aniž by se sáhlo na UI a databázi.
Repository abstrahuje zdroje dat: vzdálené API, lokální databázi nebo cache v paměti. ViewModel a Use Case nevědí, odkud přesně data pocházejí — Repository rozhoduje, zda načíst ze sítě nebo z cache. Toto oddělení umožňuje změnit implementaci úložiště bez ovlivnění obchodní logiky a UI.
DataSource je ještě nižší úroveň oddělení: NetworkDataSource odpovídá pouze za HTTP požadavky, LocalDataSource — za práci s Room nebo CoreData. Repository kombinuje volání na různé DataSource do jednotného konzistentního rozhraní. Každý DataSource je testován nezávisle pomocí mocků nebo falešných serverů.
Správná implementace vrstvy DataSource zaručuje, že změna schématu databáze nebo nahrazení REST API GraphQL ovlivní pouze jeden DataSource, ale ne Repository a jeho konzumenty. To je přímý důsledek Separation of Concerns na úrovni infrastruktury: každá technical concern je izolována a nahraditelná bez kaskádových změn.
MVVM (Model-View-ViewModel) — nejoblíbenější vzor pro mobilní vývoj, který přímo implementuje Separation of Concerns. Model obsahuje data a obchodní logiku, View odpovídá za zobrazení a ViewModel je propojuje prostřednictvím reaktivních mechanismů. Ve Flutteru hraje analogickou roli BLoC s rozdělením na události, stavy a obchodní logiku.
Clean Architecture Roberta Martina (Uncle Bob) dovádí SoC na maximum: systém je rozdělen na nezávislé kruhy — entity, use cases, adaptéry a frameworky. Vnitřní kruhy (entity) nezávisí na vnějších kruzích (frameworky). To umožňuje měnit databázi, UI framework a dokonce i platformu bez přepisování základní logiky aplikace.
V praxi mobilní projekty zřídka implementují plnou Clean Architecture — pro většinu aplikací stačí třívrstvá architektura: UI, Domain a Data. Domain vrstva obsahuje Use Cases a obchodní modely a je zcela izolována od Android SDK nebo iOS SDK. Takové oddělení poskytuje 80% výhod při 20% úsilí.
// Data layer — odpovídá pouze za získávání dat
class UserRepository(private val api: UserApi) {
suspend fun getUser(id: String): User = api.fetchUser(id)
}
// Domain layer — obchodní logika, nezná API ani databázi
class GetUserNameUseCase(
private val repo: UserRepository
) {
suspend fun invoke(id: String): String {
val user = repo.getUser(id)
return "${user.firstName} ${user.lastName}"
}
}
// UI layer — pouze zobrazení
class UserViewModel(
private val getUserName: GetUserNameUseCase
) {
fun onUserLoaded(id: String) {
viewModelScope.launch {
_name.value = getUserName.invoke(id)
}
}
}
Výše uvedený kód demonstruje čisté oddělení: UserRepository pracuje pouze s API, GetUserNameUseCase obsahuje obchodní logiku formátování názvu a UserViewModel spravuje stav UI. Každá třída má jeden důvod ke změně, což je podstatou Separation of Concerns.
Hlavní výhodou SoC — udržovatelnost. Kód rozdělený do nezávislých vrstev je snadněji analyzovatelný: vývojář se dívá pouze na vrstvu, ve které se vyskytuje chyba, a nerozptyluje se ostatními. V dlouhodobých projektech to zkracuje čas hledání a opravy chyb o 30–50% ve srovnání s monolitickým kódem.
Druhou důležitou výhodou — testovatelnost. Když je obchodní logika izolována od UI a frameworků, je pokryta unit testy bez spouštění emulátoru. Android a iOS projekty s vysokým pokrytím unit testů mají výrazně méně regresí při přidávání nových funkcí.
Hlavním omezením — růst složitosti. Nadměrné drobení na mikrovrstvy a abstrakce vede k tomu, že pro přidání jednoduchého tlačítka vývojář upravuje pět souborů. Princip Separation of Concerns vyžaduje rozumnou rovnováhu: oddělovat pouze ty oblasti, které se skutečně mění nezávisle. Pro malé projekty stačí základní oddělení na UI, logiku a data bez dalších abstrakcí.
Často kladené otázky
SoC je princip oddělení podle oblastí odpovědnosti, zatímco modularita je způsob organizace kódu do fyzických modulů. SoC lze realizovat uvnitř jednoho modulu prostřednictvím vrstev nebo tříd, zatímco modularita vyžaduje rozdělení na nezávislé sestavení.
SoC je nadstavba nad principy SOLID. Single Responsibility Principle (S) je SoC na úrovni jedné třídy. Dependency Inversion Principle (D) pomáhá realizovat SoC mezi vrstvami prostřednictvím rozhraní a vkládání závislostí.
Ano, ale v mírné míře. Pro jednoduchou aplikaci stačí oddělit UI a obchodní logiku. Nadměrný počet vrstev zkomplikuje kód bez praktického užitku. Jak projekt roste, počet vrstev se postupně zvyšuje.
Nemá přímý vliv na výkon — SoC se týká architektury kódu, nikoli provádění. Nicméně rozdělení do vrstev může přidat nepřímou zátěž kvůli dodatečným voláním mezi vrstvami. V praxi je tento vliv zanedbatelný ve srovnání s výhodami udržovatelnosti.
Dependency injection (Hilt, Koin, Swinject) explicitně spravuje hranice mezi vrstvami. Architektonická linter pravidla v Detekt (Android) a SwiftLint (iOS) zakazují importy z nepovolených vrstev. Git hooks mohou kontrolovat, že obchodní vrstva neimportuje UI knihovny.
Shrnutí
Vyvineme mobilní aplikaci na klíč
IT Sectr vytváří aplikace pro iOS a Android pro startupy a podniky od roku 2017. Poradíme vám a navrhneme nejlepší řešení.
Přečtěte si také