Sparround

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.

AbstraksiyaQiymətiNə vaxt ödəyirNə vaxt ödəmir
Repository müqaviləsi (abstract)1 əlavə faylHəmişə — ikinci implementasiya test fake-idirTesti olmayan, bir dəfəlik prototipdə
Ayrı DTO + mapper2 fayl + mapper testi hər model üçünBackend başqa komandanındır, JSON "çirklidir"Backend-i özün idarə edirsən və forma uyğundur
Use-case sinifləri4 artefakt: fayl, provider, mock, testİki repository birləşir, ya da məntiq təkrarlanırBir repository çağırışını sarımaq
`Result` / `Either`Hər çağırışda `switch`Yazma və çox addımlı əməliyyatlarSadə oxuma — `AsyncNotifier` kifayətdir
`sealed` state unionN variant × M sahəFormalar, wizard, bir-birini istisna edən hallarSadə 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ürTə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 implements edə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ətir expect — 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.
  • build metodu 200 sətirdir.
  • İstifadəçi SocketException mesajı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.

text
╔═══ 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