Mobilni razvoj
FlutterMonorepoMelosModularizationCI/CDTestingArchitecture

Skaliranje Fluttera modularizacijom: Postavljanje monorepa s Melosom, dijeljenim paketima i čistim granicama

AO
Adrijan Omićević
·14 min čitanja

# Š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#

SignalKako to osjećate iz dana u danTipičan prag
Rast timaMerge konflikti, nejasno vlasništvo, code review usporava4+ inženjera koji tjedno diraju ista područja
Bolna vremena builda i testiranjaCijeli test suite traje predugo, lokalni rebuildovi su tromiUnit testovi 10+ minuta, česti full rebuildovi
Više aplikacijaTreba vam shared design system, auth, analytics, domenska logika2+ aplikacije ili white-label setup
Visoka povezanost (coupling)Featurei importaju jedni druge, refactori ruše nevezana područjaČeste regresije izvan promijenjenog featurea
Rizik releasaTeško je razumjeti promjene, nema izolacijeHotfixovi 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#

ScenarijBolji pristup
Jedna aplikacija, 1 do 3 developeraZadržite jedan paket, koristite feature-first module unutar lib/
Brzo mijenjanje tijekom product discoveryjaOptimizirajte za iteraciju, modularizaciju razmotrite kasnije
Nema stabilnih granicaPrvo definirajte featuree i vlasništvo, tek onda dijelite u pakete
Nedostaje CI disciplinaPopravite 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 paketaSvrhaSmije ovisiti oNe bi smio ovisiti o
apps/*Deployable Flutter aplikacijefeatures/*, shared/*Ostalim aplikacijama
features/*Feature moduli, UI plus feature-specifična logikashared/*Drugim featureima (radije shared apstrakcije)
shared/*Cross-cutting bibliotekeOstalim shared/* (oprezno)features/* ili apps/*
tools/*CI skripte, generatori, dev alatiSvemu (samo dev)Runtime kodu aplikacije

Primjer strukture mapa#

PutanjaPrimjerZašto postoji
apps/customer_appglavna korisnička aplikacijaReleasea se u storeove
apps/admin_appinterna admin aplikacijaDijeli auth i design system
features/checkoutcheckout UI i tokoviU vlasništvu feature tima
shared/design_systemkomponente, tokeniSprječava dupliranje UI-ja
shared/corelogging, error tipovi, envDrži “app glue” minimalnim
shared/api_clientHTTP klijent i interceptoriIzbjegava copy-paste API setupa
shared/domain_modelsstabilni modeli korišteni kroz featureeIzbjegava 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.

YAML
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 packages

Korak 2: Bootstrap#

Bash
melos bootstrap

Ovo povezuje lokalne path ovisnosti, tako da paketi mogu ovisiti jedni o drugima bez objave na pub.dev.

💡 Savjet: Commitajte pubspec_overrides.yaml fajlove ako timu pomaže reproducibilan lokalni setup, ali osigurajte da CI pokreće melos bootstrap kako bi overrideovi ostali dosljedni.

Korak 3: Uskladite SDK i Flutter constraintove#

U svakom pubspec.yaml paketa držite SDK constraintove identičnima.

ConstraintPreporukaZašto
Dart SDKJedan zajednički range, npr. >=3.4.0 <4.0.0Izbjegava razlike u analyzeru i buildu
Flutter SDKIsti Flutter channel i verzija pinana u CI-juSprječ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#

ObrazacPrimjer paketaŠto sadržiŠto izbjegava
Core utilitishared/coreerror tipove, logging, env konfiguracijuUI, feature logiku
API slojshared/api_clientDio setup, auth interceptore, retryFeature endpointove
Design systemshared/design_systemgumbe, tipografiju, spacing tokeneFeature ekrane
Observabilityshared/analyticssučelje za evente, adaptereposlovne odluke (business logic)
Domenske primitiveshared/domain_modelsMoney, Address, ID-eveAPI 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.

Dart
// 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.

SlojDopušten smjer
Appsprema dolje na features i shared
Featuresprema dolje na shared
Sharedsamo 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#

MapaSadržajPraktično pravilo
lib/src/uiekrani, widgetiUI ovisi samo o feature stateu
lib/src/stateBLoC, Riverpod, controlleriBez direktnog HTTP-a, zovite use caseove
lib/src/domainentiteti, use caseoviČisti Dart, bez Flutter importa
lib/src/datarepozitoriji, mapperiData 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.

Dart
// 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 pravilaZašto je važno u monorepovima
Higijena importaSprječava deep importove i slučajan coupling između featurea
Neiskorištene ovisnostiZaustavlja rast ovisnosti i smanjuje vrijeme builda
Strogoća analyzeraČini refactore sigurnima kroz pakete

Pokrećite analizu kroz Melos kako bi bila dosljedna.

Bash
melos run analyze

Provedite 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.

Bash
# 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
fi

Drž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.dart i 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”.

KontrolaImplementacijaRezultat
Allow-list ovisnostiDokumentirajte što pripada u shared/core vs featureManje “brzih dodavanja”
Mjesečni pregled ovisnostiUklonite neiskorištene, konsolidirajte HTTP, JSON, DI liboveManja veličina appa, brži buildovi
Ujednačene verzijeIste verzije dio, freezed, riverpodManje 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 ovisnostiGdje deklariratiZašto
Core toolingRoot dokumentacija i CI skripteDosljedno kroz workspace
Shared runtime libovishared/* paketiCentraliziraju stabilne primitive
Feature-specifični libovifeatures/* paketiIzbjegava napuhavanje cijelog workspacea
Dev ovisnostiPo paketuOdrž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 paketaPrimarni testoviSekundarni testovi
Shared čisti DartUnit testoviContract testovi za adaptere
Shared Flutter UIGolden testovi, widget testoviProvjere pristupačnosti
Feature paketiUnit testovi za domain, widget testovi za UIIntegration testovi u appu
App paketiIntegration testovi, smoke testoviEnd-to-end tokovi po releaseu

Učinite featuree testabilnima bez aplikacije#

Dizajnirajte feature pakete tako da se mogu renderirati izolirano uz injektirane ovisnosti.

Dart
// 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 flakeaRješenje
Timeri i animacijeKoristite fake async ili pumpajte s determinističkim trajanjem
Network poziviKoristite fakes, ne pravi HTTP
Golden testovi na različitim strojevimaPinajte 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#

FazaPokreće se naCilj
Lint i formatsvaki PRrano uhvatiti jeftine probleme
Unit testovi po paketusvaki PRbrzi signal ispravnosti
Build aplikacijakada se promijene app ili shared paketiprovjera kompilacije i asseta
Integration testovinoću i prije releasaprovjera kritičnih tokova
Releasetagirani commitovideterministič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 putanjaMinimalni 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 ubrzavaNapomene
Pub cachefetch ovisnostiRadi kroz jobove ako je key ispravno postavljen
Build outputiincremental buildoviČesto vezano uz Flutter verziju i OS
CocoaPodsiOS buildoviCacheirajte 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. 1

    Previše paketa prerano
    Krenite s nekoliko splitova visoke vrijednosti: design system, API client i jedan ili dva velika featurea.

  2. 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. 3

    Shared paket postaje “utils” kanta
    Ako je naziv paketa neodređen, postat će i sadržaj. Preferirajte specifična imena poput api_client, observability ili design_system.

  4. 4

    Monorepo bez provođenja granica
    Dodajte barem jednu automatiziranu boundary provjeru u CI i pokrećite dart analyze svugdje na PR-ovima.

  5. 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

Share
A
Adrijan OmićevićSamioda Team
All articles →

Više iz kategorije Mobilni razvoj

Sve
·13 min čitanja

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.

FlutteriOSAndroidRazvoj mobilnih aplikacijaUsporedbaVrijeme do izlaska na tržišteTrošak aplikacije
Adrijan OmićevićPročitaj članak
·13 min čitanja

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.

FlutterRazvoj mobilnih aplikacijaDeep linkingUniversal LinksAndroid App Linksgo_routerAnalitika
Adrijan OmićevićPročitaj članak
·15 min čitanja

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.

FlutterCI/CDGitHub ActionsCodemagicFastlaneDevOpsMobilno
Adrijan OmićevićPročitaj članak

Trebate pomoć s projektom?

Gradimo prilagođena rješenja koristeći tehnologije iz ovog članka. Senior tim, fiksne cijene.

Povezani članci

·15 min čitanja

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.

FlutterCI/CDGitHub ActionsCodemagicFastlaneDevOpsMobilno
Adrijan OmićevićPročitaj članak
·13 min čitanja

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.

FlutteriOSAndroidRazvoj mobilnih aplikacijaUsporedbaVrijeme do izlaska na tržišteTrošak aplikacije
Adrijan OmićevićPročitaj članak
·13 min čitanja

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.

FlutterRazvoj mobilnih aplikacijaDeep linkingUniversal LinksAndroid App Linksgo_routerAnalitika
Adrijan OmićevićPročitaj članak