Kā noņemt viena tabulas mantojumu no jūsu sliežu monolīta

Mantojums ir vienkāršs - kamēr nav jātiek galā ar tehnisko parādu un nodokļiem.

Kad pirms pieciem gadiem sāka darboties Learn galvenā koda bāze, diezgan populāra bija viena tabulas mantošana (STI). Tajā pašā laikā Flatiron Labs komanda izjuta visu, izmantojot to visam, sākot no novērtējumiem un mācību programmas līdz aktivitāšu plūsmas notikumiem un saturam mūsu augošajā mācību vadības sistēmā. Un tas bija lieliski - tas padarīja darbu paveicamu. Tas ļāva pasniedzējiem sagatavot mācību programmu, izsekot studentu progresam un radīt saistošu lietotāju pieredzi.

Bet, kā jau daudzi emuāru ieraksti ir norādījuši (šis, tas, un, piemēram, šis), STI nav ļoti labi mērogojams, īpaši tāpēc, ka pieaug dati un jaunas apakšklases sāk ievērojami atšķirties no to superklases un viena ar otru. Kā jūs jau varēja nojaust, tas pats notika arī mūsu kodu bāzē! Mūsu skola paplašinājās, un mēs atbalstījām arvien vairāk iespēju un stundu veidu. Laika gaitā modeļi sāka uzpūsties un mutēt, un tie vairs neatspoguļo pareizo domēna abstrakciju.

Mēs kādu laiku dzīvojām šajā telpā, piešķirot šim kodam plašu piestātni un labojot to tikai nepieciešamības gadījumā. Un tad pienāca laiks reaktoram.

Dažos pēdējos mēnešos es sāku misiju, lai novērstu vienu īpaši nerimtīgu STI gadījumu, kurā bija iesaistīts nedaudz neskaidri nosauktaisSaturēšanas modelis. Tikpat viegli kā sākotnēji izveidot STI, to faktiski ir diezgan grūti noņemt.

Tātad šajā rakstā es mazliet apskatīšu STI, sniegšu zināmu kontekstu par mūsu jomu, ieskicējam darba jomu un apspriedīsim stratēģijas, kuras es izmantoju, lai droši ieviestu izmaiņas, vienlaikus samazinot nopietnu bojājumu virsmas laukumu, kamēr es izķidāju kodolu no mūsu lietotnes.

Par viena galda mantojumu (STI)

Īsumā, viena tabulas mantojums sliedēs ļauj vienā tabulā uzglabāt vairāku veidu klases. Aktīvajā reģistrā klases nosaukums tiek glabāts kā tips tabulā. Piemēram, jums, iespējams, ir laboratorija, Readme un Projekts, kas visi atrodas satura tabulā:

klase Lab 

Šajā piemērā laboratorijas, lasījumi un projekti ir visa veida saturs, kas varētu būt saistīts ar nodarbību.

Mūsu satura tabulas shēma izskatījās nedaudz šāda, tāpēc jūs varat redzēt, ka tips ir tikai saglabāts tabulā.

izveidot_tabu "saturs", spēks:: kaskāde do | t |
  t.integer "curriculum_id",
  t.string "tips",
  t.text "markdown_format",
  t.string "title",
  t.integer "track_id",
  t.integer "github_repository_id"
beigas

Darba jomas identificēšana

Saturs izplatījās visā lietotnē, dažreiz mulsinoši. Piemēram, tas aprakstīja attiecības stundā modelī.

klases nodarbība  {order (ordinal:: asc)}
  has_one: saturs, ārzemju_atslēga:: mācību programma_id
  has_many: readmes, ārvalstu_key:: curriculum_id
  has_one: lab, ārzemju_atslēga:: curriculum_id
  has_one: readme, ārzemju_atslēga:: curriculum_id
  has_many: piešķirtais_repos, izmantojot:: saturs
beigas

Apjucis? Tā bija arī es. Un tas bija tikai viens no modeļiem, kas man bija jāmaina.

Tā es kopā ar saviem spožajiem un talantīgajiem komandas biedriem (Keita Traversa, Stīvens Nunezs un Spensers Rodžerss) izdomāju labāku dizainu, lai palīdzētu mazināt neskaidrības un atvieglotu šīs sistēmas paplašināšanu.

Jauns dizains

Koncepcija, kuru Content mēģināja pārstāvēt, bija starpnieks starp GithubRepository un stundu.

Katrs “kanoniskās” nodarbības satura fragments ir saistīts ar krātuvi GitHub. Kad stundas tiek publicētas vai “izvērstas” studentiem, mēs izgatavojam šī GitHub repozitorija kopiju un dodam studentiem saiti uz to. Saikne starp nodarbību un izmantoto versiju tiek saukta par AssignedRepo.

Tātad abos nodarbību galos ir GitHub krātuves: kanoniskā versija un izmantotā versija.

klases saturs 
klase AssignedRepo 

Vienā stundā stundām varēja būt vairāki satura elementi, bet mūsu pašreizējā pasaulē tas vairs nav. Tā vietā ir dažāda veida nodarbības, kuras var palūkoties uz sevi, apskatot failus, kas iekļauti saistītajās krātuvēs.

Tātad tas, ko mēs nolēmām darīt, bija aizstāt saturu ar jaunu koncepciju, ko sauc par CanonicalMaterial, un dot AssignedRepo tiešu atsauci uz ar to saistīto mācību, nevis iet cauri saturam.

Vecās un jaunās sistēmas diagramma, kur sarkanas punktētas līnijas norāda ceļus, kas apzīmēti novājināšanai

Ja tas izklausās mulsinoši un patīk lielam darbam, tas tāpēc, ka tā ir. Tomēr galvenais atņemšanas fakts ir tāds, ka mums nācās nomainīt modeli diezgan lielā kodu bāzē, un beigās kaut kur nomainījām 6000 koda rindas.

Tomēr galvenais atņemšanas fakts ir tāds, ka mums nācās aizstāt modeli diezgan lielā kodolbāzē, un beigās kaut kur nomainījām 6000 koda rindas.

STI atjaunošanas un aizstāšanas stratēģijas

Jaunais modelis

Pirmkārt, mēs izveidojām jaunu tabulu ar nosaukumu canonical_materials un izveidojām jauno modeli un asociācijas.

klase CanonicalMaterial 

Mācību programmu tabulai mēs pievienojām arī svešvalodu canonical_material_id, lai stunda varētu saglabāt atsauci uz to.

Piešķirtai_reposu tabulai mēs pievienojām kolonnu lesson_id.

Divkāršie raksti

Pēc tam, kad jaunās tabulas un kolonnas bija uzstādītas, mēs sākām rakstīt gan vecajās, gan jaunajās tabulās vienlaicīgi, lai mums nevajadzētu vairāk nekā vienu reizi izpildīt aizpildīšanas uzdevumu. Ikreiz, kad kaut kas mēģināja izveidot vai atjaunināt satura rindu, mēs arī izveidojām vai atjauninātu kanonisko_materiālu.

Piemēram:

lesson.build_content (
  'repo_name' => repo.name,
  'github_repository_id' => repo_id,
  'markdown_format' => repo.readme
)

lesson.canonical_material = repo.canonical_material
stunda.saudz

Tas ļāva mums likt pamatus satura galīgai noņemšanai.

Aizpildīšana

Nākamais procesa solis bija datu aizpildīšana. Mēs uzrakstījām grābekļa uzdevumus, lai aizpildītu mūsu tabulas un nodrošinātu, ka katram GithubRepository pastāv CanonicalMaterial un ka katrai stundai ir CanonicalMaterial. Un tad mēs vadījām uzdevumus savā ražošanas serverī.

Šajā atkārtotās reakcijas kārtā mēs deva priekšroku derīgu datu iegūšanai, lai mēs varētu veikt labu pārtraukumu, izmantojot mantojuma veidu, kā rīkoties. Tomēr vēl viena reāla iespēja ir rakstīt kodu, kas joprojām atbalsta vecākus modeļus. Pēc mūsu pieredzes ir daudz mulsinošāk un dārgāk uzturēt kodu, kas atbalsta mantoto domāšanu, nekā tas bija aizpildīt un pārliecināties, vai dati ir pareizi.

Pēc mūsu pieredzes ir daudz mulsinošāk un dārgāk uzturēt kodu, kas atbalsta mantoto domāšanu, nekā tas bija aizpildīt un pārliecināties, vai dati ir pareizi.

Aizvietošana

Un tad sākās jautrā daļa. Lai nomaiņu padarītu pēc iespējas drošāku, mēs izmantojām funkciju karodziņus, lai nosūtītu tumšo kodu mazākos PR, kas ļāva mums izveidot ātrāku atgriezenisko saiti un ātrāk zināt, vai lietas sabojājas. Lai to izdarītu, mēs izmantojām ieviešanas dārgakmeni, kuru mēs izmantojam arī standarta funkciju izstrādei.

Ko meklēt

Viena no vissmagākajām sastāvdaļām, veicot nomaiņu, bija milzīgais meklējamo lietu skaits. Vārds “saturs” diemžēl ir sugasvārds, tāpēc nebija iespējams veikt vienkāršu, globālu meklēšanu un aizstāt, tāpēc es mēdzu veikt plašāku meklēšanu, mēģinot ņemt vērā variācijas.

Noņemot STI, jums vajadzētu meklēt šādas lietas:

  • Modeļa vienskaitļa un daudzskaitļa formas, ieskaitot visas tā apakšklases, metodes, lietderības metodes, asociācijas un vaicājumus.
  • Svītrkodēti SQL vaicājumi
  • Kontrolieri
  • Serializatori
  • Skati

Piemēram, attiecībā uz saturu tas nozīmēja meklēt:

  • : saturs - asociācijām un jautājumiem
  • : saturs - asociācijām un jautājumiem
  • .joins (: saturs) - pievienošanās vaicājumiem, kuri jānoķer iepriekšējā meklēšanā
  • .includes (: content) - lai dedzīgi ielādētu otrās kārtas asociācijas, kuras arī jānoķer iepriekšējā meklēšanā
  • saturs: - ligzdotiem vaicājumiem
  • saturs: - atkal vairāk ligzdotu vaicājumu
  • content_id - vaicājumiem tieši ar id
  • .content - metodes izsaukumi
  • . saturs - kolekcijas metodes izsaukumi
  • .build_content - utilītas metode, ko pievieno has_one un pieder_to
  • .create_content - lietderības metode, ko pievieno asociācija has_one un pieder_to
  • .content_ids - utilītas metode, ko pievieno has_many asociācija
  • Saturs - pats klases nosaukums
  • saturs - vienkārša virkne jebkādām grūti kodētām atsaucēm vai SQL jautājumiem

Es uzskatu, ka tas ir diezgan visaptverošs satura saraksts. Un tad es darīju to pašu laboratorijai, readme un projektam. Var redzēt, ka, tā kā Rails ir tik elastīgs un tam ir pievienotas daudzas noderīgas metodes, ir grūti atrast visas vietas, kur modelis tiek izmantots.

Kā faktiski nomainīt ieviešanu pēc tam, kad esat atradis visus zvanītājus

Kad esat atradis visas modeļa zvana vietnes, kuras jūs mēģināt aizstāt vai noņemt, jums jāpārraksta lietas. Kopumā process, kuru mēs sekojām, bija

  1. Aizvietojiet metodes izturēšanos definīcijā vai mainiet metodi zvana vietā
  2. Uzrakstiet jaunas metodes un piezvaniet tām aiz objekta karoga zvana vietā
  3. Izjauciet atkarību no asociācijām ar metodēm
  4. Paaugstiniet kļūdas aiz objekta karoga, ja neesat pārliecināts par kādu metodi
  5. Apmainiet objektus, kuriem ir vienāds interfeiss

Šeit ir katras stratēģijas piemēri.

1a. Aizstāt metodes izturēšanos vai vaicājumu

Daži no aizvietojumiem ir diezgan vienkārši. Jūs ievietojāt objekta karodziņu vietā, lai teiktu: “kad šis karodziņš ir ieslēgts, izsauciet šo kodu, nevis šo citu kodu”.

Tātad, tā vietā, lai veiktu vaicājumus, pamatojoties uz saturu, šeit mēs meklējam, pamatojoties uz canonical_material.

1b. Mainiet metodi zvana vietnē

Dažreiz metodi ir vieglāk nomainīt zvana vietnē, lai standartizētu izsaukto metodi. (To darot, jums vajadzētu palaist savu testa komplektu un / vai rakstīt testus.) Šādi rīkojoties, var pavērt ceļu uz turpmāku reakciju.

Šis piemērs demonstrē, kā pārtraukt atkarību no kolonnas canonical_id, kuras drīz vairs nebūs. Ņemiet vērā, ka mēs aizstājām metodi zvana vietā, neliekot to aiz objekta karoga. Veicot šo reakciju, mēs pamanījām, ka kanonical_id esam noplūkuši vairāk nekā vienā vietā, tāpēc mēs iesaiņojām loģiku, lai to izdarītu citā metodē, kuru mēs varētu ķēde uz citiem jautājumiem. Metode zvana vietā tika mainīta, taču uzvedība nemainījās, kamēr nebija ieslēgts funkcijas karodziņš.

2. Uzrakstiet jaunas metodes un izsauciet tās aiz objekta karoga zvana vietā

Šī stratēģija ir saistīta ar metodes nomaiņu, tikai šajā mēs uzrakstām jaunu metodi un izsaucam to aiz funkcijas karoga zvana vietā. Īpaši noderīga bija metode, kuru sauca tikai vienā vietā. Tas arī ļāva mums sniegt metodei labāku parakstu - vienmēr noderīgu.

3. Izjauciet atkarību no saistībām ar metodēm

Šajā nākamajā piemērā celiņā ir daudz laboratoriju. Tā kā mēs zinām, ka asociācija has_many pievieno noderīgas metodes, mēs aizstājām to, kuru visbiežāk sauc, un noņēmām rindu has_many: labs. Šī metode atbilst tai pašai saskarnei, tāpēc viss, kas izsauca metodi pirms objekta ieslēgšanas, turpinās darboties.

4. Paaugstiniet kļūdas aiz objekta karoga, ja neesat pārliecināts par kādu metodi

Dažreiz mēs nebijām pārliecināti, vai nokavējām zvanu vietni. Tātad, tā vietā, lai sākumā tikai grūti noņemtu metodes, mēs apzināti izvirzījām kļūdas, lai tās varētu noķert manuālās pārbaudes posmā. Tas mums deva labāku veidu, kā izsekot, kur tiek saukta metode.

5. Apmainiet objektus, kuriem ir vienāds interfeiss

Tā kā mēs gribējām atbrīvoties no laboratorijas asociācijas, mēs pārrakstījām laboratorijas ieviešanu? metode. Tā vietā, lai pārbaudītu laboratorijas ieraksta esamību, mēs samainījāmies kanoniskajā_materiālā, deleģējām zvanu un likām objektam atbildēt ar to pašu metodi.

Šīs bija visnoderīgākās stratēģijas atkarību sadalīšanai un jaunu objektu apmaiņai visā mūsu Rails monolītā. Pēc simtu definīciju un zvanu vietņu pārskatīšanas mēs tos aizstājām vai pārrakstījām pa vienam. Tas ir nogurdinošs process, ko es nevienam nevēlu, bet tas galu galā bija ārkārtīgi noderīgs, lai padarītu mūsu kodu bāzi saprotamāku un noņemtu veco kodu, kurš sēdēja, lai neko nedarītu. Lai nonāktu līdz beigām, bija vajadzīgas vairākas neapmierinošas un matu vilkšanas nedēļas, bet pēc tam, kad bija aizstāta lielākā daļa atsauču, mēs sākām veikt manuālu pārbaudi.

Pārbaude un manuāla pārbaude

Tā kā izmaiņas ietekmēja funkcijas visā kodeksa bāzē, no kurām dažas netika pārbaudītas, bija grūti QA ar pārliecību, bet mēs darījām visu iespējamo. Mēs veicām manuālu pārbaudi mūsu QA serverī, kurā tika konstatēts daudz kļūdu un malu gadījumu. Un tad mēs gājām uz priekšu un pa kritiskāku ceļu uzrakstījām jaunus testus.

Izlaist, sākt dzīvot un sakopt

Pēc QA nokārtošanas mēs uzvilkt mūsu funkcijas karogu un ļaut sistēmai nokārtot. Pēc tam, kad bijām pārliecinājušies, ka tā ir stabila, no kodu bāzes mēs noņēmām funkciju karodziņus un vecos koda ceļus. Diemžēl tas bija grūtāk, nekā gaidīts, jo tas radīja nepieciešamību pārrakstīt lielu daļu testa komplekta, galvenokārt rūpnīcas, kas netieši balstījās uz satura modeli. Retrospektīvi, ko mēs būtu varējuši izdarīt, bija reakcijas laikā uzrakstīt divus testu komplektus - vienu pašreizējam kodam un otru kodam aiz objekta karoga.

Kā pēdējais solis, kas vēl jāveic, mums vajadzētu dublēt datus un nometiet neizmantotās tabulas.

Un tas, draugi, ir viens no veidiem, kā atbrīvoties no viena galda mantojuma izplešanās jūsu Rails monolītā vienībā. Varbūt arī šis gadījuma izpēte jums palīdzēs.

Vai jums ir citi veidi, kā novērst STI vai reaģēt? Mums ir interesanti uzzināt. Paziņojiet mums komentāros.

Mēs arī īrējam! Pievienojieties mūsu komandai. Es esam forši, es apsolu.

Resursi un papildu lasīšana

  • Rails vada mantojumu
  • Kā un kad izmantot vienotā tabulas mantojumu sliedēs Eugene Wang (Flatiron Grad!)
  • Lietotnes Rails refaktorēšana no viena galda mantojuma
  • Viena tabulas mantojuma un polimorfās asociācijas sliedēs
  • Viena galda mantojums, izmantojot sliedes 5.02

Lai uzzinātu vairāk par Flatiron skolu, apmeklējiet vietni, sekojiet mums Facebook un Twitter un apmeklējiet mūs gaidāmajos pasākumos netālu no jums.

Flatiron School ir lepns WeWork ģimenes loceklis. Iepazīstieties ar mūsu māsu tehnoloģiju emuāriem WeWork Technology and Making Meetup.