Over-engineering sərhədi: nə vaxt sadələşdirmək
Bu branch boyu qatların qazancını göstərdik. Bu mövzu əks tərəfdən — qiymətindən danışır, çünki hər abstraksiyanın ödənilməli olan haqqı var.
Rəsmi Flutter tövsiyələri özləri bu balansı qurur: bəndlərin bir hissəsi ən yüksək prioritetdədir (data/UI ayrılığı, repository, MVVM, DI, immutable modellər, fake-lərlə test), digər hissəsi isə şərtli (domain qatı, ayrı API/domain modelləri, konkret state management seçimi).
Şərtli bəndlərin şərtləri konkretdir:
- Domain qatı (use-case-lər) — yalnız view model-ləri sıxışdıran mürəkkəb məntiq olduqda. Bələdçi əksər tətbiqlər üçün onu artıq yük sayır.
- Ayrı API və domain modelləri — böyük tətbiqlərdə tövsiyə olunur, kiçikdə əlavə söz-söhbət yaradır.
Bundan çıxan praktik nəticə: "tam dəst" arxitektura hər layihə üçün düzgün cavab deyil. Kiçik layihədə minimum dəst (service + repository + notifier) rəsmi tövsiyələrin ən yüksək prioritetli hissəsini tam ödəyir.
| Abstraksiya | Qiyməti | Nə vaxt ödəyir | Nə vaxt ödəmir |
|---|---|---|---|
| Repository müqaviləsi (abstract) | 1 əlavə fayl | Həmişə — ikinci implementasiya test fake-idir | Testi olmayan, bir dəfəlik prototipdə |
| Ayrı DTO + mapper | 2 fayl + mapper testi hər model üçün | Backend başqa komandanındır, JSON "çirklidir" | Backend-i özün idarə edirsən və forma uyğundur |
| Use-case sinifləri | 4 artefakt: fayl, provider, mock, test | İki repository birləşir, ya da məntiq təkrarlanır | Bir repository çağırışını sarımaq |
| `Result` / `Either` | Hər çağırışda `switch` | Yazma və çox addımlı əməliyyatlar | Sadə oxuma — `AsyncNotifier` kifayətdir |
| `sealed` state union | N variant × M sahə | Formalar, wizard, bir-birini istisna edən hallar | Sadə siyahı — `AsyncValue` kifayətdir |
| Ayrı paketlərə bölmə | `pubspec` idarəsi, codegen hər paketdə, `melos` | Bir neçə komanda, kod paylaşılır, uzun ömür | Tək developer, 15 ekran |
| Hər sahə üçün `freezed` | `build_runner` vaxtı | JSON, union, çoxlu sahə | 2 sahəli sadə model — `Equatable` kifayətdir |
Over-engineering-in əlamətləri. Bunlar konkret və müşahidə ediləndir:
- Bir sətirlik siniflər.
class GetProductsUseCase { call() => _repo.fetchActive(); }— məntiq yoxdur, yalnız ötürmə. - Yalnız bir istifadəçisi olan abstraksiya, testdə də əvəz olunmayan. İnterfeys var, lakin
implementsedən bir sinif və o da heç vaxt dəyişməyəcək. - Naviqasiya dərinliyi. Bir baqı izləmək üçün 6 fayl açmaq lazımdır: widget → notifier → use-case → repository → mapper → service.
- Boş qovluqlar.
usecases/,entities/,datasources/— şablondan gəlib, lakin heç vaxt istifadə olunmayıb. - Yalnız mock quraşdırmasından ibarət testlər. Test 20 sətir
when(...)və 1 sətirexpect— belə test davranışı yoxlamır. - Öz "framework"ün. Bir feature üçün yazılmış generic bazalar:
BaseRepository<T>,BaseNotifier<S, E>.
Əks tərəf: under-engineering əlamətləri. Balansı görmək üçün onları da bilmək lazımdır:
- Testdə HTTP-ni ya da JSON-u mock etmək lazım gəlir.
- Eyni hesablama 3+ yerdə təkrarlanır.
- Backend bir sahəni dəyişdikdə 5+ fayl sınır.
buildmetodu 200 sətirdir.- İstifadəçi
SocketExceptionmesajını görür.
Yoxlama sualı. Hər abstraksiya üçün: "bunu silsəm, nə pisləşəcək?" Cavab konkret olmalıdır ("testdə şəbəkəni əvəz edə bilməyəcəyəm", "ikinci ekran kodu kopyalayacaq"). Cavab "clean architecture-a uyğun olmayacaq"dırsa — abstraksiya öz səbəbini itirmişdir.
╔═══ PROTOTİP (bir dəfəlik, 1-2 həftə ömür) ═══╗
✅ Widget + birbaşa sorğu — kifayətdir
❌ Repository, DTO, use-case, test — vaxt itkisi
Səbəb: kod silinəcək. Abstraksiyanın qazancı yaşamağa vaxt tapmır.
╔═══ KİÇİK (< 10 ekran, 1 developer, uzun ömür) ═══╗
✅ Service + repository (müqavilə ilə) + notifier
✅ Domain modelləri (immutable, biznes getter-ləri)
✅ Tipli Failure + dörd UI halı
✅ Repository və notifier testləri
⚠️ DTO: yalnız "çirkli" endpoint-lər üçün
❌ Use-case sinifləri (şərtlər ödənmir)
❌ Paketlərə bölmə
Qeyd: bu dəst rəsmi tövsiyələrin ƏN YÜKSƏK prioritetli
hissəsini tam ödəyir. "Yarımçıq" deyil.
╔═══ ORTA (10-40 ekran, 2-5 developer) ═══╗
✅ Yuxarıdakıların hamısı
✅ DTO + mapper (bütün endpoint-lər)
✅ Hibrid qovluq strukturu (ui/<feature>, data/ qat üzrə)
✅ Result (yazma əməliyyatları üçün)
✅ CI-də domain təmizliyi yoxlaması
⚠️ Use-case: yalnız şərt ödənən yerlərdə (3-5 ədəd normaldır)
❌ Paketlərə bölmə (hələ lazım deyil)
╔═══ BÖYÜK (40+ ekran, bir neçə komanda) ═══╗
✅ Yuxarıdakıların hamısı
✅ Use-case qatı (daha geniş)
✅ Paketlərə bölmə: domain, data, feature-lər
✅ melos + paket-səviyyəsində CI
✅ Golden testlər, integration test paketi
─── QAYDA ───
Dəsti YUXARIDAN AŞAĞI böyütmək lazımdır, əksinə deyil.
Kiçik layihəni "böyük" dəstlə başlamaq ən çox rast gəlinən
over-engineering formasıdır — və onu geri qaytarmaq çətindir.Layihə ölçüsünə görə tövsiyə olunan dəst. Hər sətir rəsmi tövsiyələrin prioritetlərinə söykənir.
Müsahibədə bu mövzu güclü siqnaldır. "Clean architecture-ı hər yerdə tətbiq edirəm" cavabı zəifdir; "hansı hissəsini niyə tətbiq edirəm" cavabı güclüdür. Rəsmi tövsiyələrin prioritet bölgüsünə istinad etmək bu cavabı mənbəyə əsaslandırır: "domain qatını hər feature-də yazmıram, çünki rəsmi bələdçi onu şərtli sayır və bizim view model-lərimiz hələ sadədir".
Praktika. Öz layihən üçün (ya da tanış bir layihə üçün) audit yaz: hər abstraksiyanı sadala və hər biri üçün "bunu silsəm, nə pisləşəcək?" sualına bir cümləlik cavab ver.
Cavabı konkret olmayan (yalnız "prinsipə uyğun olmayacaq") abstraksiyaları qeyd et — onlar silinməyə namizəddir.
Hazır sayılır: siyahıda ən azı 8 abstraksiya var, hər biri üçün konkret cavab yazılıb, və 1-2 namizəd tapılıb (ya da "hamısının səbəbi var" nəticəsi əsaslandırılıb).
📚 Mənbələr və sənədlər
- Arxitektura tövsiyələrirəsmidocs.flutter.dev
Bu mövzunun mənbəyi: hansı bəndlər ən yüksək prioritetlidir, hansılar şərtli.
- Arxitektura bələdçisi: opsional domain qatırəsmidocs.flutter.dev
Use-case-lərin qazanc/qiymət siyahısı və "yalnız lazım olduqda əlavə et" tövsiyəsi.
- Arxitektura konseptlərirəsmidocs.flutter.dev
Prinsiplərin özü — şablonlardan azad şəkildə; sadələşdirmə qərarları üçün istinad nöqtəsi.