-Banjang veb frontendini qayta tuzish: joylashtirish birliklarini funksiyalar asosida qayta belgilash bo‘yicha retrospektiva-
1. Kirish — Bitta to‘plamga birlashtirilgan ilova
Banjang veb frontend’i uzoq vaqt davomida episode-banjang deb nomlangan yagona ilova sifatida ishlatib kelindi. Unda Bosh sahifa, Ishlar, Jamoa, Meniki, Bildirishnomalar, Autentifikatsiya, Hisob-kitoblar va boshqa turli funksiyalar bitta build artefaktiga kiritilgan, foydalanuvchilarni ajratish usuli ham sodda edi. Yuqori darajadagi router ostida ishchilar va obyekt menejerlari uchun alohida routerlar mavjud bo‘lib, rollar URL prefikslari orqali ajratilgan.
// AS-IS: pages/worker/router.tsx
export const router: RouteObject = {
path: 'wkr', // 작업자(worker)는 /wkr/* 로
element: (
<MobileLayout role="worker" ...>
<Outlet />
</MobileLayout>
),
children: [...],
};
// AS-IS: pages/leader/router.tsx — 구조는 동일
export const router: RouteObject = {
path: 'ldr', // 현장 관리자(leader)는 /ldr/* 로
...
};
URL prefikslari (wkr/ldr) rollarni ko‘rsatgan, ekranlar ro‘yxati esa rolga qarab o‘zgargan. Funksiyalarning o‘zi ishonchli ishlagan bo‘lsa-da, xizmat kengaygani sari “qaysi kod birgalikda joylashtirilishi kerak?” degan savol bo‘yicha cheklovlar ko‘rina boshladi. Ushbu qayta tuzishning boshlang‘ich nuqtasi routingni o‘zgartirish emas, balki joylashtirish birliklarini qayta belgilash edi.
2. Nega ularni ajratdik — Joylashtirish birliklarini funksiyalar bilan moslashtirish
Bitta ilovada barcha funksiya o‘zgarishlari bitta relizga birlashtiriladi. Ishlar ekranidagi kichik o‘zgarish uchun ham butun ilovani qayta build qilish va joylashtirish talab etiladi, bundan tashqari, foydalanuvchi roliga aloqador bo‘lmagan ekran kodi ham shu bundle tarkibiga kiradi. Mobil qurilmalarda ham React Native Shell yagona WebView ilovasini ishga tushirishdan boshqa tanlov yo‘q edi, shu sababli “avval Bosh sahifani ishga tushirib, qolganini kerak bo‘lganda yuklash” kabi strategiyalarni qo‘llash qiyin bo‘lgan.
Shu sababli mezonni rollardan funksiyalarga o‘zgartirdik. Asosiy tamoyil sodda. Joylashtirish birliklarini funksiyalar bo‘yicha ajrating va rollarni har bir funksiya ichida boshqaring.
|
Episode |
Egasi |
|---|---|
|
episode-banjang-home |
Bosh sahifa |
|
episode-banjang-job |
Ishlar |
|
episode-banjang-team |
Jamoa |
|
episode-banjang-chat |
Suhbatlar (Loopin) |
|
episode-banjang-my |
Meniki |
Mobil ilovaning pastki panelidagi Bosh sahifa, Ishlar, Jamoa, Suhbatlar va Meniki bo‘limlarining har biri web’dagi Episode’ga 1:1 mos keladi. Admin (episode-banjang-admin) foydalanuvchi ilovasining pastki panelidagi bo‘limlardan alohida web back-office joylashtirish birligi sifatida yuritiladi. Bu ekran guruhlari va joylashtirish guruhlarini bir xil yo‘nalishda moslashtirish imkonini beradi.
3. Yangi chegaralar — Shell va Episode’lar
Repository tuzilmasi ham Episode’larning ajratilishiga mos ravishda tartibga solinmoqda. Har bir birlikning batafsil ta’riflari kompaniyaning umumiy arxitektura hujjatlarida keltirilgan; bu yerda esa faqat qayta tuzishni tushunish uchun zarur bo‘lgan chegaralarni ko‘rib chiqamiz.
shell/
banjang-mobile-shell # RN 호스트. WebView, 네이티브 인증, 세션, 에피소드 전환 담당
banjang-episode-shell # 웹 에피소드들이 공유하는 UI·브라우저 런타임
shared/
banjang-bridge-contracts # Shell <-> Episode 메시지 계약
banjang-constants # 공통 상수
episodes/
episode-banjang-{home,job,team,my,chat} # + episode-banjang-admin
dramas/
banjang-view / banjang-state / banjangnote-stub
Asosiy qoida shundan iboratki, Episode mustaqil ravishda build qilinadigan va joylashtiriladigan Microapp hisoblanadi. U boshqa Episode’larning source kodini bevosita import qilmaydi, bir nechta Episode’ga kerak bo‘ladigan xatti-harakatlar esa Shell yoki umumiy qatlamlar orqali ulashiladi. Avval “nima umumiy va funksiya qayerdan boshlanadi?” degan savol jamoa kelishuviga bog‘liq edi, ammo ajratishdan keyin papkalar tuzilmasi va import chegaralari bu tamoyilni ancha ravshan ko‘rsatadi.
4. Rollarni boshqarish — URL o‘qidan ekran tanlashga
Mavjud /wkr, /ldr prefiksli routing yo‘qolgan taqdirda ham, rollar o‘rtasidagi farq yo‘qolmaydi. Rollar endi yuqori darajadagi routing o‘qi emas, balki har bir Episode ichida ekran tanlash mezoniga aylanadi. Har bir Episode’da bitta router mavjud bo‘lib, joriy rol BanjangContext.displayType orqali aniqlanadi.
// TO-BE: shell/banjang-episode-shell/src/routing/BanjangRoleRoute.tsx
export const BanjangRoleRoute = ({ allowedRoles, redirectTo }: BanjangRoleRouteProps) => {
const { displayType } = useBanjangContext();
const roles = Array.isArray(allowedRoles) ? allowedRoles : [allowedRoles];
return roles.includes(displayType) ? <Outlet /> : <Navigate to={redirectTo} replace />;
};
Kirishni boshqarish route guard’lar orqali amalga oshiriladi. Boshlang‘ich ekran rolga qarab farq qilsa, ko‘rsatiladigan Scene ayni router ichida tanlanadi.
// TO-BE: episodes/episode-banjang-job/src/pages/router.tsx
const JobRoleIndex = () => {
const { displayType } = useBanjangContext();
const selectedRoutes = displayType === 'leader' ? recruitmentRoutes : jobsRoutes;
return selectedRoutes.children?.find((route) => route.index)?.element ?? null;
};
Masalan, Job Episode’ida ishga qabul qilishni boshqarish (recruitment) obyekt menejerlariga indeks sifatida, ishlar ro‘yxati (jobs) esa ishchilarga ko‘rsatiladi. URL tuzilmasi soddalashadi va “foydalanuvchi roli o‘zgarganda qaysi URL to‘plamiga o‘tishi kerak?” degan savolni boshqarish o‘rniga, tizim “qaysi rollarga ushbu ekranga kirishga ruxsat berilgan?” deb e’lon qiladi. Ikki o‘q — joylashtirish birliklari va rollar — ajratilgach, yangi rol qo‘shish routerlarni takrorlash emas, balki rolni belgilash va ekranlarni tanlash bilan cheklanadi.
5. O‘tishlar uchun mas’ul tomon — Shell va keep-alive deck
Episode’lar soni bir nechta bo‘lgach, o‘tishlar uchun mas’ul tomon aniq belgilanishi kerak. Banjang’da web Episode’lari o‘tishlarni bevosita bajarmaydi; ular faqat manzilni so‘raydi. Qaror qabul qilish va bajarishni mobil Shell amalga oshiradi. Web’da esa u bridge xabarini REQUEST_EPISODE_NAVIGATION yuboradi, navigateBanjangEpisode esa uni o‘rab oladi. So‘rov maqsadli Episode’ni va uning ichida o‘tiladigan path’ni o‘z ichiga oladi.
Mobil Shell’ning yuklash siyosati Bosh sahifaga ustuvorlik beradi.
-
Ilova ishga tushganda, u faqat Bosh sahifa WebView’ini yaratadi.
-
Qolgan tablar birinchi marta tanlanganda bir martadan yaratiladi.
-
Yaratilgandan so‘ng WebView olib tashlanmaydi; ular orasida navigatsiya qilinganda faqat faol qatlam almashtiriladi (keep-alive).
Shuning uchun barcha Episode’lar boshidanoq bir vaqtda mavjud bo‘lmaydi; faqat amalda tashrif buyurilgan Episode’lar WebView stekida saqlanadi. Tayyorlik signallari ham WEB_APP_READY va EPISODE_SCREEN_READY ga bo‘linadi. Birinchisi bridge orqali aloqa qilishga tayyorlikni, ikkinchisi esa Home sahifasidagi dastlabki ma’lumotlarni chizish yakunlanganini bildiradi. Yuklanish holatlarini bitta bosqichga birlashtirish o‘rniga ajratish Shell nimani va qachon kutayotganini aniq ko‘rsatadi.
6. Yuklash strategiyasi — sinab ko‘rilgan va bekor qilingan yondashuvlar bilan birga
Ajratishning boshidagi unumdorlik bilan bog‘liq xavotirlar noaniq farazlardan iborat emas edi. Ishlab chiqish muhitida barcha Episode’larni ishga tushirishning o‘zi qariyb uch daqiqa vaqt olar, shu sababli sekin ishga tushish juda yaqqol sezilardi. Refaktoring davrida to‘qnashuvlarning oldini olish uchun boshqa frontend ishlari vaqtincha to‘xtatilgan edi, shuning uchun ishlab chiqish muhitini tez ishga tushirish ustuvor vazifa bo‘ldi.
Biz avval ketma-ket yuklashni, keyin oldindan yuklashni, undan so‘ng esa tab birinchi marta tanlanganda Episode yaratishni taqqosladik. Natija avvalroq tavsiflangan Home-first + lazy creation + keep-alive siyosati bo‘ldi. Xuddi shu doirada root barrel importlarini fayl darajasidagi deep importlarga o‘zgartirishni ham sinab ko‘rdik.
Tekshiruvni bir necha kun davomida takrorladik. Har bir Episode uchun dastlabki yuklanish HAR faylini taqqosladik va o‘tish bosqichlarini [banjang-episode-timing] konsol xronologiyasi yordamida kuzatdik. Shuningdek, faqat ma’lum bir Episode’ni ishga tushirish va o‘lchovlarga aralashadigan omillarni kamaytirish uchun EXPO_PUBLIC_BENCHMARK_EPISODE dan foydalandik. Bu rasmiy benchmark vositasi bo‘lmasa-da, bir xil sharoitlarda turli yo‘nalishlarni taqqoslash uchun mos keladigan nisbiy o‘lchov vazifasini bajardi.
Kuzatuvlar import konvensiyasini barcha joylarda o‘zgartirishdan olinadigan foyda sezilarli emasligini ko‘rsatdi. Shu sababli kompaniyaning barrel import konvensiyasini saqlab qoldik va deep importlarni bekor qildik; subpath importlarini esa faqat aniq zarur bo‘lgan bir nechta yo‘lda qoldirdik. Muhimi o‘zgarishning o‘zi emas, balki muhitimizda qaysi tanlovlar haqiqatan ham farq yaratganini tasdiqlash edi.
Biz bekor qilgan ishlar ham natijaning bir qismidir. Haqiqiy kodda “barrel sekin” degan farazni qo‘lladik va tekshirdik, shuningdek, muhitimizdagi farq konvensiyani o‘zgartirishni oqlash uchun yetarlicha katta emasligini ko‘rsatuvchi dalil qoldirdik. Xuddi shu masala yana ko‘tarilsa ham, taxminlardan boshlash o‘rniga o‘sha paytdagi kuzatuvlar va qaror ortidagi sabablarni tayanch nuqtasi sifatida ishlatishimiz mumkin.
7. Ajratish qiymati va uning qoplanishi
Tuzilmani bo‘lish nafaqat qo‘lga kiritadigan foydamizni, balki boshqarishimiz kerak bo‘lgan narsalarni ham ko‘paytiradi. Ushbu refaktoringda ham xarajat yaqqol ko‘rindi.
-
Bajarilish birliklari soni oshgani sababli, lokal muhitda bir nechta Vite serverlari va gateway’larni birgalikda boshqarishimiz kerak bo‘ladi; build va deployment pipeline’lari ham Episode’lar soniga mutanosib ravishda boshqariladigan obyektlarga aylanadi.
-
Dastlab topshiriq olganlar darhol ish boshlashi uchun umumiy kodni tartibga solishdan ko‘ra asosiy muhitni deploy qilishni ustuvor deb bildik. Bu jarayonda autentifikatsiya, mavzular, tarmoq va FCM kabi ko‘ndalang masalalar Episode’lar bo‘ylab vaqtincha takrorlandi. Bu keyinchalik ularni birlashtirish niyatida rejalashtirilgan vaqtinchalik chora edi.
-
Nosozliklarni tuzatish chegaralari ham ko‘payadi. Avvalo muammo Shell’da, muayyan Episode’da yoki bridge’ning yuboruvchi yoxud qabul qiluvchi tomonida ekanini aniqlashimiz kerak.
Bu xarajatni qoplaydigan qatlam — Episode Shell. Autentifikatsiya routelari har bir Episode chegarasiga muvofiq createAuthRoutes({ episodePath }) yordamida yaratiladi, gate’ga kirish BanjangAppGate orqali boshqariladi, rollarni almashtirish esa useRoleSelection orqali amalga oshiriladi. Tarmoq, offline fallback’lar va sessiyani sinxronlashtirish ham xuddi shu umumiy qatlamga ko‘chiriladi. Topshiriq olganlarning ish asoslari barqarorlashgach, har bir Episode’ning kirish nuqtasida uning domeniga tegishli ekranlar markaziy e’tibor sifatida qolishi uchun dastlab takrorlangan boilerplate kodni tozaladik.
Yangi ekran qo‘shilganda bu tuzilmaning ta’siri yaqqol namoyon bo‘ladi. Umumiy bog‘lovchi kod Shell’da allaqachon mavjud bo‘lgani uchun yangi kod domen haqida bo‘lishi mumkin. Izolyatsiya xarajati dastlab ortiqcha sarfdek ko‘rinishi mumkin, biroq uni umumiy qatlam orqali qoplash keyingi ishlarda takroriy xarajatlarni kamaytiradigan poydevor yaratadi.
8. Ishlab chiquvchi tajribasi — ishga tushirish usullarini ham tartibga solish
Deployment birliklari soni oshgani sari lokal ishlab chiqish buyruqlarini ham maqsadiga ko‘ra ajratdik. Barcha Episode’larni doim birga ishga tushirish o‘rniga, vazifa uchun kerak bo‘lgan qamrovning o‘zinigina ishga tushirish mumkin bo‘ladigan tarzda tashkil qildik.
-
pnpm start:banjang-local-single [home|job|team|my]: Faqat lokal ravishda o‘zgartirilayotgan Episode’ni ishga tushiradi va qolganlari uchun umumiy ishlab chiqish muhitidan foydalanadi. Bu buyruq kundalik funksiyalarni ishlab chiqish uchun kerak bo‘ladigan qamrovni kichik saqlaydi.
-
pnpm start:banjang-local-all: Barcha Episode’lar va gateway’ni birga ishga tushirib, Shell va bridge o‘rtasidagi integratsiyalangan oqimni tekshiradi. Brauzerda navigatsiya oqimini keep-alive siyosatiga amal qiladigan iframe steki yordamida ham takrorlash mumkin.
-
pnpm build:pkg home, pnpm storybook:pkg view: Paket darajasidagi bajarish buyruq formatini standartlashtirish uchun run-pkg.mjs bilan guruhlangan.
9. Xulosa
Ushbu refaktoringdagi eng katta o‘zgarish routerning shakli emas, balki “nimani yagona deployment birligi deb hisoblash kerak” degan mezon bo‘ldi. Ilovani rollar bo‘yicha bo‘ladigan tuzilma o‘rniga, funksiyalarga asoslangan Episode’larni mustaqil deployment birliklariga aylantirdik va rollarni ular ichidagi ekran tanloviga ko‘chirdik. Mobil Shell Episode’lar o‘rtasidagi o‘tishlar va yuklash siyosatlarini boshqaradi, Episode Shell esa bir nechta web Episode’lar uchun umumiy zarur bo‘lgan bog‘lovchi kodni jamlaydi.
Oxir-oqibat, arxitektura birliklari faqat kod papkalari bilan belgilanmaydi. Deployment birliklari foydalanuvchi tajribasi va tashkilotning ishlash tezligini belgilaydi. Agar asosiy xizmat chegaralari — Home, Job, Team, Conversations va My — barqaror bo‘lsa, shu chegaralarga moslashtirilgan deployment birliklaridan ham uzoq vaqt foydalanish mumkin. Bu ish yaxshi ishlayotgan tuzilmani rad etgan refaktoringdan ko‘ra, rivojlantirish va deploy qilishning keyingi bosqichi uchun chegaralarni qayta chizishga qaratilgan harakat edi.
Mobil ilovadagi pastki navigator kabi tez-tez o‘zgarmaydigan foydalanuvchi tajribasi chegaralari deployment chegaralarini belgilash uchun yaxshi asos bo‘lishi mumkin. Home, Job, Team, Conversations va My kabi beshta funksiya o‘z joyida qolar ekan, har bir Episode o‘zgarish va deploymentning mustaqil birligi sifatida xizmat qilishda davom etishi mumkin. Tuzilmani bo‘lish natijasidan ko‘ra uzoqroq saqlanadigan narsa — bu chegaralar nima uchun tanlanganini tushuntiradigan mezonning mavjudligidir.
Hazel