# Što ćete naučiti#
Ovaj vodič uspoređuje dva provjerena pristupa arhitekturi Flutter aplikacija koja se skalira u stvarnim proizvodima: Clean Architecture i Feature-First. Vidjet ćete strukture mapa koje možete copy-pasteati, stroge granice ovisnosti i strategiju testiranja koja refaktore čini sigurnima.
Ako još odlučujete o upravljanju stanjem (state management), uskladite arhitekturu i odabir state pristupa što ranije. Uparite ovaj vodič s našom analizom aktualnih najboljih praksi u članku Flutter state management u 2026.
# Zašto je arhitektura Flutter aplikacije važna u 2026.#
Skalabilna arhitektura Flutter aplikacije smanjuje cijenu promjena. Kako codebase raste, usko grlo postaje koordinacija i rizik regresija, a ne UI framework.
Konkretan utjecaj koji viđamo u timovima koji isporučuju:
- Timovi bez jasnih granica obično usporavaju nakon otprilike 20.000 do 50.000 linija Darta jer “male promjene” počnu se prelijevati preko ekrana, servisa i modela.
- Automatizirani testovi isplate se kada često isporučujete. Industrijska istraživanja dosljedno pokazuju da visoko-performantni timovi snažno ovise o automatiziranom testiranju i CI-u kako bi održali ritam, posebno kod tjednih ili dnevnih izdanja.
- Mobilne aplikacije imaju veći QA overhead nego web. Čista granica između UI-a i poslovne logike drastično smanjuje debugging vezan uz uređaje i ubrzava cikluse pregleda (review).
Cilj nije “savršena arhitektura”. Cilj su predvidljive promjene i brz onboarding.
# Dva pristupa koja se skaliraju#
Oba pristupa rade. Pravi odabir ovisi o složenosti domene, veličini tima i učestalosti izdanja.
| Dimenzija | Clean Architecture | Feature-First arhitektura |
|---|---|---|
| Primarni princip organizacije | Slojevi po odgovornosti | Moduli po featureu |
| Najbolje za | Složena domena, dugovječne aplikacije, regulirani proizvodi | Brza iteracija, više vertikalnih sliceova, produktno vođen razvoj |
| Tipično odgovara timu | 6 do 20+ inženjera | 2 do 10 inženjera |
| Sigurnost refaktora | Vrlo visoka uz jake granice | Visoka ako se granice provode |
| Početni trošak postave | Srednji do visok | Nizak do srednji |
| Česti način propadanja | Previše boilerplatea za jednostavne aplikacije | Coupling između featurea i duplicirana logika |
🎯 Ključna poruka: Odaberite arhitekturu koja minimizira vaš budući trošak promjena, ne onu koja najljepše izgleda na dijagramu.
# Preduvjeti i pretpostavke#
Ovaj članak pretpostavlja:
| Zahtjev | Preporuka | Napomena |
|---|---|---|
| Flutter | Stable channel | U timu držite SDK konzistentnim preko FVM-a |
| Upravljanje stanjem | Bilo koji moderni pristup | Riverpod i Bloc su česti; primjeri su neutralni |
| DI | Opcionalno, ali korisno | get_it ili constructor injection |
| Backend | Bilo koji | Ako koristite Firebase, pogledajte Flutter Firebase tutorial za production uzorke postave |
# Pristup 1: Clean Architecture u Flutteru#
Clean Architecture tipično znači da odvajate odgovornosti u slojeve s pravilima ovisnosti. U Flutteru se dobro preslikava na tri sloja po featureu ili po modulu:
- Presentation: UI, state, kontroleri, view modeli.
- Domain: poslovna pravila, entiteti, use caseovi, sučelja repozitorija.
- Data: API klijenti, perzistencija, DTO-i, implementacije repozitorija.
Pravila ovisnosti za Clean Architecture#
Osnovno pravilo: ovisnosti idu prema unutra.
- Presentation ovisi o Domain.
- Data ovisi o Domain.
- Domain ne ovisi ni o čemu iz koda aplikacije, samo o Dart core i malim čistim paketima.
Time sprječavate da poslovna logika “pokupi” detalje frameworka, što olakšava testiranje i ponovnu upotrebu.
Struktura mapa za Clean Architecture (stvarni primjer)#
Ovo je praktična struktura koja radi za srednje do velike aplikacije. Organizirana je po slojevima, a zatim po featureu unutar svakog sloja.
lib/
app/
di/
injector.dart
routing/
app_router.dart
theme/
app_theme.dart
bootstrap.dart
core/
error/
failures.dart
exceptions.dart
network/
dio_client.dart
connectivity_service.dart
storage/
secure_storage.dart
preferences.dart
utils/
date_time.dart
validators.dart
features/
auth/
data/
datasources/
auth_remote_data_source.dart
auth_local_data_source.dart
dto/
login_request_dto.dart
user_dto.dart
repositories/
auth_repository_impl.dart
domain/
entities/
user.dart
repositories/
auth_repository.dart
usecases/
login.dart
logout.dart
get_current_user.dart
presentation/
controllers/
auth_controller.dart
pages/
login_page.dart
widgets/
login_form.dart
payments/
data/
domain/
presentation/
main.dart
test/
features/
auth/
domain/
login_test.dart
data/
auth_repository_impl_test.dart
presentation/
login_page_test.dartOva struktura se dobro skalira jer svaka datoteka ima jasan “dom”. Također podržava postupno izdvajanje u Dart pakete kasnije, bez promjene konceptualnog modela.
Granice Clean Architecture u praksi#
Čist set import pravila je ono što sprječava da se ova arhitektura raspadne.
| Sloj | Smije importati | Ne smije importati |
|---|---|---|
| Presentation | Domain, core, Flutter UI | Data implementacije, DTO-e |
| Domain | Dart core, funkcionalne pomoćnike | Flutter, Dio, Firebase, shared_preferences |
| Data | Domain, core, vanjske SDK-ove | Presentation |
⚠️ Upozorenje: Najčešće kršenje je korištenje
UserDtou UI-u jer “ima polja koja trebate”. Time vežete UI uz API shape i promjene na backendu postaju puno skuplje.
Entiteti vs DTO-i vs UI modeli#
Odvojite ove koncepte ili ćete kasnije platiti cijenu.
| Tip modela | Nalazi se u | Svrha | Mijenja se kada |
|---|---|---|---|
| Entity | Domain | Stabilno poslovno značenje | Mijenjaju se poslovna pravila |
| DTO | Data | Odgovara API ili bazi (schema) | Mijenja se API ili storage |
| UI model | Presentation | Prilagođen ekranu | Mijenja se UI ili UX |
Ako preskočite UI modele, i dalje možete držati Entities stabilnima i mapirati ih prema UI-u po potrebi. Ključ je: ne dopustite da DTO-i “procure” izvan data sloja.
Primjer: Granica repozitorija i mapiranje#
Ovaj primjer prikazuje sučelje repozitorija u Domain i njegovu implementaciju u Data. UI vidi samo Domain tipove.
// lib/features/auth/domain/repositories/auth_repository.dart
abstract class AuthRepository {
Future<User> login({
required String email,
required String password,
});
Future<void> logout();
}// lib/features/auth/data/repositories/auth_repository_impl.dart
class AuthRepositoryImpl implements AuthRepository {
AuthRepositoryImpl(this._remote);
final AuthRemoteDataSource _remote;
@override
Future<User> login({
required String email,
required String password,
}) async {
final dto = await _remote.login(email: email, password: password);
return User(id: dto.id, email: dto.email);
}
@override
Future<void> logout() => _remote.logout();
}Linija mapiranja izgleda dosadno i upravo je to poanta. To je “šav” na kojem upijate churn s backenda.
Strategija testiranja za Clean Architecture#
Clean Architecture podržava jednostavnu piramidu testiranja jer se svaki sloj može testirati izolirano.
| Tip testa | Meta | Tipični alati | Što validirate |
|---|---|---|---|
| Unit testovi | Domain use caseovi, entiteti | test, fake repozitoriji | Poslovna pravila, rubni slučajevi |
| Unit testovi | Data mapiranje i repo implementacije | mocktail, lokalni fakes | DTO konverzije, obrada grešaka |
| Widget testovi | Presentation stanja | flutter_test | UI za loading, error, success |
| Integracijski testovi | End-to-end tokovi | integration_test | Login, checkout, kritični funnel-i |
Praktičan omjer za produktni tim je otprilike 70 posto unit testova, 20 posto widget testova, 10 posto integracijskih testova. Točan omjer ovisi o tome koliko je UI dinamičan i koliko su funnel-i kritični.
💡 Savjet: Krenite testirati Domain use caseove prvo. Mijenjaju se rjeđe od UI-a i daju najbrži feedback po napisanom testu.
Kada je Clean Architecture pravi izbor#
Odaberite Clean Architecture kada su barem dvije od ovih tvrdnji istinite:
- Vaša domena je složena: dozvole, pravila naplate, workflow-i, offline-first sinkronizacija ili agresivno cacheiranje.
- Više timova radi paralelno i trebate stroge granice kako biste izbjegli merge konflikte.
- Gradite za životni vijek 2+ godine gdje su onboarding i sigurnost refaktora važniji od današnje brzine.
- Trebate visoku pokrivenost testovima jer su izdanja rizična ili regulirana.
Ako rano procjenjujete scope i budžet, arhitekturne odluke utječu na to koliko brzo možete isporučiti MVP i koliko ćete platiti iteracije. Koristite okvir troška kao što opisujemo u mobile app MVP cost.
# Pristup 2: Feature-First arhitektura u Flutteru#
Feature-First organizira codebase oko vertikalnih sliceova: auth, onboarding, profile, feed, itd. Svaki feature sadrži sve što mu treba, što smanjuje kretanje kroz module i čini ownership jasnijim.
Ovaj pristup se skalira kada provodite granice i imate mali shared core za cross-cutting concerns.
Struktura mapa za Feature-First (stvarni primjer)#
Ovo je provjerena struktura za timove koji često isporučuju. Drži svaki feature samodostatnim i koristi shared područje samo za uistinu globalne servise.
lib/
app/
main.dart
bootstrap.dart
router.dart
shared/
http/
api_client.dart
storage/
secure_storage.dart
analytics/
analytics_service.dart
ui/
components/
primary_button.dart
theme/
app_theme.dart
utils/
result.dart
debounce.dart
features/
auth/
api/
auth_api.dart
auth_dto.dart
data/
auth_repository.dart
domain/
user.dart
auth_rules.dart
state/
auth_controller.dart
auth_state.dart
ui/
login_page.dart
widgets/
login_form.dart
test_support/
auth_fakes.dart
profile/
api/
data/
domain/
state/
ui/
main.dart
test/
features/
auth/
auth_flow_test.dart
shared/
utils/
result_test.dartOva struktura nije “manje disciplinirana”. Samo je disciplinirana oko featurea, a ne oko globalnih slojeva.
Granice ovisnosti za Feature-First#
Feature-First propada kada featurei slobodno importaju jedni druge. Riješite to definiranjem eksplicitno dopuštenih ovisnosti.
| Područje | Smije ovisiti o | Ne bi trebalo ovisiti o |
|---|---|---|
features/* | shared/* i isti feature | Direktno o drugim featureima |
shared/* | Dart core, vanjski SDK-ovi | Bilo koji features/* |
app/* | Featurei i shared | Ništa izvan lib/ |
Komunikaciju između featurea možete dopustiti kroz uske “seamove”:
- Routing sloj koji prosljeđuje samo primitive ili ID-eve.
- Zajednički domenski ugovor u
shared/ili zaseban paket. - Evente preko analytics ili messaging sučelja.
ℹ️ Napomena: Ako vam trebaju česti importovi feature-to-feature, to obično znači da vam nedostaje shared apstrakcija ili da granica featurea ne odgovara proizvodu.
Pravila modeliranja podataka u Feature-First#
Feature-First najbolje radi kada svaki feature “posjeduje” svoje modele i mapira ih na granici.
- Držite API DTO-e u
features/feature/api. - Držite sučelja repozitorija i lokalnu data logiku u
features/feature/data. - Držite poslovna pravila i stabilne entitete u
features/feature/domain.
Time sprječavate “global models” mapu koja postaje odlagalište.
Primjer: Održavanje izolacije featurea#
Čest slučaj je da profile treba trenutnog korisnika iz autha. Umjesto da importate auth interne detalje, izložite minimalan ugovor.
Opcija A: definirajte shared sučelje u shared/.
// lib/shared/session/session_reader.dart
abstract class SessionReader {
String? get currentUserId;
}Auth ga implementira interno, a Profile ovisi samo o sučelju. DI wiring veže implementaciju.
Opcija B: proslijedite userId kroz navigaciju i dohvatite profil po ID-u. Time zadržavate nizak coupling i olakšavate deep linkove.
Strategija testiranja za Feature-First#
Testiranje u Feature-First najefikasnije je kada svaki feature ima vlastite test assete i fakes, a integracijske testove rezervirate za tokove kroz više featurea.
| Tip testa | Gdje se nalazi | Fokus |
|---|---|---|
| Unit testovi | features/*/domain | Pravila, mapperi, rubni slučajevi |
| Widget testovi | features/*/ui | Stanja ekrana i renderiranje |
| Integracijski testovi | test/ root | Kritični putovi kroz više featurea |
| Contract testovi | Feature API sloj | Stabilnost API shapea i obrada grešaka |
Contract testovi su važni kada često isporučujete, a backend timovi deployaju neovisno. Ako koristite Firebase, problemi stabilnosti se i dalje mogu dogoditi zbog security rules, indeksa i evolucije sheme, pa pomaže rano validirati reads i writes. Pogledajte production uzorke u Flutter Firebase tutorial.
💡 Savjet: Za testove featurea, kad god je moguće, preferirajte fakes umjesto mockova. Fakes smanjuju krhka očekivanja i ponašaju se bliže production kodu.
# Clean Architecture vs Feature-First: odabir prema timu i ritmu izdanja#
Najjednostavniji okvir odluke: optimizirajte prema svojim ograničenjima.
Matrica odluke#
| Vaše ograničenje | Bolji default | Zašto |
|---|---|---|
| Veličina tima 1 do 3 | Feature-First | Nizak overhead, najbrža iteracija |
| Veličina tima 4 do 8 | Feature-First sa strogim granicama | Ownership po featureu, upravljiva složenost |
| Veličina tima 9+ | Clean Architecture ili hibrid | Jača separacija smanjuje trošak koordinacije |
| Ritm izdanja tjedno ili brže | Feature-First | Kratke povratne petlje i vertikalni sliceovi |
| Ritm izdanja mjesečno, težak QA | Clean Architecture | Izolacija testova smanjuje rizik regresija |
| Složenost domene visoka | Clean Architecture | Štiti domenu od framework churn-a |
| Dugoročno održavanje 2+ godine | Clean Architecture ili hibrid | Bolja sigurnost refaktora i onboarding |
Praktičan hibrid koji dobro radi#
Mnoge uspješne aplikacije kombiniraju oba pristupa:
- Feature-First na top razini:
features/auth,features/profile. - Clean Architecture unutar svakog featurea:
data,domain,presentationiliui/state.
Time se izbjegava “global layered monolith”, a zadržavaju se stroge granice.
Hibridna struktura izgleda ovako:
lib/
shared/
network/
storage/
ui/
features/
checkout/
data/
domain/
presentation/
catalog/
data/
domain/
presentation/
app/
router.dart
di.dartTo je često najbolja polazna točka za timove od 4 do 10 koji isporučuju svakih 1 do 2 tjedna.
Što standardizirati kako biste izbjegli “architecture drift”#
Arhitektura se skalira samo ako ju je lako pratiti i teško prekršiti.
Standardizirajte ove tri stvari:
- 1Smjer ovisnosti: dokumentirajte dopuštene importove i provodite kroz code review.
- 2Konvencije imenovanja: konzistentno
dto,entity,repository,controller. - 3Ugovore testiranja: minimalni skup testova potreban da se feature smatra završenim.
Ako standardizirate i granice statea, smanjujete churn kada se zahtjevi mijenjaju. Za moderne obrasce i trade-offe koristite naš referentni članak Flutter state management u 2026.
⚠️ Upozorenje: “Refaktorirat ćemo kasnije” postaje skupo nakon trećeg ili četvrtog featurea izgrađenog na pogrešnim pretpostavkama. Ako već osjećate bol, zamrznite razvoj featurea na jedan sprint i riješite probleme s granicama prije nego se umnože.
# Checklist za implementaciju: kako skalirati bilo koju arhitekturu#
Ovo su konkretne prakse koje oba pristupa održavaju zdravima.
1) Provedite import pravila#
Napravite jednostavan set arhitekturnih pravila u README-u i učinite ih provjerljivima u reviewu. Zatim rušite build kada se pravila krše.
Lagani pristup je skripta koja u CI-u grep-a zabranjene importove.
#!/usr/bin/env bash
set -e
# Example: prevent cross-feature imports
if rg "import 'package:.*features/.*/" lib/features -g'*.dart' | rg -v "features/\1"; then
echo "Cross-feature imports detected. Use shared contracts or routing."
exit 1
fiNeka bude jednostavno. Kasnije možete prijeći na strože alate.
2) Držite shared kod malenim i namjernim#
Ako sve postane shared, nitko ne “posjeduje” ništa.
Dobri kandidati za shared/:
| Dobar shared kandidat | Zašto pripada u shared |
|---|---|
| Wrapper za API klijenta | Cross-cutting concern |
| Logging i analytics | Cross-cutting concern |
| UI komponente | Koriste se u više featurea |
| Result i error tipovi | Standardiziraju obradu failurea |
Loši kandidati za shared/:
| Loš shared kandidat | Zašto stvara probleme |
|---|---|
| Modeli specifični za feature | Coupling i nejasan ownership |
| Servisi specifični za feature | Skriveni dependencyji |
| “utils” odlagalište | Teško za pronaći, lako za zloupotrijebiti |
3) Koristite stabilnu obradu grešaka kroz slojeve#
Rano odaberite konzistentnu strategiju grešaka. Na primjer:
- Data sloj interno baca exceptions.
- Domain sloj izlaže failures kao tipizirani rezultat.
- Presentation mapira failure u korisničke poruke.
Zapišite formulu za tim na jednom mjestu i držite je konzistentnom.
4) Uložite u CI od početka#
Skalabilna arhitektura bez CI-a i dalje puca pod brzinom.
Minimalne CI provjere koje se brzo isplate:
flutter analyze- unit testovi
- mali set widget testova
- provedba formatiranja
# Ključne poruke#
- Koristite Clean Architecture kada složenost domene, rizik i dugoročno održavanje nadmašuju kratkoročnu brzinu isporuke.
- Koristite Feature-First kada često isporučujete i želite brzu iteraciju, ali provedite stroge granice kako biste izbjegli coupling između featurea.
- Držite modele odvojenima: Entities u domainu, DTO-i u data sloju i UI modeli u presentationu kako biste smanjili cijenu promjena.
- Usvojite praktičnu piramidu testiranja: unit testovi za pravila, widget testovi za UI stanja i malo integracijskih testova za kritične funnel-e.
- Hibrid je često najbolji: Feature-First na top razini i Clean slojevi unutar svakog featurea.
# Zaključak#
Skalabilna arhitektura Flutter aplikacije manje je stvar mapa, a više provedbe smjera ovisnosti, držanja modela tamo gdje pripadaju i testiranja na pravim granicama. Ako odaberete Clean Architecture, kupujete dugoročnu sigurnost refaktora. Ako odaberete Feature-First, kupujete brzinu iteracije i jasniji ownership—pod uvjetom da spriječite importove između featurea.
Ako želite stručni pregled vaše trenutne strukture ili pomoć pri postavljanju production-ready temelja s CI-jem, testiranjem i automatizacijom izdanja, kontaktirajte Samioda i napravit ćemo audit vašeg Flutter codebasea te predložiti konkretan plan migracije vezan uz vaš roadmap i ritam izdanja.
FAQ
Više iz kategorije Mobilni razvoj
Sve →Flutter + Firebase: Potpuni vodič za 2026. (Auth, Firestore, Functions, Deploy)
Korak-po-korak Flutter Firebase vodič za 2026.: postavite Firebase, dodajte autentifikaciju, izgradite Firestore CRUD, napišite Cloud Functions i deployajte aplikaciju spremnu za produkciju.
Koliko košta MVP mobilne aplikacije? Realistična razrada (2026)
Objašnjenje troška MVP-a mobilne aplikacije uz realne razrade po funkcionalnostima, raspona prema tipu aplikacije i usporedbu Fluttera i nativnog razvoja kako biste realno isplanirali budžet.
Koliko košta razvoj Flutter aplikacije u 2026.? (Realni budžeti prema složenosti aplikacije)
Saznajte realan trošak razvoja Flutter aplikacije u 2026. kroz budžete po složenosti, detaljnu tablicu troškova i usporedbu native vs Flutter za iOS i Android.
Trebate pomoć s projektom?
Gradimo prilagođena rješenja koristeći tehnologije iz ovog članka. Senior tim, fiksne cijene.
Povezani članci
Flutter + Firebase: Potpuni vodič za 2026. (Auth, Firestore, Functions, Deploy)
Korak-po-korak Flutter Firebase vodič za 2026.: postavite Firebase, dodajte autentifikaciju, izgradite Firestore CRUD, napišite Cloud Functions i deployajte aplikaciju spremnu za produkciju.
Koliko košta razvoj Flutter aplikacije u 2026.? (Realni budžeti prema složenosti aplikacije)
Saznajte realan trošak razvoja Flutter aplikacije u 2026. kroz budžete po složenosti, detaljnu tablicu troškova i usporedbu native vs Flutter za iOS i Android.
Upravljanje stanjem u Flutteru 2026: Riverpod vs Bloc vs Provider
Praktična usporedba Riverpoda, Bloca i Providera za flutter state management 2026—performanse, DX, testiranje, arhitektura i kada odabrati koji pristup.