{"id":79465,"date":"2020-04-27T07:42:20","date_gmt":"2020-04-27T05:42:20","guid":{"rendered":"https:\/\/prohoster.info\/blog\/administrirovanie\/istina-prezhde-vsego-ili-pochemu-sistemu-nuzhno-proektirovat-ishodya-iz-ustrojstva-bazy-dannyh"},"modified":"2020-04-27T07:42:20","modified_gmt":"2020-04-27T05:42:20","slug":"istina-prezhde-vsego-ili-pochemu-sistemu-nuzhno-proektirovat-ishodya-iz-ustrojstva-bazy-dannyh","status":"publish","type":"post","link":"https:\/\/prohoster.info\/da\/blog\/administrirovanie\/istina-prezhde-vsego-ili-pochemu-sistemu-nuzhno-proektirovat-ishodya-iz-ustrojstva-bazy-dannyh","title":{"rendered":"Sandheden f\u00f8rst, eller hvorfor systemet skal designes med udgangspunkt i databasedesign","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p>Hello, Habr!<\/p>\n<p>Vi forts\u00e6tter med at udforske emnet <noindex><a rel=\"nofollow\" href=\"https:\/\/www.piter.com\/collection\/all\/product\/sovremennyy-yazyk-java-lyambda-vyrazheniya-potoki-i-funktsionalnoe-programmirovanie\">Java<\/a><\/noindex> og <noindex><a rel=\"nofollow\" href=\"https:\/\/storage.piter.com\/upload\/contents\/978544611587\/978544611587_X.pdf\">Spring<\/a><\/noindex>, herunder p\u00e5 database-niveau. I dag tilbyder vi at l\u00e6se om, hvorfor strukturen af databasen, snarere end Java-koden, b\u00f8r have afg\u00f8rende betydning, n\u00e5r man designer store applikationer, hvordan dette g\u00f8res, og hvilke undtagelser der eksisterer til denne regel.<br \/>\n<noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex> <br \/>\nI denne ret forsinkede artikel vil jeg forklare, hvorfor jeg mener, at data-modellen i en applikation i n\u00e6sten alle tilf\u00e6lde skal designes \"ud fra databasen\", snarere end \"ud fra Java\u2019s kapaciteter\" (eller et andet klientsprog, du arbejder med). Ved at v\u00e6lge den anden tilgang tr\u00e6der du ind p\u00e5 en lang vej fyldt med smerte og lidelse, s\u00e5 snart dit projekt begynder at vokse.<\/p>\n<p>Artiklen er skrevet ud fra inspiration fra <noindex><a rel=\"nofollow\" href=\"https:\/\/stackoverflow.com\/questions\/50706556\/kotlin-with-jooq-and-write-table-models-manually-without-code-generation\">et sp\u00f8rgsm\u00e5l<\/a><\/noindex>, stillet p\u00e5 Stack Overflow.<\/p>\n<p>Interessante diskussioner p\u00e5 reddit i sektionerne <noindex><a rel=\"nofollow\" href=\"https:\/\/www.reddit.com\/r\/java\/comments\/8p0uj3\/truth_first_or_why_you_should_mostly_implement\/\">\/r\/java<\/a><\/noindex> og <noindex><a rel=\"nofollow\" href=\"https:\/\/www.reddit.com\/r\/programming\/comments\/8p0qle\/truth_first_or_why_you_should_mostly_implement\/\">\/r\/programming<\/a><\/noindex>.<\/p>\n<h4>Kodegenerering<\/h4>\n<p>\nHvor overrasket blev jeg ikke over, at der er en s\u00e5 lille gruppe brugere, der, efter at have l\u00e6rt jOOQ at kende, er opr\u00f8rte over det faktum, at n\u00e5r man arbejder med jOOQ, l\u00e6gges der stor v\u00e6gt p\u00e5 genereringen af kildekode. Ingen forhindrer dig i at bruge jOOQ, som du \u00f8nsker, og ingen tvinger dig til at bruge kodegenerering. Men som standard (som beskrevet i vejledningen) fungerer arbejdet med jOOQ s\u00e5ledes: du starter med en (arvet) database-skitse, udf\u00f8rer dens reverse engineering ved hj\u00e6lp af jOOQs kodegenerator for dermed at f\u00e5 et s\u00e6t klasser, der repr\u00e6senterer dine tabeller, og derefter skriver du typesikre foresp\u00f8rgsler til disse tabeller:<\/p>\n<pre><code class=\"java\">\tfor (Record2 record : DSL.using(configuration)\n\/\/   ^^^^^^^^^^^^^^^^^^^^^^^ Typeoplysningerne er deduceret fra\n\/\/   den genererede kode, som der henvises til i den\n\/\/   efterf\u00f8lgende SELECT-betingelse\n\n       .select(ACTOR.FIRST_NAME, ACTOR.LAST_NAME)\n\/\/           vvvvv ^^^^^^^^^^^^  ^^^^^^^^^^^^^^^ genererede navne\n       .from(ACTOR)\n       .orderBy(1, 2)) {\n    \/\/ ...\n}<\/code><\/pre>\n<p>\nKoden genereres enten manuelt uden for build-processen, eller manuelt ved hver build. For eksempel kan en s\u00e5dan regenerering finde sted umiddelbart efter <noindex><a rel=\"nofollow\" href=\"https:\/\/blog.jooq.org\/2014\/06\/25\/flyway-and-jooq-for-unbeatable-sql-development-productivity\/\">Flyway-database-migrationen, som ogs\u00e5 kan udf\u00f8res manuelt eller automatisk.<\/a><\/noindex>.<\/p>\n<h4>Generering af kildekode<\/h4>\n<p>\nMed s\u00e5danne tilgange til kodegenerering \u2013 b\u00e5de manuelle og automatiserede \u2013 er der forskellige filosofier, fordele og ulemper, som jeg ikke vil g\u00e5 n\u00e6rmere ind i i denne artikel. Men alt i alt handler genereret kode om, at den g\u00f8r det muligt at reproducere den 'sandhed', vi accepterer som given, enten inden for vores system eller udenfor. P\u00e5 en m\u00e5de g\u00f8r compilere det samme ved at generere bytekode, maskinkode eller en anden form for kode baseret p\u00e5 kildetekster \u2013 vi f\u00e5r vores 'sandhed' pr\u00e6senteret p\u00e5 et andet sprog, uanset de specifikke \u00e5rsager. <\/p>\n<p>Der findes mange s\u00e5danne kodegeneratorer. For eksempel, <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.oracle.com\/javase\/8\/docs\/technotes\/tools\/unix\/xjc.html\">XJC kan generere Java-kode baseret p\u00e5 XSD- eller WSDL-filer<\/a><\/noindex>. Princippet er altid det samme:<\/p>\n<ul>\n<li>Der eksisterer en vis sandhed (intern eller ekstern) \u2013 for eksempel en specifikation, datamodel osv.<\/li>\n<li>Vi har brug for en lokal repr\u00e6sentation af denne sandhed p\u00e5 vores programmeringssprog.<\/li>\n<\/ul>\n<p>\nOg det er n\u00e6sten altid fornuftigt at generere s\u00e5dan en repr\u00e6sentation \u2013 for at undg\u00e5 overdreven kompleksitet.<\/p>\n<h4>Typeudbydere og behandling af annotationer<\/h4>\n<p>\nTil note: en anden, mere moderne og specifik tilgang til kodegenerering for jOOQ vedr\u00f8rer brugen af typeudbydere, <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/fsharp\/tutorials\/type-providers\/\">i den form, de er implementeret i F#<\/a><\/noindex>. I s\u00e5 fald genereres koden af compileren, faktisk p\u00e5 kompileringstidspunktet. I form af kildetekster eksisterer denne kode grundl\u00e6ggende ikke. I Java findes der lignende, omend mindre elegante v\u00e6rkt\u00f8jer \u2013 det er annotationsprocessorer, for eksempel, <noindex><a rel=\"nofollow\" href=\"https:\/\/projectlombok.org\">Lombok<\/a><\/noindex>.<\/p>\n<p>I en vis forstand sker der de samme ting her som i det f\u00f8rste tilf\u00e6lde, undtagen for:<\/p>\n<ul>\n<li>Du ser ikke den genererede kode (m\u00e5ske synes denne situation ikke s\u00e5 frast\u00f8dende for nogen?)<\/li>\n<li>Du skal sikre, at typer kan stilles til r\u00e5dighed, det vil sige, 'sandheden' skal altid v\u00e6re tilg\u00e6ngelig. Det er let i tilf\u00e6lde af Lombok, der annoterer 'sandheden'. Lidt sv\u00e6rere med databaser, hvis funktion afh\u00e6nger af et konstant tilg\u00e6ngeligt aktivt forbindelses.<\/li>\n<\/ul>\n<p><\/p>\n<h4>Hvad er problemet med kodegenerering?<\/h4>\n<p>\nUd over det snedige sp\u00f8rgsm\u00e5l om, hvordan man bedst starter koden generering \u2013 manuelt eller automatisk, m\u00e5 man ogs\u00e5 n\u00e6vne, at der er folk, der mener, at koden generering slet ikke er n\u00f8dvendig. Begrundelsen for dette synspunkt, som jeg oftest har set, er, at det s\u00e5 bliver sv\u00e6rt at konfigurere build-pipelinen. Ja, det er faktisk sv\u00e6rt. Der opst\u00e5r ekstra infrastrukturelle omkostninger. Hvis du er ny i arbejdet med et bestemt produkt (uanset om det er jOOQ, JAXB, Hibernate osv.), tager det tid at s\u00e6tte arbejdsomgivelserne op, som du helst ville bruge p\u00e5 at l\u00e6re API'et at kende, s\u00e5 du senere kan f\u00e5 v\u00e6rdi ud af det.<\/p>\n<p>Hvis omkostningerne til at forst\u00e5 generatorens indretning er for store \u2013 s\u00e5 har API'et virkelig ikke gjort et godt stykke arbejde med brugervenligheden af kodegeneratoren (og det viser sig senere, at brugerdefineringen i den ogs\u00e5 er vanskelig). Brugervenlighed b\u00f8r have h\u00f8jeste prioritet i ethvert s\u00e5dant API. Men det er kun \u00e9t argument imod kodegenerering. Ellers skal man helt klart skrive en lokal repr\u00e6sentation af indre eller ydre sandheder manuelt.<\/p>\n<p>Mange vil sige, at de ikke har tid til alt det. De har stramme deadlines for deres Super-Produkt. Senere kan vi tage os af build-pipelinerne. Jeg vil svare dem:<\/p>\n<p><img decoding=\"async\" alt=\"Sandheden f\u00f8rst, eller hvorfor systemet skal designes med udgangspunkt i databasedesign\" src=\"\/wp-content\/uploads\/2020\/04\/cd9aca1e0365fa5af28cfc72c201cb8c.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<noindex><a rel=\"nofollow\" href=\"https:\/\/www.flickr.com\/photos\/toddle_email_newsletters\/15596940251\/in\/photostream\/\">Original<\/a><\/noindex>, <noindex><a rel=\"nofollow\" href=\"http:\/\/audiencestack.com\/static\/blog.html\">Alan O'Rourke, Audience Stack<\/a><\/noindex> <\/p>\n<p><b>Men i Hibernate \/ JPA er det s\u00e5 let at skrive kode \"til Java\". <\/b><\/p>\n<p>Det er sandt. For Hibernate og dets brugere er det b\u00e5de en velsignelse og en forbandelse. I Hibernate kan man let skrive et par entiteter, s\u00e5dan her:<\/p>\n<pre><code class=\"java\">\t@Entity\nclass Book {\n  @Id\n  int id;\n  String title;\n}<\/code><\/pre>\n<p>\nOg n\u00e6sten alt er klar. Nu er Hibernates opgave \u2013 at generere de komplekse \"detaljer\", hvordan netop denne entitet vil blive defineret i DDL'en af din \"dialekt\" SQL:<\/p>\n<pre><code class=\"sql\">\tCREATE TABLE book (\n  id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY,\n  title VARCHAR(50),\n \n  CONSTRAINT pk_book PRIMARY KEY (id)\n);\n \nCREATE INDEX i_book_title ON book (title);<\/code><\/pre>\n<p>\n\u2026 og vi begynder at k\u00f8re applikationen. Det er en virkelig fantastisk mulighed for hurtigt at komme i gang og pr\u00f8ve forskellige ting.<\/p>\n<p>Men vent lidt. Jeg har ikke v\u00e6ret helt \u00e6rlig.<\/p>\n<ul>\n<li>Og vil Hibernate faktisk anvende definitionen af denne navngivne prim\u00e6re n\u00f8gle?<\/li>\n<li>Og vil Hibernate skabe en indeks i TITLE? \u2013 jeg ved bestemt, vi f\u00e5r brug for det. <\/li>\n<li>Og vil Hibernate helt sikkert g\u00f8re denne n\u00f8gle identificerende i Identity Specification?<\/li>\n<\/ul>\n<p>\nN\u00e6ppe. Hvis du udvikler dit projekt fra bunden, er det altid nemt bare at smide den gamle database v\u00e6k og generere en ny, s\u00e5 snart du har tilf\u00f8jet de n\u00f8dvendige annotationer. S\u00e5dan vil entiteten Book i sidste ende tage form: <\/p>\n<pre><code class=\"java\">\t@Entity\n@Table(name = \"book\", indexes = {\n  @Index(name = \"i_book_title\", columnList = \"title\")\n})\nclass Book {\n  @Id\n  @GeneratedValue(strategy = IDENTITY)\n  int id;\n  String title;\n}\n<\/code><\/pre>\n<p>\nFedt. Generer det p\u00e5 ny. Igen, i s\u00e5 fald vil det v\u00e6re meget nemt i starten.<\/p>\n<h4>Men senere vil du skulle betale for det.<\/h4>\n<p>\nTidspunktet vil komme, hvor du skal g\u00e5 i produktion. Netop da vil denne model stoppe med at fungere. Fordi: <\/p>\n<p>I produktionen vil du ikke l\u00e6ngere kunne smide den gamle database v\u00e6k og starte fra en ren tavle. Din database vil blive arvet.<\/p>\n<p>Fra nu af og for evigt vil du v\u00e6re n\u00f8dt til at skrive <noindex><a rel=\"nofollow\" href=\"https:\/\/blog.jooq.org\/2014\/06\/25\/flyway-and-jooq-for-unbeatable-sql-development-productivity\/\">migrationsskripter DDL, for eksempel ved hj\u00e6lp af Flyway.<\/a><\/noindex>. Hvad vil der i s\u00e5 fald ske med dine entiteter? Du kan enten tilpasse dem manuelt (og dermed fordoble dit arbejdsvolumen), eller du kan bede Hibernate om at generere dem for dig (hvordan st\u00e5r chancerne for, at det genererede vil leve op til dine forventninger?) Du vil i hvert fald tabe.<\/p>\n<p>S\u00e5 snart du g\u00e5r i produktion, vil du have brug for hotfixes. Og de skal hurtigt sendes i produktion. Da du ikke har forberedt dig og ikke har organiseret en glat konveyerisering af dine migrationer til produktion, vil du patch'e det hele h\u00e6ftigt. Og s\u00e5 vil du ikke have tid til at g\u00f8re det rigtigt. Og du beskylder Hibernate, eftersom enhver anden er skyld i det, bortset fra dig...<\/p>\n<p>I stedet kunne alt fra starten have v\u00e6ret gjort helt anderledes. For eksempel kunne du have sat runde hjul p\u00e5 cyklen.<\/p>\n<h4>F\u00f8rst databasen.<\/h4>\n<p>\nDen virkelige 'sandhed' i skemaet for din database og 'suver\u00e6nitet' over det ligger inde i databasen. Skemaet defineres kun i selve databasen og ingen andre steder, og hver af kunderne har en kopi af dette skema, s\u00e5 det giver fuldst\u00e6ndig mening at h\u00e5ndh\u00e6ve skemaets overholdelse og integritet direkte i databasen - der hvor informationen opbevares. <br \/>\nDet er en gammel og endda slidt visdom. Prim\u00e6re og unikke n\u00f8gler er gode. Udenlandske n\u00f8gler er gode. Kontrol af begr\u00e6nsninger er gode. <noindex><a rel=\"nofollow\" href=\"https:\/\/community.oracle.com\/ideas\/13028\">Udsagn<\/a><\/noindex> \u2013 er gode. <\/p>\n<p>Og det er ikke alt. For eksempel, n\u00e5r du bruger Oracle, \u00f8nsker du sandsynligvis at angive:<\/p>\n<ul>\n<li>I hvilket tablespace din tabel findes <\/li>\n<li>Hvad dens PCTFREE-v\u00e6rdi er <\/li>\n<li>Hvad st\u00f8rrelsen p\u00e5 cachen i din sekvens (efter ID) er<\/li>\n<\/ul>\n<p>\nM\u00e5ske er alt dette ikke vigtigt i sm\u00e5 systemer, men du beh\u00f8ver ikke vente p\u00e5 at overg\u00e5 til omr\u00e5det med \"big data\" \u2014 du kan begynde at drage fordel af de dataopbevaringsoptimeringer, som udbyderen tilbyder, meget tidligere, som n\u00e6vnt ovenfor. Ingen af de ORM'er, jeg har set (herunder jOOQ), giver adgang til det komplette s\u00e6t af DDL-muligheder, som du m\u00e5ske vil bruge i din database. ORM'er tilbyder nogle v\u00e6rkt\u00f8jer, der hj\u00e6lper med at skrive DDL.<\/p>\n<p>Men i sidste ende er en godt designet skema manuelt skrevet i DDL. Enhver genereret DDL er kun en tiln\u00e6rmelse af det.<\/p>\n<h4>Hvad med klientskemaet?<\/h4>\n<p>\nSom n\u00e6vnt ovenfor, har du brug for en kopi af skemaet for din database p\u00e5 klienten, en klientvisning. Un\u00f8dvendigt at n\u00e6vne, at denne klientvisning skal v\u00e6re synkroniseret med den faktiske model. Hvordan opn\u00e5r man dette bedst? Ved hj\u00e6lp af en kodegenerator.<\/p>\n<p>Alle databaser giver deres metainformation via SQL. Her er hvordan du f\u00e5r alle tabeller fra din database p\u00e5 forskellige SQL-dialekter:<\/p>\n<pre><code class=\"sql\">\t-- H2, HSQLDB, MySQL, PostgreSQL, SQL Server\nSELECT table_schema, table_name\nFROM information_schema.tables\n \n-- DB2\nSELECT tabschema, tabname\nFROM syscat.tables\n \n-- Oracle\nSELECT owner, table_name\nFROM all_tables\n \n-- SQLite\nSELECT name\nFROM sqlite_master\n \n-- Teradata\nSELECT databasename, tablename\nFROM dbc.tables\n<\/code><\/pre>\n<p>\nDisse foresp\u00f8rgsler (eller lignende, afh\u00e6ngigt af om visninger, materialiserede visninger eller tabular v\u00e6rdifunktioner ogs\u00e5 skal tages i betragtning) udf\u00f8res ogs\u00e5 ved at kalde <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.oracle.com\/javase\/10\/docs\/api\/java\/sql\/DatabaseMetaData.html#getTables(java.lang.String,java.lang.String,java.lang.String,java.lang.String%5B%5D)\"><code>DatabaseMetaData.getTables()<\/code><\/a><\/noindex> fra JDBC, eller ved hj\u00e6lp af jOOQ's meta-modul.<\/p>\n<p>Ud fra resultaterne af s\u00e5danne foresp\u00f8rgsler er det relativt nemt at generere en hvilken som helst klientvisning af din databases model, uanset hvilken teknologi du bruger p\u00e5 klienten.<\/p>\n<ul>\n<li>Hvis du bruger JDBC eller Spring, kan du oprette et s\u00e6t strengkonstanter <\/li>\n<li>Hvis du bruger JPA, kan du generere selve enhederne <\/li>\n<li>Hvis du bruger jOOQ, kan du generere jOOQ's meta-model<\/li>\n<\/ul>\n<p>\nAfh\u00e6ngigt af hvilke muligheder der tilbydes via dit klient-API (f.eks. jOOQ eller JPA), kan den genererede meta-model v\u00e6re virkelig rig og detaljeret. Lad os tage muligheden for implicitte joins som et eksempel, <noindex><a rel=\"nofollow\" href=\"https:\/\/blog.jooq.org\/2018\/02\/20\/type-safe-implicit-join-through-path-navigation-in-jooq-3-11\/\">som blev introduceret i jOOQ 3.11<\/a><\/noindex>, som bygger p\u00e5 den genererede metainformation om forholdet mellem de udenlandske n\u00f8gler, der g\u00e6lder mellem dine tabeller.<\/p>\n<p>Nu vil enhver \u00e6ndring i databasen automatisk f\u00f8re til en opdatering af klientkoden. Forestil dig for eksempel:<\/p>\n<pre><code class=\"sql\">ALTER TABLE book RENAME COLUMN title TO book_title;<\/code><\/pre>\n<p>\nVil du virkelig g\u00f8re dette arbejde to gange? Overhovedet ikke. Vi registrerer blot DDL, k\u00f8rer det gennem dit build-pipeline og f\u00e5r en opdateret enhed:<\/p>\n<pre><code class=\"java\">@Entity\n@Table(name = \"book\", indexes = {\n \n  \/\/ Har du t\u00e6nkt p\u00e5 dette?\n  @Index(name = \"i_book_title\", columnList = \"book_title\")\n})\nclass Book {\n  @Id\n  @GeneratedValue(strategy = IDENTITY)\n  int id;\n \n  @Column(\"book_title\")\n  String bookTitle;\n}<\/code><\/pre>\n<p>\nEller den opdaterede jOOQ-klasse. De fleste DDL-\u00e6ndringer afspejles ogs\u00e5 i semantikken, ikke kun i syntaksen. Derfor er det ofte nyttigt at se p\u00e5 den kompilerede kode for at finde ud af, hvilken kode der vil (eller kan) blive p\u00e5virket af \u00e6ndringen i din database.<\/p>\n<h4>Den eneste sandhed<\/h4>\n<p>\nUanset hvilken teknologi du bruger, er der altid \u00e9n model, der er den eneste sandhedskilde for et bestemt subsystem - eller i det mindste b\u00f8r vi str\u00e6be efter dette og undg\u00e5 den enterprise-forvirring, hvor \"sandheden\" er overalt og ingen steder. Det kan v\u00e6re meget simplere. Hvis du blot udveksler XML-filer med et andet system, s\u00e5 brug XSD. Se p\u00e5 INFORMATION_SCHEMA-meta-modellen fra jOOQ i XML-format:<br \/>\n<noindex><a rel=\"nofollow\" href=\"https:\/\/www.jooq.org\/xsd\/jooq-meta-3.10.0.xsd\">https:\/\/www.jooq.org\/xsd\/jooq-meta-3.10.0.xsd<\/a><\/noindex><\/p>\n<ul>\n<li>XSD er let at forst\u00e5<\/li>\n<li>XSD markerer indholdet i XML meget godt og tillader validering p\u00e5 alle klientsprog <\/li>\n<li>XSD er let at versionere og har god bagudkompatibilitet <\/li>\n<li>XSD kan overs\u00e6ttes til Java-kode ved hj\u00e6lp af XJC<\/li>\n<\/ul>\n<p>\nDen sidste punkt er vigtigt. N\u00e5r vi kommunikerer med et eksternt system ved hj\u00e6lp af XML-beskeder, vil vi v\u00e6re sikre p\u00e5, at vores beskeder er gyldige. Dette kan nemt opn\u00e5s ved hj\u00e6lp af JAXB, XJC og XSD. Det ville v\u00e6re helt vanvittigt at regne med, at n\u00e5r vi designer \"Java f\u00f8rst\", hvor vi laver vores beskeder som Java-objekter, kan de p\u00e5 nogen m\u00e5de blive klart kortlagt til XML og sendt til konsum i et andet system. XML genereret p\u00e5 denne m\u00e5de ville v\u00e6re af meget d\u00e5rlig kvalitet, d\u00e5rligt dokumenteret og sv\u00e6rt at udvikle. Hvis der var en service level agreement (SLA) for et s\u00e5dant interface, ville vi straks have \u00f8delagt det.<\/p>\n<p>\u00c6rligt talt, det er pr\u00e6cis det, der konstant sker med API'er p\u00e5 JSON, men det er en anden historie, n\u00e6ste gang vil jeg klage\u2026 <\/p>\n<h4>Databaser: det er det samme<\/h4>\n<p>\nN\u00e5r man arbejder med databaser, forst\u00e5r man, at de alle i princippet ligner hinanden. En database ejer sine data og skal styre skemaet. Enhver \u00e6ndring, der foretages i skemaet, skal implementeres direkte i DDL, s\u00e5 den eneste sandheds kilde opdateres.<\/p>\n<p>N\u00e5r kildeopdateringen er sket, skal alle klienter ogs\u00e5 opdatere deres kopi af modellen. Nogle klienter kan v\u00e6re skrevet i Java ved hj\u00e6lp af jOOQ og Hibernate eller JDBC (eller alle p\u00e5 \u00e9n gang). Andre klienter kan v\u00e6re skrevet i Perl (god forn\u00f8jelse med det), mens tredje kan v\u00e6re i C#. Det er ligegyldigt. Hovedmodellen er i databasen. Modeller genereret med ORM er typisk af d\u00e5rlig kvalitet, d\u00e5rligt dokumenteret og sv\u00e6re at udvikle.<\/p>\n<p>S\u00e5 lad v\u00e6re med at lave fejl. Lav ikke fejl fra starten. Arbejd ud fra databasen. Byg en deploymentspipeline, som kan automatiseres. Inkluder kodegeneratorer, s\u00e5 det er nemt at kopiere modellen fra din database og distribuere den til klienter. Og stop med at bekymre dig om kodegeneratorer. De er gode. Med dem bliver du mere produktiv. Man skal bare bruge lidt tid i starten p\u00e5 at konfigurere dem \u2013 og derefter venter mange \u00e5rs \u00f8get produktivitet, som vil udg\u00f8re historien om dit projekt.<\/p>\n<p>Lad v\u00e6re med at takke nu, senere.<\/p>\n<h4>Forklaring<\/h4>\n<p>\nFor clarity: This article in no way advocates for bending the entire system (i.e., subject area, business logic, etc.) to fit your database model. In this article, I\u2019m saying that the client-side code interacting with the database should operate based on the database model so that it doesn\u2019t replicate the database model as a \u2018first class\u2019 citizen. Such logic usually resides at the data access level on your client.<\/p>\n<p>In two-tier architectures, which still exist in some places, such a system model may be the only possible one. However, in most systems, the data access level seems to me to be a \u2018subsystem\u2019, encapsulating the database model.<\/p>\n<h4>Undtagelser<\/h4>\n<p>\nThere are exceptions to every rule, and I\u2019ve already mentioned that the approach with database primacy and code generation can sometimes be unsuitable. Here are a couple of such exceptions (there are likely more):<\/p>\n<ul>\n<li>When the schema is unknown and needs to be uncovered. For example, you are a tool provider helping users navigate any schema. Ugh. This is without code generation. But still \u2013 the database comes first.<\/li>\n<li>When the schema must be generated on the fly to solve some task. This example seems a bit like an extravagant version of the <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Entity%E2%80%93attribute%E2%80%93value_model\">entity attribute value<\/a><\/noindex>, meaning you really don\u2019t have a clearly defined schema. In this case, it\u2019s often even impossible to be sure that a relational database management system (RDBMS) will be suitable for you.<\/li>\n<\/ul>\n<p>\nExceptions are exceptional by nature. In most cases involving the use of an RDBMS, the schema is known in advance, it resides within the RDBMS and is the only source of \u2018truth\u2019, while all clients must acquire copies derived from it. Ideally, a code generator should be utilized in this scenario.<br \/>\n<br \/>Kilde: <a content=\"nofollow\" rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/piter\/blog\/498816\/\">habr.com<\/a> <\/p>","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"excerpt":{"rendered":"<p>\u041f\u0440\u0438\u0432\u0435\u0442, \u0425\u0430\u0431\u0440! \u041c\u044b \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0430\u0435\u043c \u0438\u0441\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u044c \u0442\u0435\u043c\u0443 Java \u0438 Spring, \u0432 \u0442\u043e\u043c \u0447\u0438\u0441\u043b\u0435, \u043d\u0430 \u0443\u0440\u043e\u0432\u043d\u0435 \u0431\u0430\u0437 \u0434\u0430\u043d\u043d\u044b\u0445. \u0421\u0435\u0433\u043e\u0434\u043d\u044f \u043f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u0435\u043c \u043f\u043e\u0447\u0438\u0442\u0430\u0442\u044c \u043e \u0442\u043e\u043c, \u043f\u043e\u0447\u0435\u043c\u0443 \u043f\u0440\u0438 \u043f\u0440\u043e\u0435\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0438 \u0431\u043e\u043b\u044c\u0448\u0438\u0445 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439 \u0438\u043c\u0435\u043d\u043d\u043e \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445, \u0430 \u043d\u0435 \u043a\u043e\u0434 Java, \u0434\u043e\u043b\u0436\u043d\u0430 \u0438\u043c\u0435\u0442\u044c \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u044e\u0449\u0435\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435, \u043a\u0430\u043a \u044d\u0442\u043e \u0434\u0435\u043b\u0430\u0435\u0442\u0441\u044f, \u0438 \u043a\u0430\u043a\u0438\u0435 \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u0435\u0441\u0442\u044c \u0438\u0437 \u044d\u0442\u043e\u0433\u043e \u043f\u0440\u0430\u0432\u0438\u043b\u0430. \u0412 \u044d\u0442\u043e\u0439 \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u0437\u0430\u043f\u043e\u0437\u0434\u0430\u043b\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u044f \u043e\u0431\u044a\u044f\u0441\u043d\u044e, [&hellip;]<\/p>\n","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"author":1,"featured_media":79466,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[688],"tags":[],"class_list":["post-79465","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-administrirovanie"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 5.0.3 - aioseo.com -->\n\t<meta name=\"description\" content=\"\u041f\u0440\u0438\u0432\u0435\u0442, \u0425\u0430\u0431\u0440! \u041c\u044b \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0430\u0435\u043c \u0438\u0441\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u044c \u0442\u0435\u043c\u0443\" \/>\n\t<meta name=\"robots\" content=\"max-image-preview:large\" \/>\n\t<meta name=\"author\" content=\"Yuri Gagarin\"\/>\n\t<link rel=\"canonical\" href=\"https:\/\/prohoster.info\/da\/blog\/administrirovanie\/istina-prezhde-vsego-ili-pochemu-sistemu-nuzhno-proektirovat-ishodya-iz-ustrojstva-bazy-dannyh\" \/>\n\t\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.3\" \/>\n\t\t<meta property=\"og:locale\" content=\"da_DK\" \/>\n\t\t<meta property=\"og:site_name\" content=\"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b\" \/>\n\t\t<meta property=\"og:type\" content=\"article\" \/>\n\t\t<meta property=\"og:title\" content=\"\ud83e\udd47\u0418\u0441\u0442\u0438\u043d\u0430 \u043f\u0440\u0435\u0436\u0434\u0435 \u0432\u0441\u0435\u0433\u043e, \u0438\u043b\u0438 \u043f\u043e\u0447\u0435\u043c\u0443 \u0441\u0438\u0441\u0442\u0435\u043c\u0443 \u043d\u0443\u0436\u043d\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c, \u0438\u0441\u0445\u043e\u0434\u044f \u0438\u0437 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445 | ProHoster\" \/>\n\t\t<meta property=\"og:description\" content=\"\u041f\u0440\u0438\u0432\u0435\u0442, \u0425\u0430\u0431\u0440! \u041c\u044b \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0430\u0435\u043c \u0438\u0441\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u044c \u0442\u0435\u043c\u0443\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/prohoster.info\/da\/blog\/administrirovanie\/istina-prezhde-vsego-ili-pochemu-sistemu-nuzhno-proektirovat-ishodya-iz-ustrojstva-bazy-dannyh\" \/>\n\t\t<meta property=\"og:image\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:secure_url\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:width\" content=\"350\" \/>\n\t\t<meta property=\"og:image:height\" content=\"350\" \/>\n\t\t<meta property=\"article:published_time\" content=\"2020-04-27T05:42:20+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2020-04-27T05:42:20+00:00\" \/>\n\t\t<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<meta property=\"article:author\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<!-- All in One SEO -->\n\n","aioseo_head_json":{"title":"\ud83e\udd47Truth comes first, or why a system should be designed based on the database arrangement | ProHoster","description":"Hello, Habr! We continue to explore the topic","canonical_url":"https:\/\/prohoster.info\/da\/blog\/administrirovanie\/istina-prezhde-vsego-ili-pochemu-sistemu-nuzhno-proektirovat-ishodya-iz-ustrojstva-bazy-dannyh","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"da_DK","og:site_name":"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b","og:type":"article","og:title":"\ud83e\udd47\u0418\u0441\u0442\u0438\u043d\u0430 \u043f\u0440\u0435\u0436\u0434\u0435 \u0432\u0441\u0435\u0433\u043e, \u0438\u043b\u0438 \u043f\u043e\u0447\u0435\u043c\u0443 \u0441\u0438\u0441\u0442\u0435\u043c\u0443 \u043d\u0443\u0436\u043d\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c, \u0438\u0441\u0445\u043e\u0434\u044f \u0438\u0437 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445 | ProHoster","og:description":"\u041f\u0440\u0438\u0432\u0435\u0442, \u0425\u0430\u0431\u0440! \u041c\u044b \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0430\u0435\u043c \u0438\u0441\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u044c \u0442\u0435\u043c\u0443","og:url":"https:\/\/prohoster.info\/da\/blog\/administrirovanie\/istina-prezhde-vsego-ili-pochemu-sistemu-nuzhno-proektirovat-ishodya-iz-ustrojstva-bazy-dannyh","og:image":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:secure_url":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:width":350,"og:image:height":350,"article:published_time":"2020-04-27T05:42:20+00:00","article:modified_time":"2020-04-27T05:42:20+00:00","article:publisher":"https:\/\/www.facebook.com\/prohoster","article:author":"https:\/\/www.facebook.com\/prohoster"},"aioseo_meta_data":{"post_id":"79465","title":null,"description":null,"keywords":null,"keyphrases":null,"primary_term":null,"canonical_url":null,"og_title":null,"og_description":null,"og_object_type":"default","og_image_type":"default","og_image_url":null,"og_image_width":null,"og_image_height":null,"og_image_custom_url":null,"og_image_custom_fields":null,"og_video":null,"og_custom_url":null,"og_article_section":null,"og_article_tags":null,"twitter_use_og":false,"twitter_card":"default","twitter_image_type":"default","twitter_image_url":null,"twitter_image_custom_url":null,"twitter_image_custom_fields":null,"twitter_title":null,"twitter_description":null,"schema":{"blockGraphs":[],"customGraphs":[],"default":{"data":{"Article":[],"Course":[],"Dataset":[],"FAQPage":[],"Movie":[],"Person":[],"Product":[],"ProductReview":[],"Car":[],"Recipe":[],"Service":[],"SoftwareApplication":[],"WebPage":[]},"graphName":"","isEnabled":true},"graphs":[]},"schema_type":null,"schema_type_options":null,"pillar_content":false,"robots_default":true,"robots_noindex":false,"robots_noarchive":false,"robots_nosnippet":false,"robots_nofollow":false,"robots_noimageindex":false,"robots_noodp":false,"robots_notranslate":false,"robots_max_snippet":null,"robots_max_videopreview":null,"robots_max_imagepreview":"large","priority":null,"frequency":null,"local_seo":null,"seo_analyzer_scan_date":null,"breadcrumb_settings":null,"limit_modified_date":false,"reviewed_by":null,"ai":null,"created":"2021-02-28 16:37:24","updated":"2022-09-29 16:13:15","focus_keyword":null,"additional_keywords":null,"truseo_locale":null},"gt_translate_keys":[{"key":"link","format":"url"}],"_links":{"self":[{"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/posts\/79465","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/comments?post=79465"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/posts\/79465\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/media\/79466"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/media?parent=79465"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/categories?post=79465"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/tags?post=79465"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}