Kūrėjo dienoraštis Nr. 2: Bėkite nuo įdėjimo funkcijų į savo kodą

Įsivaizduokite sceną: turite atlikti svarbų mokėjimų sistemos riktą, o suinteresuotoji šalis nervingai stebi iš kambario kampo ir klausia naudingų dalykų, pvz., „Kaip arti mes esame iki pataisos?.
Pradedate žiūrėdami į tam tikrą kodą get-payment-methods.ts
aukščiausio lygio API maršruto įėjimo taškas.
// get-payment-methods.ts
const getPaymentMethods = async (req) => {
const paymentMethods = await loadPaymentMethodsForCustomer(req.customerId);
return res.send(paymentMethods);
}
Tai atrodo pagrįsta, jūs galvojate patys.
1 problema: sunku naršyti
Reikia pasižiūrėti ką loadPaymentMethodsForCustomer()
daro. Gerai, eime.
CMD spustelėkite.
// services/payment-methods.ts
const loadPaymentMethodsForCustomer = async (customerId) => {
try {
const traceId = uuid();
const customerPaymentMethods = getPaymentMethodsOrThrow(customerId, traceId);
return customerPaymentMethods.map(someConversionFunction);
} catch(err) {
return errorHandler(err);
}
}
Hmm. Dabar reikia pažiūrėti getPaymentMethodsOrThrow()
.
CMD spustelėkite. CMD spustelėkite. CMD spustelėkite.
Vis giliau ir giliau einame į susivėlusį įdėtų funkcijų tinklą. Saulės šviesa negali prasiskverbti taip giliai į kodų bazę, ir seniai praradote supratimą, kodėl esate čia.
Po kurio laiko atvykstate į Ground Zero, kur vyksta tikrasis dalykas. Šiuo atveju kodas skambina į Stripe API, kad gautų kliento išsaugotus mokėjimo metodus.
// repositories/stripe.ts
await stripe.paymentMethods.list({ customer: stripeCustomerId });
Pagaliau!
2 problema: sunku suprasti įvykių seką
Žinoma, radote šio konkretaus įdėtųjų funkcijų rinkinio apačią. Tačiau dabar bandote suprasti, ką kodas daro duomenims, pavyzdžiui, apskaičiavimą, transformavimą ir atvaizdavimą.
Paprastai naudojant įdėtas funkcijas, kažkas vyksta su duomenimis beveik visuose medžio lygmenyse. Pavyzdžiui, viena funkcija gali atlikti a try/catch
. Kitas gali konvertuoti masyvą, kad tiesiog grąžintų id
kiekvieno mokėjimo būdo lauke. Kitas gali papildyti šią informaciją antruoju API iškvietimu. Ir net po to, kai sekėte kilpą iki pat medžio apačios ir vėl grįžtate, vėliau gali atsirasti visas antrasis įdėtųjų funkcijų rinkinys.
Trumpai tariant, beveik neįmanoma susidaryti mintis apie įvykių seką. Galbūt širdyje žinote, kad kažkur yra loginis srautas, susidedantis iš dešimties dalykų, kurie vyksta vienas po kito, tačiau jums prireiks trijų dienų ir šešių lentos rašiklių, kad tai suprastumėte.
3 problema: tai trapu keisti
Atėjo laikas pakeisti kodą! Tu pats pliena. Eime.
if(paymentMethod.expiry < new Date()) {
throw new Error("Oh dang, try a new card");
}
Gerai. Atlikime testus.
npm test
Momentinis gaisras 🔥🔥🔥. Sprogimai. 600 testų nepavyko. Kas atsitiko?!
Giliai įterptųjų funkcijų problema yra ta, kad skirtingų kodų bazės dalių priklausomybės grafikas greitai tampa daug sudėtingesnis nei reikia.
Tarkime, kad funkcija A vadina funkciją B, kuri vadina funkciją C. Funkcija X taip pat iškviečia funkciją B, kuri iškviečia funkciją C. Funkcija Y iškviečia funkciją Z, kuri savo ruožtu taip pat iškviečia funkciją C.
Taigi, kai pakeitėte funkciją C, sugadinote funkcijas A, B, X, Y ir Z.
Praleidžiate tris valandas bandydami suprasti, kaip tiksliai veikia visos tos funkcijos. Jūs atsisakote ir tiesiog nukopijuokite / įklijuokite funkciją C į naują sritį, kad nesugadintumėte visų kitų dalykų. Liūdno veido jaustukas 😔
4 problema: daug sunkiau išbandyti
Tarkime, kad turite funkciją A, kuri vadinasi funkcija B.
const functionA = async () => {
// complicated things here
const data = await functionB();
// more complicated things here
}
Funkcijoje A turite sudėtingą logiką. Nusprendžiate, kad ji nusipelno gražaus, išsamaus vieneto patikrinimo.
Problema ta, kad norėdami išbandyti funkciją A, turite dvi parinktis:
Bandymo funkcija A ir Funkcija B
Norėdami apeiti funkciją B, naudokite juokelius
1 varianto problema yra ta, kad dabar mūsų vienetų testai iš tikrųjų nėra vienetiniai testai. Tikrai norėjome išbandyti gryną logiką, o dabar atliekame veiksmus su asinchroniniais skambučiais, o tai reikalauja daug daugiau pastangų.
2 varianto problema yra ta, kad pašaipos yra bjaurios ir mažina pasitikėjimą jūsų testais. Jie yra naudinga priemonė, kai to tikrai reikia, bet kai pasikeičiate functionB
jūsų testai functionA
dabar rizikuojate pateikti klaidingus teigiamus rezultatus.
Nė vienas iš variantų nėra puikus ir, kol to dar nesuvokiate, esate „Testing Is Hard And I Don't Like It“ klubo narys. Labai liūdno veido jaustukas 😪
Sprendimas
Man patinka naudoti tai, ką aš vadinu Orkestratoriaus / veiksmų modelis.
An Veiksmas gali būti beveik bet kas. Pavyzdžiui, veiksmas gali:
- Gauti duomenis asinchroniškai
- Transformuoti duomenis
- Atlikite skaičiavimus
Svarbiausia veiksmo savybė yra ta, kad jis griežtai atitinka Vienos atsakomybės principas. Paprastai tariant, jis turėtų atlikti tik vieną iš pirmiau nurodytų dalykų. Gaukite duomenis arba pakeiskite juos. Ne abu.
An Orkestratorius iškviečia daug veiksmų iš eilės.
Orchestrator pavyzdys gali atrodyti taip:
const exampleOrchestrator = async () => {
const initialData = await getInitialData();
const calculation = runCalculation(initialData);
const otherData = await getOtherData(calculation);
const combinedData = combine({ initialData, otherData });
return combinedData;
}
Svarbiausia orkestro savybė yra ta, kad jūs aiškiai matote įvykių seką. Žinoma, jūs galite turėti kokią nors paprastą logiką, pvz if
pareiškimus nustatyti kurios skambinti, tačiau, kol jį lengva skaityti, padarėte gerą darbą.
Uždarymo mintys
Iš tikrųjų ne visada įmanoma parašyti kodą be jokio lizdo. Kartais lizdų įdėjimas gali būti naudingas. Bet jei apskritai galite stengtis parašyti kodą naudodami Orkestras / veiksmo modelis, tavo kodas bus:
- Lengviau naršyti, nes viskas vieno lygio gylyje.
- Skaityti lengviau, nes Orchestratoriuose matoma įvykių seka.
- Mažiau trapus keisti, nes kiekvieno veiksmo tikslas yra aiškus, o jei norite atlikti ką nors konkretaus, galite tiesiog pridėti daugiau veiksmų.
- Lengviau atlikti vienetų testą (kai reikia), nes kiekvienas veiksmas yra atskiras funkcionalumo vienetas, o tyčiotis retai reikia.
Tikiuosi, kad tai bus naudinga. Leiskite man žinoti savo mintis komentaruose!