{"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\/pt\/blog\/administrirovanie\/istina-prezhde-vsego-ili-pochemu-sistemu-nuzhno-proektirovat-ishodya-iz-ustrojstva-bazy-dannyh","title":{"rendered":"A verdade em primeiro lugar, ou por que o sistema deve ser projetado a partir da estrutura do banco de dados","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p>Ol\u00e1, Habr!<\/p>\n<p>Continuamos a explorar o tema <noindex><a rel=\"nofollow\" href=\"https:\/\/www.piter.com\/collection\/all\/product\/sovremennyy-yazyk-java-lyambda-vyrazheniya-potoki-i-funktsionalnoe-programmirovanie\">Java<\/a><\/noindex> e <noindex><a rel=\"nofollow\" href=\"https:\/\/storage.piter.com\/upload\/contents\/978544611587\/978544611587_X.pdf\">Spring<\/a><\/noindex>, incluindo a n\u00edvel de bancos de dados. Hoje, convidamos voc\u00ea a ler sobre por que, ao projetar grandes aplicativos, a estrutura do banco de dados, e n\u00e3o o c\u00f3digo Java, deve ter um papel determinante, como isso \u00e9 feito e quais s\u00e3o as exce\u00e7\u00f5es a essa regra.<br \/>\n<noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex> <br \/>\nNeste artigo bastante tardio, explicarei por que acredito que, em praticamente todos os casos, o modelo de dados em um aplicativo deve ser projetado \"a partir do banco de dados\", e n\u00e3o \"a partir das capacidades do Java\" (ou de outra linguagem de cliente com a qual voc\u00ea esteja trabalhando). Ao escolher a segunda abordagem, voc\u00ea embarca em um longo caminho de dor e sofrimento assim que seu projeto come\u00e7a a crescer.<\/p>\n<p>O artigo \u00e9 baseado em <noindex><a rel=\"nofollow\" href=\"https:\/\/stackoverflow.com\/questions\/50706556\/kotlin-with-jooq-and-write-table-models-manually-without-code-generation\">uma pergunta<\/a><\/noindex>, feita no Stack Overflow.<\/p>\n<p>Discuss\u00f5es interessantes no reddit nas se\u00e7\u00f5es <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> e <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>Gera\u00e7\u00e3o de c\u00f3digo<\/h4>\n<p>\nFiquei bastante surpreso ao descobrir que h\u00e1 um pequeno grupo de usu\u00e1rios que, ao conhecer o jOOQ, se indignam com o fato de que, ao trabalhar com jOOQ, se confia seriamente na gera\u00e7\u00e3o de c\u00f3digo fonte. Ningu\u00e9m o impede de usar o jOOQ da maneira que considerar necess\u00e1ria e ningu\u00e9m o for\u00e7a a usar a gera\u00e7\u00e3o de c\u00f3digo. Mas, por padr\u00e3o (como descrito no guia), trabalhar com jOOQ acontece assim: voc\u00ea come\u00e7a com um esquema de banco de dados (herdado), realiza sua engenharia reversa usando o gerador de c\u00f3digo jOOQ para obter um conjunto de classes representando suas tabelas e, em seguida, escreve consultas seguras para o tipo para essas tabelas:<\/p>\n<pre><code class=\"java\">\tfor (Record2 record : DSL.using(configuration)\n\/\/   ^^^^^^^^^^^^^^^^^^^^^^^ As informa\u00e7\u00f5es de tipos s\u00e3o derivadas de\n\/\/   c\u00f3digo gerado, ao qual se refere a condi\u00e7\u00e3o de SELECT abaixo\n \n       .select(ACTOR.FIRST_NAME, ACTOR.LAST_NAME)\n\/\/           vvvvv ^^^^^^^^^^^^  ^^^^^^^^^^^^^^^ nomes gerados\n       .from(ACTOR)\n       .orderBy(1, 2)) {\n    \/\/ ...\n}<\/code><\/pre>\n<p>\nO c\u00f3digo \u00e9 gerado fora da compila\u00e7\u00e3o manualmente ou manualmente a cada compila\u00e7\u00e3o. Por exemplo, tal regenera\u00e7\u00e3o pode ocorrer logo ap\u00f3s <noindex><a rel=\"nofollow\" href=\"https:\/\/blog.jooq.org\/2014\/06\/25\/flyway-and-jooq-for-unbeatable-sql-development-productivity\/\">a migra\u00e7\u00e3o do banco de dados Flyway, que tamb\u00e9m pode ser realizada manual ou automaticamente.<\/a><\/noindex>.<\/p>\n<h4>Gera\u00e7\u00e3o de c\u00f3digo fonte<\/h4>\n<p>\nCom essas abordagens para a gera\u00e7\u00e3o de c\u00f3digo \u2013 manuais e autom\u00e1ticas \u2013 s\u00e3o associadas diferentes filosofias, vantagens e desvantagens, que n\u00e3o pretendo discutir detalhadamente neste artigo. Mas, em geral, toda a ess\u00eancia do c\u00f3digo gerado \u00e9 que ele permite reproduzir em Java a \"verdade\" que aceitamos como um dado na nossa realidade, seja dentro do nosso sistema ou fora dele. De certa forma, a mesma coisa \u00e9 feita pelos compiladores, que geram bytecode, c\u00f3digo de m\u00e1quina ou qualquer outro tipo de c\u00f3digo com base nos c\u00f3digos-fonte \u2013 obtemos uma representa\u00e7\u00e3o da nossa \"verdade\" em outra linguagem, independentemente das raz\u00f5es espec\u00edficas. <\/p>\n<p>Existem muitos desses geradores de c\u00f3digo. Por exemplo, <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.oracle.com\/javase\/8\/docs\/technotes\/tools\/unix\/xjc.html\">XJC pode gerar c\u00f3digo Java com base em arquivos XSD ou WSDL<\/a><\/noindex>. O princ\u00edpio \u00e9 sempre o mesmo:<\/p>\n<ul>\n<li>H\u00e1 uma certa verdade (interna ou externa) \u2013 por exemplo, uma especifica\u00e7\u00e3o, um modelo de dados, etc.<\/li>\n<li>Precisamos de uma representa\u00e7\u00e3o local dessa verdade em nossa linguagem de programa\u00e7\u00e3o.<\/li>\n<\/ul>\n<p>\nAl\u00e9m disso, gerar tal representa\u00e7\u00e3o quase sempre \u00e9 apropriado \u2013 para evitar redund\u00e2ncias.<\/p>\n<h4>Provedores de tipos e processamento de anota\u00e7\u00f5es<\/h4>\n<p>\nNota: outra abordagem mais moderna e espec\u00edfica para a gera\u00e7\u00e3o de c\u00f3digo para jOOQ est\u00e1 relacionada ao uso de provedores de tipos, <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/fsharp\/tutorials\/type-providers\/\">na forma como s\u00e3o implementados em F#<\/a><\/noindex>. Nesse caso, o c\u00f3digo \u00e9 gerado pelo compilador, na verdade, na etapa de compila\u00e7\u00e3o. Na forma de c\u00f3digos-fonte, tal c\u00f3digo praticamente n\u00e3o existe. Em Java, existem ferramentas semelhantes, embora n\u00e3o t\u00e3o elegantes \u2013 s\u00e3o os processadores de anota\u00e7\u00f5es, por exemplo, <noindex><a rel=\"nofollow\" href=\"https:\/\/projectlombok.org\">Lombok<\/a><\/noindex>.<\/p>\n<p>De certa forma, aqui ocorrem as mesmas coisas que no primeiro caso, exceto que:<\/p>\n<ul>\n<li>Voc\u00ea n\u00e3o v\u00ea o c\u00f3digo gerado (talvez essa situa\u00e7\u00e3o n\u00e3o pare\u00e7a t\u00e3o desagrad\u00e1vel para alguns?).<\/li>\n<li>Voc\u00ea deve garantir que os tipos possam ser fornecidos, ou seja, a \"verdade\" deve estar sempre acess\u00edvel. Isso \u00e9 f\u00e1cil no caso do Lombok, que anota a \"verdade\". \u00c9 um pouco mais complicado com os modelos de bancos de dados, cujo funcionamento depende de uma conex\u00e3o ativa e constantemente dispon\u00edvel.<\/li>\n<\/ul>\n<p><\/p>\n<h4>Qual \u00e9 o problema com a gera\u00e7\u00e3o de c\u00f3digo?<\/h4>\n<p>\nAl\u00e9m da quest\u00e3o astuta sobre como \u00e9 melhor iniciar a gera\u00e7\u00e3o de c\u00f3digo \u2013 manualmente ou automaticamente, \u00e9 preciso mencionar que existem pessoas que acreditam que a gera\u00e7\u00e3o de c\u00f3digo n\u00e3o \u00e9 necess\u00e1ria. O principal argumento que encontrei para essa opini\u00e3o \u00e9 que isso torna dif\u00edcil configurar o pipeline de constru\u00e7\u00e3o. Sim, \u00e9 realmente complicado. Surgem custos adicionais de infraestrutura. Se voc\u00ea est\u00e1 apenas come\u00e7ando a trabalhar com um determinado produto (seja jOOQ, JAXB, Hibernate etc.), o tempo gasto para configurar o ambiente de trabalho \u00e9 o que voc\u00ea gostaria de dedicar ao estudo da API em si, para depois extrair valor dela.<\/p>\n<p>Se os custos relacionados a entender como funciona o gerador forem muito altos, ent\u00e3o, de fato, houve problemas de usabilidade na API para o gerador de c\u00f3digo (e, no futuro, descobre-se que a configura\u00e7\u00e3o do usu\u00e1rio \u00e9 complicada). A facilidade de uso deve ser a prioridade m\u00e1xima para qualquer API desse tipo. Mas esse \u00e9 apenas um argumento contra a gera\u00e7\u00e3o de c\u00f3digo. No restante, \u00e9 preciso escrever manualmente a representa\u00e7\u00e3o local da verdade interna ou externa.<\/p>\n<p>Muitos dir\u00e3o que n\u00e3o t\u00eam tempo para lidar com isso tudo. Eles t\u00eam prazos apertados para o seu Super-Produto. Um dia, depois ajustaremos os pipelines de constru\u00e7\u00e3o, teremos tempo. Eu respondo a eles:<\/p>\n<p><img decoding=\"async\" alt=\"A verdade em primeiro lugar, ou por que o sistema deve ser projetado a partir da estrutura do banco de dados\" 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&#8217;Rourk, Audience Stack<\/a><\/noindex> <\/p>\n<p><b>Mas no Hibernate\/JPA \u00e9 t\u00e3o f\u00e1cil escrever c\u00f3digo \"para Java\". <\/b><\/p>\n<p>De fato. Para o Hibernate e seus usu\u00e1rios, isso \u00e9 ao mesmo tempo uma b\u00ean\u00e7\u00e3o e uma maldi\u00e7\u00e3o. No Hibernate \u00e9 poss\u00edvel escrever facilmente algumas entidades, assim:<\/p>\n<pre><code class=\"java\">\t@Entity\nclass Book {\n  @Id\n  int id;\n  String title;\n}<\/code><\/pre>\n<p>\nE quase tudo est\u00e1 pronto. Agora, a tarefa do Hibernate \u00e9 gerar os \"detalhes\" complexos de como exatamente essa entidade ser\u00e1 definida em DDL do seu \"dialeto\" 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 e come\u00e7amos a executar o aplicativo. \u00c9 realmente uma \u00f3tima oportunidade para come\u00e7ar rapidamente e experimentar diferentes coisas.<\/p>\n<p>No entanto, espere. Eu exagerei.<\/p>\n<ul>\n<li>E o Hibernate realmente aplicar\u00e1 a defini\u00e7\u00e3o dessa chave prim\u00e1ria nomeada?<\/li>\n<li>E o Hibernate criar\u00e1 um \u00edndice em TITLE? \u2013 eu sei que vamos precisar dele. <\/li>\n<li>O Hibernate realmente tornar\u00e1 essa chave identificadora na Especifica\u00e7\u00e3o de Identidade?<\/li>\n<\/ul>\n<p>\nProvavelmente, n\u00e3o. Se voc\u00ea est\u00e1 desenvolvendo seu projeto do zero, \u00e9 sempre conveniente simplesmente descartar o banco de dados antigo e gerar um novo assim que adicionar as anota\u00e7\u00f5es necess\u00e1rias. Assim, a entidade Book acabar\u00e1 assim: <\/p>\n<pre><code class=\"java\">\t@Entity\n@Table(name = &quot;book&quot;, indexes = {\n  @Index(name = &quot;i_book_title&quot;, columnList = &quot;title&quot;)\n})\nclass Book {\n  @Id\n  @GeneratedValue(strategy = IDENTITY)\n  int id;\n  String title;\n}\n<\/code><\/pre>\n<p>\nLegal. Regerar. Novamente, neste caso, ser\u00e1 muito f\u00e1cil no in\u00edcio.<\/p>\n<h4>Mas, posteriormente, voc\u00ea ter\u00e1 que pagar por isso.<\/h4>\n<p>\nMais cedo ou mais tarde, voc\u00ea ter\u00e1 que ir para a produ\u00e7\u00e3o. \u00c9 nesse momento que esse modelo come\u00e7ar\u00e1 a falhar. Porque: <\/p>\n<p>Na produ\u00e7\u00e3o, n\u00e3o ser\u00e1 mais poss\u00edvel descartar o banco de dados antigo, se necess\u00e1rio, e come\u00e7ar tudo do zero. Seu banco de dados se tornar\u00e1 legado.<\/p>\n<p>De agora em diante, voc\u00ea ter\u00e1 que escrever <noindex><a rel=\"nofollow\" href=\"https:\/\/blog.jooq.org\/2014\/06\/25\/flyway-and-jooq-for-unbeatable-sql-development-productivity\/\">scripts de migra\u00e7\u00e3o DDL, por exemplo, usando Flyway.<\/a><\/noindex>. E o que acontecer\u00e1 com suas entidades nesse caso? Voc\u00ea poder\u00e1 ou adapt\u00e1-las manualmente (e assim dobrar sua carga de trabalho) ou pedir ao Hibernate para ger\u00e1-las novamente para voc\u00ea (quais s\u00e3o as chances de que o gerado assim atenda \u00e0s suas expectativas?) Voc\u00ea, de qualquer forma, perde.<\/p>\n<p>Assim, assim que voc\u00ea for para a produ\u00e7\u00e3o, precisar\u00e1 de hotfixes. E eles precisam ser colocados na produ\u00e7\u00e3o muito rapidamente. Como voc\u00ea n\u00e3o se preparou e n\u00e3o organizou uma continuidade suave de suas migra\u00e7\u00f5es para a produ\u00e7\u00e3o, voc\u00ea far\u00e1 tudo apressadamente. E ent\u00e3o n\u00e3o conseguir\u00e1 fazer tudo corretamente. E voc\u00ea culpa o Hibernate, pois sempre h\u00e1 algu\u00e9m a culpar, menos voc\u00ea...<\/p>\n<p>Em vez disso, desde o in\u00edcio, poderia ter sido feito de forma completamente diferente. Por exemplo, colocar rodas redondas em uma bicicleta.<\/p>\n<h4>Primeiro o banco de dados.<\/h4>\n<p>\nA verdadeira \"verdade\" no esquema do seu banco de dados e a \"soberania\" sobre ele est\u00e1 dentro do banco de dados. O esquema \u00e9 definido apenas no pr\u00f3prio banco de dados e em nenhum outro lugar, e cada cliente tem uma c\u00f3pia desse esquema, portanto, \u00e9 completamente razo\u00e1vel impor a conformidade com o esquema e sua integridade, fazendo isso diretamente no banco de dados - l\u00e1 onde a informa\u00e7\u00e3o \u00e9 armazenada. <br \/>\n\u00c9 uma sabedoria antiga e at\u00e9 mesmo batida. Chaves prim\u00e1rias e \u00fanicas s\u00e3o boas. Chaves estrangeiras s\u00e3o boas. Verifica\u00e7\u00e3o de restri\u00e7\u00f5es \u00e9 boa. <noindex><a rel=\"nofollow\" href=\"https:\/\/community.oracle.com\/ideas\/13028\">Afirmar<\/a><\/noindex> \u00e9 bom. <\/p>\n<p>Al\u00e9m disso, isso ainda n\u00e3o \u00e9 tudo. Por exemplo, ao usar Oracle, voc\u00ea provavelmente vai querer especificar:<\/p>\n<ul>\n<li>Em qual tablespace sua tabela est\u00e1 localizada <\/li>\n<li>Qual \u00e9 o valor de PCTFREE dela <\/li>\n<li>Qual \u00e9 o tamanho do cache na sua sequ\u00eancia (ap\u00f3s o identificador)<\/li>\n<\/ul>\n<p>\nTalvez tudo isso n\u00e3o importe em sistemas pequenos, mas n\u00e3o \u00e9 necess\u00e1rio esperar atingir a \u00e1rea de \"big data\" - voc\u00ea pode come\u00e7ar a aproveitar os benef\u00edcios das otimiza\u00e7\u00f5es de armazenamento oferecidas pelo fornecedor muito antes disso, como as mencionadas acima. Nenhuma das ORM que eu j\u00e1 vi (incluindo jOOQ) fornece acesso ao conjunto completo de op\u00e7\u00f5es DDL que voc\u00ea pode querer utilizar no seu banco de dados. As ORM oferecem algumas ferramentas que ajudam a escrever DDL.<\/p>\n<p>Mas, no final das contas, um esquema bem projetado \u00e9 escrito manualmente em DDL. Qualquer DDL gerado \u00e9 apenas uma aproxima\u00e7\u00e3o dele.<\/p>\n<h4>E quanto ao modelo do cliente?<\/h4>\n<p>\nComo mencionado anteriormente, no cliente voc\u00ea precisar\u00e1 de uma c\u00f3pia do esquema do seu banco de dados, uma representa\u00e7\u00e3o do cliente. \u00c9 desnecess\u00e1rio mencionar que essa representa\u00e7\u00e3o do cliente deve ser sincronizada com o modelo real. Qual a melhor maneira de conseguir isso? Usando um gerador de c\u00f3digo.<\/p>\n<p>Todos os bancos de dados fornecem suas metainforma\u00e7\u00f5es via SQL. Veja como obter de seu banco de dados todas as tabelas em diferentes dialetos de SQL:<\/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>\nEssas consultas (ou semelhantes, dependendo se tamb\u00e9m \u00e9 necess\u00e1rio considerar views, views materializadas e fun\u00e7\u00f5es com valor de tabela) tamb\u00e9m s\u00e3o realizadas atrav\u00e9s da chamada <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> do JDBC, ou atrav\u00e9s do m\u00f3dulo de metadados do jOOQ.<\/p>\n<p>A partir dos resultados dessas consultas, \u00e9 relativamente f\u00e1cil gerar qualquer representa\u00e7\u00e3o do modelo do seu banco de dados no cliente, independentemente da tecnologia utilizada.<\/p>\n<ul>\n<li>Se voc\u00ea usa JDBC ou Spring, pode criar um conjunto de constantes de string <\/li>\n<li>Se usar JPA, pode gerar as entidades por conta pr\u00f3pria <\/li>\n<li>Se usar jOOQ, pode gerar a metamodelo do jOOQ<\/li>\n<\/ul>\n<p>\nDependendo do volume de recursos oferecido pela sua API cliente (como jOOQ ou JPA), o meta-modelo gerado pode ser realmente rico e completo. Vamos considerar, por exemplo, a possibilidade de jun\u00e7\u00f5es impl\u00edcitas, <noindex><a rel=\"nofollow\" href=\"https:\/\/blog.jooq.org\/2018\/02\/20\/type-safe-implicit-join-through-path-navigation-in-jooq-3-11\/\">que apareceu no jOOQ 3.11<\/a><\/noindex>, que se baseia nas metainforma\u00e7\u00f5es geradas sobre as rela\u00e7\u00f5es de chaves estrangeiras que existem entre suas tabelas.<\/p>\n<p>Agora, qualquer altera\u00e7\u00e3o no banco de dados levar\u00e1 automaticamente \u00e0 atualiza\u00e7\u00e3o do c\u00f3digo cliente. Imagine, por exemplo:<\/p>\n<pre><code class=\"sql\">ALTER TABLE book RENAME COLUMN title TO book_title;<\/code><\/pre>\n<p>\nVoc\u00ea realmente gostaria de fazer esse trabalho duas vezes? De maneira nenhuma. Basta registrar o DDL, execut\u00e1-lo pelo seu pipeline de constru\u00e7\u00e3o e obter a entidade atualizada:<\/p>\n<pre><code class=\"java\">@Entity\n@Table(name = &quot;book&quot;, indexes = {\n \n  \n  @Index(name = &quot;i_book_title&quot;, columnList = &quot;book_title&quot;)\n})\nclass Book {\n  @Id\n  @GeneratedValue(strategy = IDENTITY)\n  int id;\n \n  @Column(&quot;book_title&quot;)\n  String bookTitle;\n}<\/code><\/pre>\n<p>\nOu a classe jOOQ atualizada. A maioria das mudan\u00e7as no DDL tamb\u00e9m reflete na sem\u00e2ntica, e n\u00e3o apenas na sintaxe. Portanto, pode ser \u00fatil olhar para o c\u00f3digo compilado e ver qual c\u00f3digo ser\u00e1 (ou pode ser) afetado pela altera\u00e7\u00e3o do seu banco de dados.<\/p>\n<h4>A \u00fanica verdade<\/h4>\n<p>\nIndependentemente da tecnologia que voc\u00ea estiver usando, sempre h\u00e1 um \u00fanico modelo que \u00e9 a \u00fanica fonte de verdade para algum subsistema \u2013 ou, no m\u00ednimo, devemos nos esfor\u00e7ar para isso e evitar aquela confus\u00e3o corporativa, onde a 'verdade' est\u00e1 em todo lugar e em nenhum lugar ao mesmo tempo. Tudo pode ser muito mais simples. Se voc\u00ea apenas estiver trocando arquivos XML com algum outro sistema, basta usar XSD. Veja o meta-modelo INFORMATION_SCHEMA do jOOQ em formato XML:<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 \u00e9 bem compreendido<\/li>\n<li>XSD descreve muito bem o conte\u00fado XML e permite valida\u00e7\u00e3o em todas as linguagens cliente <\/li>\n<li>XSD \u00e9 bem versionado e possui uma boa compatibilidade retroativa <\/li>\n<li>XSD pode ser traduzido para c\u00f3digo Java usando XJC<\/li>\n<\/ul>\n<p>\nO \u00faltimo ponto \u00e9 importante. Ao nos comunicarmos com um sistema externo atrav\u00e9s de mensagens XML, queremos ter certeza da validade de nossas mensagens. Isso \u00e9 muito f\u00e1cil de alcan\u00e7ar com JAXB, XJC e XSD. Seria um completo absurdo esperar que, ao adotar uma abordagem de design \"Java primeiro\", onde criamos nossas mensagens como objetos Java, pud\u00e9ssemos de alguma forma mape\u00e1-las para XML e envi\u00e1-las para consumo em outro sistema. XML gerado dessa forma teria qualidade muito ruim, seria n\u00e3o documentado e dif\u00edcil de evoluir. Se houvesse um acordo de n\u00edvel de servi\u00e7o (SLA) para tal interface, estar\u00edamos imediatamente em apuros.<\/p>\n<p>Para ser sincero, isso \u00e9 exatamente o que acontece constantemente com APIs em JSON, mas isso \u00e9 outra hist\u00f3ria, da pr\u00f3xima vez eu reclamo... <\/p>\n<h4>Bancos de dados: \u00e9 tudo a mesma coisa<\/h4>\n<p>\nAo trabalhar com bancos de dados, voc\u00ea percebe que todos eles, em ess\u00eancia, s\u00e3o semelhantes. O banco possui seus pr\u00f3prios dados e deve gerenciar o esquema. Quaisquer modifica\u00e7\u00f5es feitas no esquema devem ser implementadas diretamente no DDL, para que a \u00fanica fonte da verdade seja atualizada.<\/p>\n<p>Quando a atualiza\u00e7\u00e3o da fonte ocorrer, todos os clientes tamb\u00e9m devem atualizar suas c\u00f3pias do modelo. Alguns clientes podem ser escritos em Java utilizando jOOQ e Hibernate ou JDBC (ou todos ao mesmo tempo). Outros clientes podem ser escritos em Perl (desejo sorte a eles), e outros em C#. Isso n\u00e3o \u00e9 importante. O modelo principal est\u00e1 no banco de dados. Os modelos gerados por ORM geralmente s\u00e3o de qualidade ruim, mal documentados e dif\u00edceis de evoluir.<\/p>\n<p>Portanto, n\u00e3o cometa erros. Desde o in\u00edcio, n\u00e3o cometa erros. Trabalhe a partir do banco de dados. Construa um pipeline de implanta\u00e7\u00e3o que possa ser automatizado. Inclua geradores de c\u00f3digo para facilitar a c\u00f3pia do modelo do seu banco de dados e sua implementa\u00e7\u00e3o nos clientes. E pare de se preocupar com geradores de c\u00f3digo. Eles s\u00e3o bons. Com eles, voc\u00ea ser\u00e1 mais produtivo. Basta dedicar um pouco de tempo no in\u00edcio para configur\u00e1-los \u2013 e depois voc\u00ea ter\u00e1 anos de produtividade elevada, que se transformar\u00e3o na hist\u00f3ria do seu projeto.<\/p>\n<p>N\u00e3o me agrade\u00e7a ainda, depois.<\/p>\n<h4>Explica\u00e7\u00e3o<\/h4>\n<p>\nPara maior clareza: Este artigo de forma alguma defende que todo o sistema (ou seja, o dom\u00ednio, a l\u00f3gica de neg\u00f3cios, etc.) deve se curvar ao modelo do seu banco de dados. Aqui, estou dizendo que o c\u00f3digo do cliente que interage com o banco de dados deve agir com base no modelo do banco de dados, de modo que n\u00e3o reproduza a modelo do banco de dados como sendo de 'primeira classe'. Essa l\u00f3gica geralmente reside no n\u00edvel de acesso a dados no cliente.<\/p>\n<p>Em arquiteturas de duas camadas, que ainda existem em alguns lugares, esse modelo de sistema pode ser a \u00fanica possibilidade. No entanto, na maioria dos sistemas, o n\u00edvel de acesso a dados me parece uma 'sub-sistema', encapsulando o modelo do banco de dados.<\/p>\n<h4>Exce\u00e7\u00f5es<\/h4>\n<p>\nToda regra tem suas exce\u00e7\u00f5es, e j\u00e1 mencionei que a abordagem de prioridade para o banco de dados e a gera\u00e7\u00e3o do c\u00f3digo-fonte pode, \u00e0s vezes, ser inadequada. Aqui est\u00e3o algumas dessas exce\u00e7\u00f5es (provavelmente existem outras):<\/p>\n<ul>\n<li>Quando o esquema \u00e9 desconhecido e precisa ser descoberto. Por exemplo, voc\u00ea \u00e9 um fornecedor de ferramentas que ajuda os usu\u00e1rios a navegar em qualquer esquema. Uf. Aqui n\u00e3o tem como escapar da gera\u00e7\u00e3o de c\u00f3digo. Mas, ainda assim, o banco de dados deve vir em primeiro lugar.<\/li>\n<li>Quando o esquema deve ser gerado em tempo real para resolver uma tarefa espec\u00edfica. Este exemplo parece uma vers\u00e3o um pouco extravagante do padr\u00e3o <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Entity%E2%80%93attribute%E2%80%93value_model\">entity attribute value<\/a><\/noindex>, ou seja, voc\u00ea realmente n\u00e3o tem um esquema bem definido. Nesse caso, muitas vezes nem \u00e9 poss\u00edvel ter certeza de que um SGBD ser\u00e1 adequado.<\/li>\n<\/ul>\n<p>\nPor natureza, exce\u00e7\u00f5es s\u00e3o excepcionais. Na maioria dos casos relacionados ao uso de SGBDs, o esquema \u00e9 conhecido de antem\u00e3o, est\u00e1 dentro do SGBD e \u00e9 a \u00fanica fonte de 'verdade', e todos os clientes t\u00eam que obter c\u00f3pias derivadas dele. Idealmente, neste caso, deve-se utilizar um gerador de c\u00f3digo.<br \/>\n<br \/>Fonte: <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\/pt\/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=\"pt_PT\" \/>\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\/pt\/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\udd47A verdade em primeiro lugar, ou por que o sistema deve ser projetado com base na estrutura do banco de dados | ProHoster","description":"Ol\u00e1, Habr! Continuamos a explorar o tema","canonical_url":"https:\/\/prohoster.info\/pt\/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":"pt_PT","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\/pt\/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\/pt\/wp-json\/wp\/v2\/posts\/79465","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/comments?post=79465"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/posts\/79465\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/media\/79466"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/media?parent=79465"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/categories?post=79465"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/tags?post=79465"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}