Sannleikurinn fyrst, eða hvers vegna þarf að hanna kerfi út frá hönnun gagnagrunnsins

Hæ Habr!

Við höldum áfram að rannsaka efnið Java и Vor, þar á meðal á gagnagrunnsstigi. Í dag bjóðum við þér að lesa um hvers vegna, þegar stór forrit eru hönnuð, er það gagnagrunnsuppbyggingin, en ekki Java kóðinn, sem ætti að skipta sköpum, hvernig það er gert og hvaða undantekningar eru frá þessari reglu.

Í þessari frekar síðbúnu grein mun ég útskýra hvers vegna ég tel að í næstum öllum tilfellum ætti gagnalíkanið í forriti að vera hannað „úr gagnagrunninum“ frekar en „úr getu Java“ (eða hvaða tungumáli sem þú ert að nota) vinna með). Með því að taka seinni aðferðina ertu að setja þig upp fyrir langa leið sársauka og þjáningar þegar verkefnið þitt byrjar að vaxa.

Greinin var skrifuð út frá ein spurning, gefið á Stack Overflow.

Áhugaverðar umræður um reddit á köflum /r/java и /r/forritun.

Kóða kynslóð

Það kom mér á óvart að það er svo lítill hluti notenda sem, eftir að hafa kynnst jOOQ, er hneykslaður yfir þeirri staðreynd að jOOQ treystir alvarlega á frumkóðagerð til að starfa. Enginn er að hindra þig í að nota jOOQ eins og þér sýnist, eða neyða þig til að nota kóðagerð. En sjálfgefin (eins og lýst er í handbókinni) leiðin til að vinna með jOOQ er sú að þú byrjar með (gamla) gagnagrunnsskemu, gerir það öfugt með því að nota jOOQ kóðann til að fá þannig flokka sem tákna töflurnar þínar og skrifar svo tegund -öruggar fyrirspurnir í þessar töflur:

	for (Record2<String, String> record : DSL.using(configuration)
//   ^^^^^^^^^^^^^^^^^^^^^^^ Информация о типах выведена на 
//   основании сгенерированного кода, на который ссылается приведенное
// ниже условие SELECT 
 
       .select(ACTOR.FIRST_NAME, ACTOR.LAST_NAME)
//           vvvvv ^^^^^^^^^^^^  ^^^^^^^^^^^^^^^ сгенерированные имена
       .from(ACTOR)
       .orderBy(1, 2)) {
    // ...
}

Kóðinn er myndaður annað hvort handvirkt utan samsetningar eða handvirkt á hverri samsetningu. Til dæmis getur slík endurnýjun fylgt strax á eftir Flyway gagnagrunnsflutningur, sem einnig er hægt að gera handvirkt eða sjálfvirkt.

Myndun frumkóða

Það eru ýmsar heimspeki, kostir og gallar tengdir þessum aðferðum við kóðagerð - handvirkt og sjálfvirkt - sem ég ætla ekki að fjalla ítarlega um í þessari grein. En almennt er tilgangurinn með myndaða kóðanum að hann gerir okkur kleift að endurskapa í Java þann „sannleika“ sem við tökum sem sjálfsögðum hlut, annað hvort innan kerfisins okkar eða utan þess. Í vissum skilningi er þetta það sem þýðendur gera þegar þeir búa til bætikóða, vélkóða eða einhvers konar frumkóða - við fáum framsetningu á "sannleikanum" okkar á öðru tungumáli, óháð sérstökum ástæðum.

Það eru margir slíkir kóðaraflar. Til dæmis, XJC getur búið til Java kóða byggt á XSD eða WSDL skrám. Meginreglan er alltaf sú sama:

  • Það er einhver sannleikur (innri eða ytri) - til dæmis forskrift, gagnalíkan osfrv.
  • Við þurfum staðbundna framsetningu á þessum sannleika á forritunarmálinu okkar.

Þar að auki er næstum alltaf ráðlegt að búa til slíka framsetningu til að forðast offramboð.

Tegundarveitendur og athugasemdavinnsla

Athugið: Önnur, nútímalegri og sértækari aðferð til að búa til kóða fyrir jOOQ er að nota tegundaveitur, eins og þau eru útfærð í F#. Í þessu tilviki er kóðinn búinn til af þýðandanum, í raun á samantektarstigi. Í grundvallaratriðum er slíkur kóði ekki til í frumformi. Java hefur svipuð, þó ekki eins glæsileg, verkfæri - skýringargjörva, til dæmis, Lombok.

Að vissu leyti gerast hér sömu hlutir og í fyrra tilvikinu, að undanskildum:

  • Þú sérð ekki kóðann sem myndast (kannski virðist þetta ástand vera minna fráhrindandi fyrir einhvern?)
  • Þú verður að tryggja að hægt sé að útvega tegundir, það er að "sanna" verður alltaf að vera tiltæk. Þetta er auðvelt þegar um Lombok er að ræða, sem segir „sannleika“. Það er aðeins flóknara með gagnagrunnslíkön sem eru háð stöðugri lifandi tengingu.

Hvað er vandamálið með kóðagerð?

Til viðbótar við hina erfiðu spurningu um hvernig sé best að keyra kóðagerð - handvirkt eða sjálfvirkt, verðum við líka að nefna að það er fólk sem telur að kóðagerð sé alls ekki þörf. Rökstuðningurinn fyrir þessu sjónarmiði, sem ég rakst oftast á, er sú að þá er erfitt að koma upp byggingarleiðslu. Já, það er mjög erfitt. Aukinn innviðakostnaður kemur til. Ef þú ert rétt að byrja með tiltekna vöru (hvort sem það er jOOQ, eða JAXB, eða Hibernate, osfrv.), tekur það tíma að setja upp framleiðsluumhverfi sem þú vilt frekar eyða í að læra API sjálft svo þú getir unnið verðmæti úr því .

Ef kostnaðurinn sem tengist því að skilja uppbyggingu rafallsins er of hár, þá gerði API-ið reyndar lélegt starf varðandi notagildi kóðarafallsins (og síðar kemur í ljós að aðlögun notenda í honum er líka erfið). Nothæfi ætti að vera í hæsta forgangi fyrir slíkt API. En þetta er bara ein rök gegn kóðagerð. Annars er það algjörlega handvirkt að skrifa staðbundna framsetningu á innri eða ytri sannleika.

Margir munu segja að þeir hafi ekki tíma til að gera allt þetta. Þeir eru að renna út af fresti fyrir frábær vöru sína. Einhvern tíma munum við redda samsetningarfæriböndunum, við höfum tíma. Ég mun svara þeim:

Sannleikurinn fyrst, eða hvers vegna þarf að hanna kerfi út frá hönnun gagnagrunnsins
Original, Alan O'Rourke, áhorfendastakk

En í Hibernate/JPA er svo auðvelt að skrifa Java kóða.

Í alvöru. Fyrir Hibernate og notendur þess er þetta bæði blessun og bölvun. Í Hibernate geturðu einfaldlega skrifað nokkrar einingar, eins og þetta:

	@Entity
class Book {
  @Id
  int id;
  String title;
}

Og næstum allt er tilbúið. Nú er það undir Hibernate komið að búa til flóknar „upplýsingar“ um hvernig nákvæmlega þessi eining verður skilgreind í DDL SQL „mállýskunnar“ þíns:

	CREATE TABLE book (
  id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
  title VARCHAR(50),
 
  CONSTRAINT pk_book PRIMARY KEY (id)
);
 
CREATE INDEX i_book_title ON book (title);

... og byrjaðu að keyra forritið. Virkilega flott tækifæri til að byrja fljótt og prófa mismunandi hluti.

Hins vegar, vinsamlegast leyfðu mér. Ég var að ljúga.

  • Mun Hibernate framfylgja skilgreiningunni á þessum nafngreinda aðallykil?
  • Mun Hibernate búa til vísitölu í TITLE? — Ég veit fyrir víst að við munum þurfa þess.
  • Mun Hibernate nákvæmlega gera þennan lykil auðkenndan í auðkennislýsingunni?

Örugglega ekki. Ef þú ert að þróa verkefnið þitt frá grunni er alltaf þægilegt að henda gamla gagnagrunninum og búa til nýjan um leið og þú bætir við nauðsynlegum athugasemdum. Þannig mun bókaeiningin að lokum taka á sig form:

	@Entity
@Table(name = "book", indexes = {
  @Index(name = "i_book_title", columnList = "title")
})
class Book {
  @Id
  @GeneratedValue(strategy = IDENTITY)
  int id;
  String title;
}

Flott. Endurnýja. Aftur, í þessu tilfelli verður það mjög auðvelt í upphafi.

En þú verður að borga fyrir það seinna

Fyrr eða síðar verður þú að fara í framleiðslu. Það er þegar þetta líkan mun hætta að virka. Vegna þess að:

Í framleiðslu verður ekki lengur hægt, ef þörf krefur, að farga gamla gagnagrunninum og byrja frá grunni. Gagnagrunnurinn þinn verður arfur.

Héðan í frá og að eilífu verður þú að skrifa DDL flutningsforskriftir, til dæmis með því að nota Flyway. Hvað verður um aðila þína í þessu tilfelli? Þú getur annað hvort aðlagað þau handvirkt (og þannig tvöfaldað vinnuálag þitt), eða þú getur sagt Hibernate að endurskapa þau fyrir þig (hversu líklegt er að þeir sem myndast á þennan hátt standist væntingar þínar?) Hvort heldur sem er, þú tapar.

Svo þegar þú ert kominn í framleiðslu þarftu heita plástra. Og það þarf að koma þeim í framleiðslu mjög fljótt. Þar sem þú undirbjóst ekki og skipulagðir ekki hnökralausa leiðslu fyrir flutninga þína fyrir framleiðslu, þá lagaðir þú allt í vil. Og þá hefurðu ekki lengur tíma til að gera allt rétt. Og þú gagnrýnir Hibernate, því það er alltaf einhverjum öðrum að kenna, bara ekki þér...

Þess í stað hefði mátt gera allt öðruvísi frá upphafi. Settu til dæmis kringlótt hjól á reiðhjól.

Gagnagrunnur fyrst

Hinn raunverulegi „sannleikur“ í gagnagrunnsskemanu þínu og „fullveldi“ yfir því liggur innan gagnagrunnsins. Skemanið er aðeins skilgreint í gagnagrunninum sjálfum og hvergi annars staðar, og hver viðskiptavinur hefur afrit af þessu skema, svo það er fullkomlega skynsamlegt að framfylgja samræmi við skemað og heilleika þess, gera það rétt í gagnagrunninum - þar sem upplýsingarnar eru geymd.
Þetta er gömul speki, jafnvel þreytt. Aðallyklar og einstakir lyklar eru góðir. Erlendir lyklar eru góðir. Það er gott að athuga takmarkanir. Fullyrðingar - Fínt.

Þar að auki er það ekki allt. Til dæmis, með því að nota Oracle, myndirðu líklega vilja tilgreina:

  • Í hvaða borðrými er borðið þitt?
  • Hvert er PCTFREE gildi þess?
  • Hver er skyndiminnisstærðin í röðinni þinni (á bak við auðkennið)

Þetta er kannski ekki mikilvægt í litlum kerfum, en þú þarft ekki að bíða þangað til þú ferð yfir í stóra gagnasviðið - þú getur byrjað að njóta góðs af geymsluhagræðingum frá söluaðilum eins og þær sem nefnd eru hér að ofan miklu fyrr. Ekkert af þeim ORM sem ég hef séð (þar á meðal jOOQ) veitir aðgang að öllu settinu af DDL valkostum sem þú gætir viljað nota í gagnagrunninum þínum. ORMs bjóða upp á nokkur verkfæri sem hjálpa þér að skrifa DDL.

En í lok dags er vel hönnuð hringrás handskrifuð í DDL. Sérhver myndaður DDL er aðeins nálgun á því.

Hvað með viðskiptavinalíkanið?

Eins og getið er hér að ofan, á biðlaranum þarftu afrit af gagnagrunnsskemanu þínu, viðskiptavinasýninni. Óþarfur að minnast á að þetta viðskiptavinasýn verður að vera í takt við raunverulegt líkan. Hver er besta leiðin til að ná þessu? Notkun kóðarafalls.

Allir gagnagrunnar veita metaupplýsingar sínar í gegnum SQL. Hér er hvernig á að fá allar töflurnar úr gagnagrunninum þínum á mismunandi SQL mállýskum:

	-- H2, HSQLDB, MySQL, PostgreSQL, SQL Server
SELECT table_schema, table_name
FROM information_schema.tables
 
-- DB2
SELECT tabschema, tabname
FROM syscat.tables
 
-- Oracle
SELECT owner, table_name
FROM all_tables
 
-- SQLite
SELECT name
FROM sqlite_master
 
-- Teradata
SELECT databasename, tablename
FROM dbc.tables

Þessar fyrirspurnir (eða svipaðar, eftir því hvort þú þarft líka að taka tillit til skoðana, efnislegra skoðana, aðgerða sem eru metin í töflum) eru einnig framkvæmdar með því að kalla DatabaseMetaData.getTables() frá JDBC, eða með því að nota jOOQ meta-eininguna.

Út frá niðurstöðum slíkra fyrirspurna er tiltölulega auðvelt að búa til hvers kyns framsetningu viðskiptavinar á gagnagrunnslíkaninu þínu, óháð því hvaða tækni þú notar á viðskiptavininn.

  • Ef þú ert að nota JDBC eða Spring geturðu búið til sett af strengjaföstum
  • Ef þú notar JPA geturðu búið til einingarnar sjálfar
  • Ef þú notar jOOQ geturðu búið til jOOQ meta-líkanið

Það fer eftir því hversu mikla virkni er í boði hjá forritaskilum viðskiptavinarins (td jOOQ eða JPA), getur metalíkanið sem er búið til verið mjög ríkt og fullkomið. Tökum sem dæmi möguleikann á óbeinum tengingum, kynnt í jOOQ 3.11, sem byggir á mynduðum metaupplýsingum um erlendulykiltengslin sem eru á milli borðanna þinna.

Nú mun öll gagnagrunnsaukning sjálfkrafa uppfæra biðlarakóðann. Ímyndaðu þér til dæmis:

ALTER TABLE book RENAME COLUMN title TO book_title;

Myndir þú virkilega vilja vinna þetta starf tvisvar? Í engu tilviki. Einfaldlega skuldbindu DDL, keyrðu það í gegnum byggingarleiðsluna þína og fáðu uppfærða eininguna:

@Entity
@Table(name = "book", indexes = {
 
  // Вы об этом задумывались?
  @Index(name = "i_book_title", columnList = "book_title")
})
class Book {
  @Id
  @GeneratedValue(strategy = IDENTITY)
  int id;
 
  @Column("book_title")
  String bookTitle;
}

Eða uppfærða jOOQ flokkinn. Flestar DDL breytingar hafa einnig áhrif á merkingarfræði, ekki bara setningafræði. Þess vegna getur verið gagnlegt að skoða samantekna kóðann til að sjá hvaða kóða mun (eða gæti) orðið fyrir áhrifum af aukningu gagnagrunnsins.

Eini sannleikurinn

Óháð því hvaða tækni þú notar, þá er alltaf eitt líkan sem er eina uppspretta sannleika fyrir eitthvert undirkerfi - eða, að minnsta kosti, við ættum að leitast við þetta og forðast slíkt fyrirtækisrugl, þar sem "sannleikurinn" er alls staðar og hvergi í einu . Allt gæti verið miklu einfaldara. Ef þú ert bara að skiptast á XML skrám með einhverju öðru kerfi, notaðu bara XSD. Skoðaðu INFORMATION_SCHEMA meta-líkanið frá jOOQ í XML formi:
https://www.jooq.org/xsd/jooq-meta-3.10.0.xsd

  • XSD er vel skilið
  • XSD táknar XML efni mjög vel og leyfir staðfestingu á öllum tungumálum viðskiptavinarins
  • XSD er vel útgáfa og hefur háþróaða afturábak eindrægni
  • XSD er hægt að þýða yfir í Java kóða með XJC

Síðasta atriðið er mikilvægt. Þegar við erum í samskiptum við utanaðkomandi kerfi með XML skilaboðum viljum við vera viss um að skilaboðin okkar séu gild. Þetta er mjög auðvelt að ná með því að nota JAXB, XJC og XSD. Það væri hreint brjálæði að halda að með „Java first“ hönnunarnálgun þar sem við gerum skilaboðin okkar sem Java hluti, að þau gætu einhvern veginn verið samfellt kortlögð á XML og send í annað kerfi til neyslu. XML myndað með þessum hætti væri af mjög lélegum gæðum, óskráð og erfitt að þróa. Ef það væri þjónustustigssamningur (SLA) fyrir slíkt viðmót myndum við klúðra því strax.

Heiðarlega, þetta er það sem gerist alltaf með JSON API, en það er önnur saga, ég mun rífast næst...

Gagnagrunnar: þeir eru sami hluturinn

Þegar þú vinnur með gagnagrunna gerirðu þér grein fyrir því að þeir eru allir í grundvallaratriðum svipaðir. Grunnurinn á gögnin sín og verður að stjórna kerfinu. Allar breytingar sem gerðar eru á stefinu verða að vera útfærðar beint í DDL svo að eina sannleiksuppspretta sé uppfærð.

Þegar frumuppfærsla hefur átt sér stað verða allir viðskiptavinir einnig að uppfæra afrit sín af líkaninu. Suma viðskiptavini er hægt að skrifa í Java með því að nota jOOQ og Hibernate eða JDBC (eða bæði). Aðrir viðskiptavinir geta verið skrifaðir í Perl (við óskum þeim bara góðs gengis), á meðan aðrir geta verið skrifaðir í C#. Það skiptir ekki máli. Aðallíkanið er í gagnagrunninum. Líkön sem eru búin til með ORM eru venjulega af lélegum gæðum, illa skjalfest og erfitt að þróa.

Svo ekki gera mistök. Ekki gera mistök alveg frá upphafi. Vinna úr gagnagrunninum. Búðu til dreifingarleiðslu sem hægt er að gera sjálfvirkan. Virkjaðu kóðarafla til að gera það auðvelt að afrita gagnagrunnslíkanið þitt og henda því á viðskiptavini. Og hættu að hafa áhyggjur af kóðaframleiðendum. Þau eru góð. Með þeim muntu verða afkastameiri. Þú þarft bara að eyða smá tíma í að setja þau upp alveg frá upphafi - og þá bíða þín margra ára aukin framleiðni, sem mun mynda sögu verkefnisins þíns.

Ekki þakka mér enn, seinna.

skýringar

Til að vera skýr: Þessi grein mælir á engan hátt fyrir því að þú þurfir að beygja allt kerfið (þ. Það sem ég er að segja í þessari grein er að biðlarakóði sem hefur samskipti við gagnagrunninn ætti að starfa á grundvelli gagnagrunnslíkans, þannig að hann sjálfur endurskapi ekki gagnagrunnslíkanið í "fyrsta flokks" stöðu. Þessi rökfræði er venjulega staðsett á gagnaaðgangslaginu á viðskiptavininum þínum.

Í tveggja þrepa arkitektúrum, sem enn eru varðveittir á sumum stöðum, getur slíkt kerfislíkan verið hið eina mögulega. Hins vegar, í flestum kerfum, virðist mér gagnaaðgangslagið vera "undirkerfi" sem umlykur gagnagrunnslíkanið.

Undantekningar

Það eru undantekningar á hverri reglu og ég hef þegar sagt að gagnagrunns-fyrsta og frumkóðamyndunaraðferðin getur stundum verið óviðeigandi. Hér eru nokkrar slíkar undantekningar (það eru líklega aðrar):

  • Þegar stefið er óþekkt og þarf að uppgötva það. Til dæmis, þú ert að veita tól sem hjálpar notendum að vafra um hvaða skýringarmynd sem er. Úff. Það er engin kóðagerð hér. En samt kemur gagnagrunnurinn fyrst.
  • Þegar hringrás verður að myndast á flugu til að leysa eitthvert vandamál. Þetta dæmi virðist vera örlítið ímynduð útgáfa af mynstrinu entity eigind gildi, þ.e.a.s. þú ert í raun ekki með skýrt skilgreint kerfi. Í þessu tilfelli geturðu oft ekki einu sinni verið viss um að RDBMS henti þér.

Undantekningar eru í eðli sínu óvenjulegar. Í flestum tilfellum sem fela í sér notkun á RDBMS er stefið þekkt fyrirfram, það er innan RDBMS og er eina uppspretta „sannleikans“ og allir viðskiptavinir verða að eignast afrit sem eru fengin úr því. Helst þarftu að nota kóðarafall.

Heimild: www.habr.com

Bæta við athugasemd