# Što ćete naučiti#
Ovaj vodič objašnjava kako skalirati Flutter modularizacijom u monorepu: kada se isplati dodatna kompleksnost, kako strukturirati shared pakete i kako s vremenom održavati granice između paketa čistima.
Postavit ćete Flutter monorepo s Melosom, uskladiti ovisnosti kroz pakete i izgraditi CI i strategiju testiranja koja ostaje brza čak i kako baza koda raste.
Za kontekst arhitekture aplikacije (feature-first, Clean Architecture i praktične kompromise), pročitajte Arhitektura Flutter aplikacije: Clean Architecture + feature-first.
# Kada se modularizacija isplati (a kada ne)#
Modularizacija nije “default” najbolja praksa. To je alat za skaliranje i uvodi overhead u alatima, CI-ju, upravljanju ovisnostima i održavanju granica.
Modularizirajte kada naiđete na ove signale#
| Signal | Kako to osjećate iz dana u dan | Tipičan prag |
|---|---|---|
| Rast tima | Merge konflikti, nejasno vlasništvo, code review usporava | 4+ inženjera koji tjedno diraju ista područja |
| Bolna vremena builda i testiranja | Cijeli test suite traje predugo, lokalni rebuildovi su tromi | Unit testovi 10+ minuta, česti full rebuildovi |
| Više aplikacija | Treba vam shared design system, auth, analytics, domenska logika | 2+ aplikacije ili white-label setup |
| Visoka povezanost (coupling) | Featurei importaju jedni druge, refactori ruše nevezana područja | Česte regresije izvan promijenjenog featurea |
| Rizik releasa | Teško je razumjeti promjene, nema izolacije | Hotfixovi traju dulje od planiranog |
🎯 Ključna poruka: Modularizirajte kako biste smanjili coupling i ubrzali feedback loopove, a ne da bi stablo mapa izgledalo uredno.
Nemojte još modularizirati ako ste u ovoj situaciji#
| Scenarij | Bolji pristup |
|---|---|
| Jedna aplikacija, 1 do 3 developera | Zadržite jedan paket, koristite feature-first module unutar lib/ |
| Brzo mijenjanje tijekom product discoveryja | Optimizirajte za iteraciju, modularizaciju razmotrite kasnije |
| Nema stabilnih granica | Prvo definirajte featuree i vlasništvo, tek onda dijelite u pakete |
| Nedostaje CI disciplina | Popravite testove i automatizirane provjere prije dodatne kompleksnosti repozitorija |
Korisno pravilo: ako ne možete u jednoj rečenici jasno opisati što ide u svaki paket, niste spremni za razdvajanje.
# Temelji monorepa: preporučena struktura#
Flutter monorepo treba “kodirati” arhitekturne odluke u file systemu. Cilj je predvidljivo vlasništvo, minimalan coupling između paketa i sigurno ponovno korištenje.
Praktična taksonomija paketa#
Koristite mali broj tipova paketa i držite ih dosljednima:
| Tip paketa | Svrha | Smije ovisiti o | Ne bi smio ovisiti o |
|---|---|---|---|
apps/* | Deployable Flutter aplikacije | features/*, shared/* | Ostalim aplikacijama |
features/* | Feature moduli, UI plus feature-specifična logika | shared/* | Drugim featureima (radije shared apstrakcije) |
shared/* | Cross-cutting biblioteke | Ostalim shared/* (oprezno) | features/* ili apps/* |
tools/* | CI skripte, generatori, dev alati | Svemu (samo dev) | Runtime kodu aplikacije |
Primjer strukture mapa#
| Putanja | Primjer | Zašto postoji |
|---|---|---|
apps/customer_app | glavna korisnička aplikacija | Releasea se u storeove |
apps/admin_app | interna admin aplikacija | Dijeli auth i design system |
features/checkout | checkout UI i tokovi | U vlasništvu feature tima |
shared/design_system | komponente, tokeni | Sprječava dupliranje UI-ja |
shared/core | logging, error tipovi, env | Drži “app glue” minimalnim |
shared/api_client | HTTP klijent i interceptori | Izbjegava copy-paste API setupa |
shared/domain_models | stabilni modeli korišteni kroz featuree | Izbjegava kružne ovisnosti |
⚠️ Upozorenje: “shared” paket bez stroge namjene postaje odlagalište. Ako fajl nema jasno mjesto, napravite novi fokusirani paket ili ga ostavite u featureu.
# Postavljanje Melosa za Flutter monorepo#
Melos je najčešći workspace alat za Dart i Flutter monorepove. Automatizira bootstrapping lokalnih path ovisnosti, pokretanje skripti kroz pakete i usklađivanje ovisnosti.
Korak 1: Kreirajte melos.yaml#
Smjestite ga u root repozitorija.
name: samioda_flutter_workspace
packages:
- apps/**
- features/**
- shared/**
command:
bootstrap:
usePubspecOverrides: true
scripts:
analyze:
run: melos exec --fail-fast -- dart analyze .
description: Run static analysis in all packages
test:
run: melos exec --fail-fast -- dart test
description: Run unit tests in all packages that have them
flutter_test:
run: melos exec --fail-fast -- flutter test
description: Run Flutter tests where applicable
format:
run: melos exec -- dart format --output=none --set-exit-if-changed .
description: Enforce formatting across packagesKorak 2: Bootstrap#
melos bootstrapOvo povezuje lokalne path ovisnosti, tako da paketi mogu ovisiti jedni o drugima bez objave na pub.dev.
💡 Savjet: Commitajte
pubspec_overrides.yamlfajlove ako timu pomaže reproducibilan lokalni setup, ali osigurajte da CI pokrećemelos bootstrapkako bi overrideovi ostali dosljedni.
Korak 3: Uskladite SDK i Flutter constraintove#
U svakom pubspec.yaml paketa držite SDK constraintove identičnima.
| Constraint | Preporuka | Zašto |
|---|---|---|
| Dart SDK | Jedan zajednički range, npr. >=3.4.0 <4.0.0 | Izbjegava razlike u analyzeru i buildu |
| Flutter SDK | Isti Flutter channel i verzija pinana u CI-ju | Sprječava “radi kod mene” |
Kako biste to provodili, dodajte CI provjeru koja pada kad se constraintovi raziđu. Tretirajte to kao dependency lock za monorepove.
# Dizajn shared paketa koji ostaju korisni#
Shared paketi su mjesto gdje monorepovi ili zablistaju ili se raspadnu u špagete. Trik je da shared paketi budu mali, stabilni i “opinionated”.
Obrasci shared paketa koji skaliraju#
| Obrazac | Primjer paketa | Što sadrži | Što izbjegava |
|---|---|---|---|
| Core utiliti | shared/core | error tipove, logging, env konfiguraciju | UI, feature logiku |
| API sloj | shared/api_client | Dio setup, auth interceptore, retry | Feature endpointove |
| Design system | shared/design_system | gumbe, tipografiju, spacing tokene | Feature ekrane |
| Observability | shared/analytics | sučelje za evente, adaptere | poslovne odluke (business logic) |
| Domenske primitive | shared/domain_models | Money, Address, ID-eve | API DTO-ove vezane uz promjene backenda |
Dobra praksa je tretirati shared pakete kao proizvode s verzioniranim API-jem, čak i ako ih nikad ne objavljujete.
Disciplina javnog API-ja s barrel fajlovima#
Izložite uzak javni surface s eksplicitnim exportima.
// shared/core/lib/core.dart
export 'src/logging/logger.dart';
export 'src/errors/app_exception.dart';
export 'src/env/app_env.dart';Konzumenti importaju package:core/core.dart, a ne deep putanje. To smanjuje cijenu refactora i olakšava provođenje granica.
Rano sprječavanje kružnih ovisnosti#
Kružne ovisnosti su česte kada timovi naprave “helpers” koji ovise o feature kodu. Držite tok ovisnosti u jednom smjeru.
| Sloj | Dopušten smjer |
|---|---|
| Apps | prema dolje na features i shared |
| Features | prema dolje na shared |
| Shared | samo na shared utilitije, nikad na features ili apps |
Ako shared paket treba feature-specifično ponašanje, stavite sučelje u shared i implementirajte ga u featureu ili appu.
# Feature paketi: što ide gdje#
Feature paketi trebaju biti kohezivne cjeline koje tim može posjedovati, testirati i isporučiti kao dio aplikacije bez diranja nevezanog koda.
Predložak feature paketa#
| Mapa | Sadržaj | Praktično pravilo |
|---|---|---|
lib/src/ui | ekrani, widgeti | UI ovisi samo o feature stateu |
lib/src/state | BLoC, Riverpod, controlleri | Bez direktnog HTTP-a, zovite use caseove |
lib/src/domain | entiteti, use caseovi | Čisti Dart, bez Flutter importa |
lib/src/data | repozitoriji, mapperi | Data ovisi o API klijentu, storageu |
Ovo se prirodno uklapa s Clean Architecture i feature-first organizacijom. Za širu sliku, pogledajte naš vodič za Clean Architecture + feature-first.
Dependency injection kroz pakete#
Izbjegavajte globalni service locator u shared paketima. Neka aplikacija sastavlja ovisnosti i prosljeđuje ih prema dolje.
Jednostavan composition root u app paketu drži granice čistima.
// apps/customer_app/lib/main.dart
import 'package:flutter/material.dart';
import 'package:api_client/api_client.dart';
import 'package:checkout/checkout.dart';
void main() {
final client = ApiClient(baseUrl: 'https://api.example.com');
final checkoutDeps = CheckoutDependencies(apiClient: client);
runApp(CustomerApp(checkout: checkoutDeps));
}Držite dependency objekte malima i eksplicitnima. Ako feature treba 12 ovisnosti, to je signal da feature paket radi previše.
# Održavanje čistih granica (bez usporavanja tima)#
Granice padaju kada su samo “preporuka”. Trebate laganu provedbu koja hvata kršenja tijekom PR-ova.
Koristite lint pravila dosljedno po paketu#
Minimalno, svugdje provodite flutter_lints ili dart_lints, a zatim postupno dodajte custom pravila.
| Kategorija pravila | Zašto je važno u monorepovima |
|---|---|
| Higijena importa | Sprječava deep importove i slučajan coupling između featurea |
| Neiskorištene ovisnosti | Zaustavlja rast ovisnosti i smanjuje vrijeme builda |
| Strogoća analyzera | Čini refactore sigurnima kroz pakete |
Pokrećite analizu kroz Melos kako bi bila dosljedna.
melos run analyzeProvedite granice importa arhitekturnim testovima#
Praktičan pristup je mali “architecture test” paket koji skenira importove i ruši build ako se pravila krše. Čak i jednostavno pravilo na bazi grepa uhvati većinu problema.
# tools/check_boundaries.sh
set -e
# Disallow features importing other features directly
if grep -R "package:features_" -n features | grep -v "shared/" ; then
echo "Boundary violation: feature importing another feature"
exit 1
fiDržite pravila minimalnima i usklađenima sa stvarnom arhitekturom. Pretjerano stroga pravila stvaraju workarounde i smanjuju povjerenje.
ℹ️ Napomena: Ako intenzivno koristite code generation, izuzmite
*.g.darti build output mape iz boundary provjera kako biste izbjegli bučna padanja.
Kontrola “dependency creep”-a#
U monorepovima najveći tihi trošak je dupliranje ovisnosti i transitive “bloat”.
| Kontrola | Implementacija | Rezultat |
|---|---|---|
| Allow-list ovisnosti | Dokumentirajte što pripada u shared/core vs feature | Manje “brzih dodavanja” |
| Mjesečni pregled ovisnosti | Uklonite neiskorištene, konsolidirajte HTTP, JSON, DI libove | Manja veličina appa, brži buildovi |
| Ujednačene verzije | Iste verzije dio, freezed, riverpod | Manje konflikata |
Za implikacije na performanse, pogledajte Flutter optimizacija performansi za 60 FPS. Veći dependency grafovi i previše rebuild triggera prevode se u dropane frameove i sporiji startup.
# Upravljanje ovisnostima u monorepu#
Trebate dvije stvari: usklađenost i autonomiju. Usklađenost sprječava konflikte, autonomija sprječava da se sve mora mijenjati “u lockstepu”.
Preporučena strategija ovisnosti#
| Tip ovisnosti | Gdje deklarirati | Zašto |
|---|---|---|
| Core tooling | Root dokumentacija i CI skripte | Dosljedno kroz workspace |
| Shared runtime libovi | shared/* paketi | Centraliziraju stabilne primitive |
| Feature-specifični libovi | features/* paketi | Izbjegava napuhavanje cijelog workspacea |
| Dev ovisnosti | Po paketu | Održava toolchaine fokusiranima |
Kada više paketa treba istu biblioteku, radije je dodajte u najspecifičniji shared paket koji “posjeduje” tu odgovornost, a ne u svaki feature.
Pinanje verzija#
Monorepovi često “plutaju” s verzijama dok nešto ne pukne. Za kritične biblioteke koristite eksplicitno pinanje verzija, a upgradeove radite kroz planirano održavanje.
Pragmatičan ritam je jedan dan za upgrade ovisnosti svakih 2 do 4 tjedna, uz sigurnosne nadogradnje odmah.
# Strategija testiranja kroz pakete#
Modularizacija bi trebala smanjiti opseg testiranja i ubrzati feedback. Ako testovi i dalje traže pokretanje cijele aplikacije, modularizacija ne donosi vrijednost.
Što testirati gdje#
| Tip paketa | Primarni testovi | Sekundarni testovi |
|---|---|---|
| Shared čisti Dart | Unit testovi | Contract testovi za adaptere |
| Shared Flutter UI | Golden testovi, widget testovi | Provjere pristupačnosti |
| Feature paketi | Unit testovi za domain, widget testovi za UI | Integration testovi u appu |
| App paketi | Integration testovi, smoke testovi | End-to-end tokovi po releaseu |
Učinite featuree testabilnima bez aplikacije#
Dizajnirajte feature pakete tako da se mogu renderirati izolirano uz injektirane ovisnosti.
// features/checkout/test/checkout_widget_test.dart
import 'package:flutter_test/flutter_test.dart';
import 'package:checkout/checkout.dart';
void main() {
testWidgets('shows empty cart state', (tester) async {
final deps = CheckoutDependencies.fake(emptyCart: true);
await tester.pumpWidget(CheckoutTestApp(deps: deps));
expect(find.text('Your cart is empty'), findsOneWidget);
});
}Ovako držite CI brzim: featurei se mogu verificirati bez buildanja cijele aplikacije.
Izbjegnite flaky testove u monorepu#
Flaky testovi troše više vremena nego spori testovi. Tretirajte flaky testove kao bug.
| Čest izvor flakea | Rješenje |
|---|---|
| Timeri i animacije | Koristite fake async ili pumpajte s determinističkim trajanjem |
| Network pozivi | Koristite fakes, ne pravi HTTP |
| Golden testovi na različitim strojevima | Pinajte Flutter verziju, koristite konzistentne fontove i setup renderiranja |
# CI za Flutter monorepove: brzi PR-ovi, pouzdani releasi#
CI treba biti selektivan, cacheiran i dosljedan. Najveći dobitak je preskakanje posla kada promjena pogađa samo jedan feature.
Za specifičnosti CI/CD platformi, pogledajte Flutter CI/CD s GitHub Actions, Codemagic i Fastlane.
CI faze koje se dobro pokazuju#
| Faza | Pokreće se na | Cilj |
|---|---|---|
| Lint i format | svaki PR | rano uhvatiti jeftine probleme |
| Unit testovi po paketu | svaki PR | brzi signal ispravnosti |
| Build aplikacija | kada se promijene app ili shared paketi | provjera kompilacije i asseta |
| Integration testovi | noću i prije releasa | provjera kritičnih tokova |
| Release | tagirani commitovi | deterministični store buildovi |
Odabir jobova na temelju putanja (path-based)#
Koristite path filtere da promjena dokumentacije ne pokreće buildove aplikacije. Također, ako se promijeni samo jedan feature, pokrenite testove za taj feature plus shared pakete o kojima ovisi.
I bez naprednih alata za dependency graf, jednostavno mapiranje daje velike dobitke.
| Promijenjena putanja | Minimalni jobovi |
|---|---|
shared/core/** | analyze i test za sve pakete, build aplikacija |
features/checkout/** | analyze i test za checkout, build aplikacija koje ga koriste |
apps/customer_app/** | analyze i test za customer app, integration testovi opcionalno |
💡 Savjet: Krenite konzervativno sa selekcijom, pa optimizirajte. Monorepo CI koji propušta greške gori je od sporijeg CI-ja.
Osnove cacheiranja#
Cacheirajte ono što stvarno troši vrijeme:
| Cache | Što ubrzava | Napomene |
|---|---|---|
| Pub cache | fetch ovisnosti | Radi kroz jobove ako je key ispravno postavljen |
| Build outputi | incremental buildovi | Često vezano uz Flutter verziju i OS |
| CocoaPods | iOS buildovi | Cacheirajte pods mapu oprezno |
Pinajte Flutter verziju u CI-ju. Male razlike u Flutter ili Dart patch verzijama mogu slomiti golden testove i stvoriti teško debuggabilne nedosljednosti.
# Uobičajene zamke i kako ih izbjeći#
- 1
Previše paketa prerano
Krenite s nekoliko splitova visoke vrijednosti: design system, API client i jedan ili dva velika featurea. - 2
Dopuštanje da featurei ovise jedni o drugima
Ako dva featurea trebaju shared logiku, izdvojite je u fokusirani shared paket sa stabilnim API-jem. - 3
Shared paket postaje “utils” kanta
Ako je naziv paketa neodređen, postat će i sadržaj. Preferirajte specifična imena poputapi_client,observabilityilidesign_system. - 4
Monorepo bez provođenja granica
Dodajte barem jednu automatiziranu boundary provjeru u CI i pokrećitedart analyzesvugdje na PR-ovima. - 5
CI uvijek pokreće sve
Monorepovi propadaju kada feedback loop postane spor. Uvedite path filtere i testove po paketu rano.
# Ključne poruke#
- Modularizirajte kada veličina tima, vremena builda ili reuse kroz više aplikacija stvaraju stvarnu bol; ne dijelite samo radi strukture.
- Koristite jasnu taksonomiju:
apps/*,features/*,shared/*i držite ovisnosti jednosmjerne. - Shared pakete držite malima i stabilnima, s eksplicitnim javnim API-jima i bez ovisnosti o featureima ili appovima.
- Provedite granice kroz dosljedan lint i jednostavne automatizirane provjere koje blokiraju cross-feature importe.
- Optimizirajte CI path-based selekcijom jobova, agresivnim cachingom i testovima na razini paketa kako bi PR feedback ostao brz.
# Zaključak#
Flutter monorepo s Melosom se isplati kada smanjuje coupling i ubrzava isporuku. Ključni posao nije postavljanje alata, nego dizajniranje paketa s jasnim odgovornostima, automatizirano provođenje granica i izgradnja CI pipelinea koji pokreće samo ono što se promijenilo.
Ako želite pomoć pri implementaciji Flutter monorepo Melos modularizacije u stvarnom proizvodu, Samioda može auditirati vašu trenutnu arhitekturu, predložiti plan podjele na pakete i postaviti CI i testiranje kako bi vaš tim isporučivao brže uz manje regresija.
FAQ
Više iz kategorije Mobilni razvoj
Sve →Flutter vs izvorni iOS/Android u 2026.: kompromisi između troška, performansi i vremena do izlaska na tržište
Praktična, brojkama potkrijepljena usporedba Fluttera i izvornog iOS-a i Androida za 2026. — uključuje model troška, realnost performansi, utjecaj održavanja i okvir za odluku za MVP-ove, UI visokih performansi, zahtjevne platform API-je i regulirane aplikacije.
Vodič za Flutter deep linking za 2026.: Universal Links, Android App Links i pouzdano rutiranje unutar aplikacije
Praktičan, produkcijski spreman vodič za Flutter deep linking: Universal Links, Android App Links, go_router obrada ruta, deferred deep linkovi, osnove atribucije u analitici i kontrolna lista za rješavanje problema.
Flutter CI/CD u 2026.: GitHub Actions vs Codemagic vs Fastlane (uz nacrt produkcijskog pipelinea)
Praktičan vodič za Flutter CI/CD u 2026.: usporedba GitHub Actionsa, Codemagica i Fastlanea te implementacija produkcijski spremnog pipelinea s flavorima, potpisivanjem, build brojevima, testovima, generiranjem koda, cacheiranjem i deployem na trgovine.
Trebate pomoć s projektom?
Gradimo prilagođena rješenja koristeći tehnologije iz ovog članka. Senior tim, fiksne cijene.
Povezani članci
Flutter CI/CD u 2026.: GitHub Actions vs Codemagic vs Fastlane (uz nacrt produkcijskog pipelinea)
Praktičan vodič za Flutter CI/CD u 2026.: usporedba GitHub Actionsa, Codemagica i Fastlanea te implementacija produkcijski spremnog pipelinea s flavorima, potpisivanjem, build brojevima, testovima, generiranjem koda, cacheiranjem i deployem na trgovine.
Flutter vs izvorni iOS/Android u 2026.: kompromisi između troška, performansi i vremena do izlaska na tržište
Praktična, brojkama potkrijepljena usporedba Fluttera i izvornog iOS-a i Androida za 2026. — uključuje model troška, realnost performansi, utjecaj održavanja i okvir za odluku za MVP-ove, UI visokih performansi, zahtjevne platform API-je i regulirane aplikacije.
Vodič za Flutter deep linking za 2026.: Universal Links, Android App Links i pouzdano rutiranje unutar aplikacije
Praktičan, produkcijski spreman vodič za Flutter deep linking: Universal Links, Android App Links, go_router obrada ruta, deferred deep linkovi, osnove atribucije u analitici i kontrolna lista za rješavanje problema.