{"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\/es\/blog\/administrirovanie\/istina-prezhde-vsego-ili-pochemu-sistemu-nuzhno-proektirovat-ishodya-iz-ustrojstva-bazy-dannyh","title":{"rendered":"La verdad ante todo, o por qu\u00e9 el sistema debe ser dise\u00f1ado considerando el dispositivo base de datos","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p>\u00a1Hola, Habr!<\/p>\n<p>Continuamos explorando el 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> y <noindex><a rel=\"nofollow\" href=\"https:\/\/storage.piter.com\/upload\/contents\/978544611587\/978544611587_X.pdf\">Spring<\/a><\/noindex>, incluyendo a nivel de bases de datos. Hoy les proponemos leer sobre por qu\u00e9, al dise\u00f1ar aplicaciones grandes, la estructura de la base de datos y no el c\u00f3digo Java deber\u00eda tener un papel decisivo, c\u00f3mo se hace y qu\u00e9 excepciones hay a esta regla.<br \/>\n<noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex> <br \/>\nEn este art\u00edculo bastante atrasado explicar\u00e9 por qu\u00e9 creo que, pr\u00e1cticamente en todos los casos, el modelo de datos en una aplicaci\u00f3n debe ser dise\u00f1ado \"basado en la base de datos\", y no \"basado en las capacidades de Java\" (o cualquier otro lenguaje del cliente que est\u00e9s utilizando). Si eliges el segundo enfoque, te adentras en un largo camino de dolor y sufrimiento, tan pronto como tu proyecto comience a crecer.<\/p>\n<p>El art\u00edculo est\u00e1 basado en <noindex><a rel=\"nofollow\" href=\"https:\/\/stackoverflow.com\/questions\/50706556\/kotlin-with-jooq-and-write-table-models-manually-without-code-generation\">una pregunta<\/a><\/noindex>, formulada en Stack Overflow.<\/p>\n<p>Interesantes discusiones en reddit en las secciones <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> y <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>Generaci\u00f3n de c\u00f3digo<\/h4>\n<p>\nMe sorprendi\u00f3 mucho que haya tan poca capa de usuarios que, al conocer jOOQ, se indignan con el hecho de que al trabajar con jOOQ se conf\u00eda considerablemente en la generaci\u00f3n de c\u00f3digo fuente. Nadie te impide usar jOOQ como consideres necesario, y no te obliga a usar la generaci\u00f3n de c\u00f3digo. Pero por defecto (como se describe en la gu\u00eda), trabajar con jOOQ funciona as\u00ed: comienzas con un esquema de base de datos (heredado), realizas su ingenier\u00eda inversa con la ayuda del generador de c\u00f3digo jOOQ para obtener un conjunto de clases que representan tus tablas, y luego escribes consultas seguras en tipo a esas tablas:<\/p>\n<pre><code class=\"java\">\tfor (Record2 record : DSL.using(configuration)\n\/\/   ^^^^^^^^^^^^^^^^^^^^^^^ La informaci\u00f3n de tipos es inferida a partir\n\/\/   del c\u00f3digo generado al que se refiere la\n\/\/   condici\u00f3n SELECT siguiente\n\n       .select(ACTOR.FIRST_NAME, ACTOR.LAST_NAME)\n\/\/           vvvvv ^^^^^^^^^^^^  ^^^^^^^^^^^^^^^ nombres generados\n       .from(ACTOR)\n       .orderBy(1, 2)) {\n    \/\/ ...\n}<\/code><\/pre>\n<p>\nEl c\u00f3digo se genera ya sea manualmente fuera de la compilaci\u00f3n, o manualmente en cada compilaci\u00f3n. Por ejemplo, tal regeneraci\u00f3n puede seguir inmediatamente despu\u00e9s de <noindex><a rel=\"nofollow\" href=\"https:\/\/blog.jooq.org\/2014\/06\/25\/flyway-and-jooq-for-unbeatable-sql-development-productivity\/\">la migraci\u00f3n de bases de datos Flyway, que tambi\u00e9n se puede realizar manual o autom\u00e1ticamente.<\/a><\/noindex>.<\/p>\n<h4>Generaci\u00f3n de c\u00f3digo fuente<\/h4>\n<p>\nCon enfoques como la generaci\u00f3n de c\u00f3digo, tanto manuales como autom\u00e1ticos, hay distintas filosof\u00edas, ventajas y desventajas que no voy a discutir en detalle en este art\u00edculo. Pero, en general, toda la esencia del c\u00f3digo generado es que permite reproducir en Java esa \"verdad\" que aceptamos como un hecho, ya sea dentro de nuestro sistema o fuera de \u00e9l. En cierto sentido, lo mismo hacen los compiladores, generando bytecode, c\u00f3digo m\u00e1quina o alg\u00fan otro tipo de c\u00f3digo a partir del c\u00f3digo fuente; obtenemos una representaci\u00f3n de nuestra \"verdad\" en otro lenguaje, independientemente de las razones concretas. <\/p>\n<p>Existen muchos generadores de este tipo. Por ejemplo, <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.oracle.com\/javase\/8\/docs\/technotes\/tools\/unix\/xjc.html\">XJC puede generar c\u00f3digo Java a partir de archivos XSD o WSDL.<\/a><\/noindex>El principio siempre es el mismo:<\/p>\n<ul>\n<li>Hay una verdad (interna o externa), como una especificaci\u00f3n, un modelo de datos, etc.<\/li>\n<li>Necesitamos una representaci\u00f3n local de esa verdad en nuestro lenguaje de programaci\u00f3n.<\/li>\n<\/ul>\n<p>\nAdem\u00e1s, generar tal representaci\u00f3n casi siempre es conveniente para evitar la redundancia.<\/p>\n<h4>Proveedores de tipos y procesamiento de anotaciones<\/h4>\n<p>\nA tener en cuenta: otro enfoque m\u00e1s moderno y espec\u00edfico para la generaci\u00f3n de c\u00f3digo para jOOQ est\u00e1 relacionado con el uso de proveedores de tipos, <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/fsharp\/tutorials\/type-providers\/\">tal como est\u00e1n implementados en F#.<\/a><\/noindex>En este caso, el c\u00f3digo es generado por el compilador, precisamente en la fase de compilaci\u00f3n. En forma de c\u00f3digo fuente, tal c\u00f3digo en principio no existe. En Java hay herramientas similares, aunque no tan elegantes: son los procesadores de anotaciones, por ejemplo, <noindex><a rel=\"nofollow\" href=\"https:\/\/projectlombok.org\">Lombok.<\/a><\/noindex>.<\/p>\n<p>En cierto sentido, aqu\u00ed ocurren las mismas cosas que en el primer caso, excepto que:<\/p>\n<ul>\n<li>No ves el c\u00f3digo generado (\u00bfquiz\u00e1s esta situaci\u00f3n no es tan desalentadora para algunos?).<\/li>\n<li>Debes garantizar que los tipos puedan ser proporcionados, es decir, la \"verdad\" siempre debe ser accesible. Esto es f\u00e1cil en el caso de Lombok, que anota la \"verdad\". Es un poco m\u00e1s complicado con los modelos de bases de datos, cuya operaci\u00f3n depende de una conexi\u00f3n en vivo disponible de forma constante.<\/li>\n<\/ul>\n<p><\/p>\n<h4>\u00bfCu\u00e1l es el problema con la generaci\u00f3n de c\u00f3digo?<\/h4>\n<p>\nAdem\u00e1s de la astuta pregunta sobre la mejor manera de iniciar la generaci\u00f3n de c\u00f3digo, ya sea manualmente o de forma autom\u00e1tica, hay que mencionar que hay personas que consideran que la generaci\u00f3n de c\u00f3digo no es necesaria en absoluto. La justificaci\u00f3n de este punto de vista, que he encontrado con m\u00e1s frecuencia, es que luego es complicado configurar la tuber\u00eda de construcci\u00f3n. S\u00ed, realmente es complicado. Surgen costos adicionales de infraestructura. Si apenas est\u00e1s comenzando a trabajar con un producto determinado (ya sea jOOQ, JAXB, Hibernate, etc.), el tiempo que se dedica a configurar el entorno de trabajo es un tiempo que preferir\u00edas gastar en estudiar la API misma, para luego extraer valor de ella.<\/p>\n<p>Si los costos relacionados con entender el funcionamiento del generador son demasiado altos, entonces, efectivamente, la usabilidad del generador de c\u00f3digo en la API no ha sido bien trabajada (y posteriormente resulta que la configuraci\u00f3n personalizada en \u00e9l tambi\u00e9n es complicada). La facilidad de uso debe ser la m\u00e1xima prioridad para cualquier API de este tipo. Pero este es tan solo un argumento en contra de la generaci\u00f3n de c\u00f3digo. En el resto de los casos, es completamente necesario escribir manualmente la representaci\u00f3n local de la verdad interna o externa.<\/p>\n<p>Muchos dir\u00e1n que no tienen tiempo para ocuparse de todo esto. Tienen fechas l\u00edmites para entregar su S\u00faper Producto. Alguna vez despu\u00e9s ajustaremos las tuber\u00edas de construcci\u00f3n, habr\u00e1 tiempo. Yo les responder\u00e9:<\/p>\n<p><img decoding=\"async\" alt=\"La verdad ante todo, o por qu\u00e9 el sistema debe ser dise\u00f1ado considerando el dispositivo base de datos\" 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>Pero en Hibernate \/ JPA es tan sencillo escribir c\u00f3digo \"para Java\". <\/b><\/p>\n<p>De hecho. Para Hibernate y sus usuarios, esto es al mismo tiempo una bendici\u00f3n y una maldici\u00f3n. En Hibernate, puedes simplemente escribir un par de entidades, as\u00ed:<\/p>\n<pre><code class=\"java\">\t@Entity\nclass Book {\n  @Id\n  int id;\n  String title;\n}<\/code><\/pre>\n<p>\nY casi todo est\u00e1 listo. Ahora, la tarea de Hibernate es generar los complejos \"detalles\" sobre c\u00f3mo exactamente esta entidad ser\u00e1 definida en el DDL de tu \"dialecto\" 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... y empezamos a ejecutar la aplicaci\u00f3n. Realmente es una gran oportunidad para comenzar a trabajar r\u00e1pidamente y probar diferentes cosas.<\/p>\n<p>Sin embargo, perm\u00edteme. He sido un poco deshonesto.<\/p>\n<ul>\n<li>\u00bfY Hibernate realmente aplicar\u00e1 la definici\u00f3n de esta clave primaria nombrada?<\/li>\n<li>\u00bfHibernate crear\u00e1 un \u00edndice en TITLE? \u2013 s\u00e9 con certeza que lo necesitaremos. <\/li>\n<li>\u00bfY Hibernate realmente har\u00e1 que esta clave sea identificativa en la Especificaci\u00f3n de Identidad?<\/li>\n<\/ul>\n<p>\nProbablemente no. Si est\u00e1s desarrollando tu proyecto desde cero, siempre es conveniente simplemente deshacerse de la antigua base de datos y generar una nueva, tan pronto como agregues las anotaciones necesarias. As\u00ed, la entidad Book al final tomar\u00e1 la forma: <\/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>\nGenial. Regenerar de nuevo. De nuevo, en ese caso ser\u00e1 muy f\u00e1cil al principio.<\/p>\n<h4>Pero posteriormente tendr\u00e1s que pagar por ello.<\/h4>\n<p>\nTarde o temprano, tendr\u00e1s que salir a producci\u00f3n. Es precisamente entonces cuando este tipo de modelo dejar\u00e1 de funcionar. Porque: <\/p>\n<p>En producci\u00f3n ya no podr\u00e1s deshacerte de la antigua base de datos cuando sea necesario y comenzar de nuevo desde cero. Tu base de datos se convertir\u00e1 en heredada.<\/p>\n<p>A partir de ahora y para siempre, tendr\u00e1s que escribir <noindex><a rel=\"nofollow\" href=\"https:\/\/blog.jooq.org\/2014\/06\/25\/flyway-and-jooq-for-unbeatable-sql-development-productivity\/\">scripts de migraci\u00f3n DDL, por ejemplo, utilizando Flyway.<\/a><\/noindex>. \u00bfY qu\u00e9 pasar\u00e1 entonces con tus entidades? Podr\u00e1s adaptarlas manualmente (y as\u00ed duplicar tu carga de trabajo), o le pedir\u00e1s a Hibernate que las regenere por ti (\u00bfqu\u00e9 probabilidades hay de que lo generado est\u00e9 a la altura de tus expectativas?) En cualquier caso, pierdes.<\/p>\n<p>As\u00ed que, una vez que pases a producci\u00f3n, necesitar\u00e1s parches urgentes. Y necesitas implementarlos en producci\u00f3n r\u00e1pidamente. Dado que no te preparaste y no organizaste una canalizaci\u00f3n suave para tus migraciones en producci\u00f3n, todo ser\u00e1 parcheado de manera desordenada. Y luego ya no tendr\u00e1s tiempo para hacerlo bien. Y culpas a Hibernate, ya que siempre hay alguien m\u00e1s a quien culpar, menos a ti...<\/p>\n<p>En lugar de eso, desde el principio todo podr\u00eda haberse hecho de manera completamente diferente. Por ejemplo, poner ruedas redondas en la bicicleta.<\/p>\n<h4>Primero la base de datos.<\/h4>\n<p>\nLa verdadera \"verdad\" en el esquema de tu base de datos y la \"soberan\u00eda\" sobre ella reside dentro de la base de datos. El esquema se define \u00fanicamente en la propia base de datos y en ninguna otra parte, y cada uno de los clientes tiene una copia de ese esquema, por lo que tiene total sentido imponer el cumplimiento del esquema y su integridad, hacer esto directamente en la base de datos - all\u00ed donde se almacena la informaci\u00f3n. <br \/>\nEs una sabidur\u00eda antigua, incluso manida. Las claves primarias y \u00fanicas son buenas. Las claves for\u00e1neas son buenas. La verificaci\u00f3n de restricciones es buena. <noindex><a rel=\"nofollow\" href=\"https:\/\/community.oracle.com\/ideas\/13028\">Afirmaciones<\/a><\/noindex> son buenas. <\/p>\n<p>Sin embargo, eso no es todo. Por ejemplo, si utiliza Oracle, probablemente querr\u00e1 especificar:<\/p>\n<ul>\n<li>\u00bfEn qu\u00e9 espacio de tablas se encuentra su tabla? <\/li>\n<li>\u00bfCu\u00e1l es su valor PCTFREE? <\/li>\n<li>\u00bfCu\u00e1l es el tama\u00f1o de la cach\u00e9 en su secuencia (por el identificador)?<\/li>\n<\/ul>\n<p>\nPuede que todo esto no sea relevante en sistemas peque\u00f1os, pero no es necesario esperar a entrar en la zona de \"big data\"; se puede comenzar a beneficiarse de las optimizaciones de almacenamiento proporcionadas por los proveedores mucho antes, como las mencionadas anteriormente. Ninguna de las ORM que he visto (incluyendo jOOQ) proporciona acceso al conjunto completo de opciones DDL que puede que desee usar en su base de datos. Las ORM ofrecen algunas herramientas que ayudan a escribir DDL.<\/p>\n<p>Pero, al final, un esquema bien dise\u00f1ado se escribe manualmente en DDL. Cualquier DDL generado es solo una aproximaci\u00f3n de ello.<\/p>\n<h4>\u00bfQu\u00e9 pasa con el modelo del cliente?<\/h4>\n<p>\nComo se mencion\u00f3 anteriormente, en el cliente necesitar\u00e1 una copia del esquema de su base de datos, una vista del cliente. Es innecesario mencionar que esta vista del cliente debe estar sincronizada con el modelo real. \u00bfCu\u00e1l es la mejor manera de lograr esto? A trav\u00e9s de un generador de c\u00f3digo.<\/p>\n<p>Todas las bases de datos proporcionan su metainformaci\u00f3n a trav\u00e9s de SQL. As\u00ed es como obtener todas las tablas de su base de datos en diferentes dialectos 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>\nEstas consultas (o similares, dependiendo de si tambi\u00e9n se deben tener en cuenta las vistas, vistas materializadas, funciones con valor de tabla) tambi\u00e9n se realizan a trav\u00e9s de la llamada <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> desde JDBC, o a trav\u00e9s del m\u00f3dulo meta de jOOQ.<\/p>\n<p>A partir de los resultados de tales consultas, es relativamente f\u00e1cil generar cualquier vista del modelo de su base de datos en el cliente, independientemente de la tecnolog\u00eda que utilice.<\/p>\n<ul>\n<li>Si utiliza JDBC o Spring, puede crear un conjunto de constantes de cadena. <\/li>\n<li>Si utiliza JPA, puede generar las entidades usted mismo. <\/li>\n<li>Si utiliza jOOQ, puede generar el modelo meta de jOOQ.<\/li>\n<\/ul>\n<p>\nDependiendo del volumen de posibilidades que ofrece su API cliente (por ejemplo, jOOQ o JPA), el modelo de metadatos generado puede ser realmente rico y completo. Tomemos, por ejemplo, la posibilidad de uniones 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\/\">introducida en jOOQ 3.11<\/a><\/noindex>, que se basa en la metainformaci\u00f3n generada sobre las relaciones de claves externas que existen entre sus tablas.<\/p>\n<p>Ahora, cualquier modificaci\u00f3n en la base de datos actualizar\u00e1 autom\u00e1ticamente el c\u00f3digo cliente. Imagine, por ejemplo:<\/p>\n<pre><code class=\"sql\">ALTER TABLE book RENAME COLUMN title TO book_title;<\/code><\/pre>\n<p>\n\u00bfRealmente querr\u00eda hacer este trabajo dos veces? De ninguna manera. Simplemente registramos el DDL, lo pasamos a trav\u00e9s de su pipeline de compilaci\u00f3n y obtenemos la entidad actualizada:<\/p>\n<pre><code class=\"java\">@Entity\n@Table(name = \"book\", indexes = {\n \n  \/\/ \u00bfLo has pensado?\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>\nO la clase jOOQ actualizada. La mayor\u00eda de los cambios en el DDL tambi\u00e9n se reflejan en la sem\u00e1ntica, no solo en la sintaxis. Por lo tanto, puede ser conveniente ver en el c\u00f3digo compilado qu\u00e9 c\u00f3digo ser\u00e1 (o puede ser) afectado por la modificaci\u00f3n de su base de datos.<\/p>\n<h4>La \u00fanica verdad<\/h4>\n<p>\nIndependientemente de la tecnolog\u00eda que utilice, siempre hay un modelo que es la \u00fanica fuente de verdad para alg\u00fan subsistema, o al menos, debemos aspirar a ello y evitar esa confusi\u00f3n empresarial donde la \"verdad\" est\u00e1 en todas partes y en ninguna parte. Todo puede ser mucho m\u00e1s simple. Si solo est\u00e1 intercambiando archivos XML con alg\u00fan otro sistema, simplemente use XSD. Mire el modelo de metadatos INFORMATION_SCHEMA de jOOQ en 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 es muy comprensible<\/li>\n<li>XSD etiqueta muy bien el contenido XML y permite realizar validaciones en todos los lenguajes cliente <\/li>\n<li>XSD tiene un buen versionado y presenta una s\u00f3lida compatibilidad hacia atr\u00e1s <\/li>\n<li>XSD se puede traducir a c\u00f3digo Java usando XJC<\/li>\n<\/ul>\n<p>\nEl \u00faltimo punto es importante. Al comunicarnos con un sistema externo mediante mensajes XML, queremos asegurarnos de que nuestros mensajes sean v\u00e1lidos. Esto se puede lograr f\u00e1cilmente con JAXB, XJC y XSD. Ser\u00eda una locura esperar que, con un enfoque de dise\u00f1o 'Java primero', donde creamos nuestros mensajes como objetos Java, se pudieran mapear de manera clara a XML y enviarse para ser consumidos por otro sistema. El XML generado de esta manera tendr\u00eda una calidad muy baja, no estar\u00eda documentado y ser\u00eda dif\u00edcil de desarrollar. Si hubiera un acuerdo sobre el nivel de calidad del servicio (SLA) para tal interfaz, lo habr\u00edamos estropeado de inmediato.<\/p>\n<p>Para ser honesto, esto es lo que constantemente sucede con las API en JSON, pero esa es otra historia, la pr\u00f3xima vez me quejar\u00e9... <\/p>\n<h4>Bases de datos: es lo mismo.<\/h4>\n<p>\nAl trabajar con bases de datos, usted comprende que todas son, en principio, similares. Una base de datos posee sus propios datos y debe gestionar su esquema. Cualquier modificaci\u00f3n realizada en el esquema debe implementarse directamente en DDL, para que se actualice la \u00fanica fuente de verdad.<\/p>\n<p>Cuando se actualiza la fuente, todos los clientes tambi\u00e9n deben actualizar sus copias del modelo. Algunos clientes pueden estar escritos en Java usando jOOQ y Hibernate o JDBC (o todos a la vez). Otros clientes pueden estar escritos en Perl (les queda desearles suerte), los terceros, en C#. No importa. El modelo principal se encuentra en la base de datos. Los modelos generados mediante ORM suelen ser de mala calidad, poco documentados y dif\u00edciles de desarrollar.<\/p>\n<p>As\u00ed que no cometas errores. Desde el principio, no cometas errores. Trabaja desde la base de datos. Construye un canal de despliegue que pueda ser automatizado. Incluye generadores de c\u00f3digo para facilitar la copia del modelo de tu base de datos y su transferencia a los clientes. Y deja de preocuparte por los generadores de c\u00f3digo. Son buenos. Con ellos ser\u00e1s m\u00e1s productivo. Solo necesitas dedicar un poco de tiempo al principio para configurarlos, y luego te esperan a\u00f1os de mayor productividad, de los que se compone la historia de tu proyecto.<\/p>\n<p>No me agradezcas todav\u00eda, despu\u00e9s.<\/p>\n<h4>Explicaci\u00f3n<\/h4>\n<p>\nPara mayor claridad: Este art\u00edculo no promueve de ninguna manera que toda la sistema (es decir, el dominio del problema, la l\u00f3gica empresarial, etc.) deba ajustarse al modelo de su base de datos. En este art\u00edculo, hablo sobre c\u00f3mo el c\u00f3digo del cliente que interact\u00faa con la base de datos debe actuar, bas\u00e1ndose en el modelo de la base de datos, de modo que el propio c\u00f3digo no reproduzca el modelo de la base de datos en un estado de \u00abprimera clase\u00bb. Esta l\u00f3gica generalmente se ubica en el nivel de acceso a los datos en su cliente.<\/p>\n<p>En arquitecturas de dos niveles, que todav\u00eda se conservan en algunos lugares, este modelo de sistema puede ser la \u00fanica opci\u00f3n posible. Sin embargo, en la mayor\u00eda de los sistemas, el nivel de acceso a los datos me parece una \u00absub-sistema\u00bb que encapsula el modelo de la base de datos.<\/p>\n<h4>Excepciones<\/h4>\n<p>\nDe cualquier regla hay excepciones, y ya he mencionado que el enfoque con la primac\u00eda de la base de datos y la generaci\u00f3n de c\u00f3digo fuente a veces puede resultar inapropiado. Aqu\u00ed hay un par de tales excepciones (probablemente haya otras):<\/p>\n<ul>\n<li>Cuando la estructura es desconocida y debe ser descubierta. Por ejemplo, usted es un proveedor de herramientas que ayuda a los usuarios a orientarse en cualquier esquema. Uf. Aqu\u00ed no hay forma de generar c\u00f3digo. Pero aun as\u00ed, la base de datos es primordial.<\/li>\n<li>Cuando la estructura debe generarse sobre la marcha para resolver alguna tarea. Este ejemplo parece una versi\u00f3n un poco extravagante del patr\u00f3n <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Entity%E2%80%93attribute%E2%80%93value_model\">entity attribute value<\/a><\/noindex>, es decir, realmente no tiene una estructura claramente definida. En este caso, a menudo ni siquiera se puede estar seguro de que le convenga un RDBMS.<\/li>\n<\/ul>\n<p>\nLas excepciones, por su naturaleza, son excepcionales. En la mayor\u00eda de los casos relacionados con el uso de RDBMS, la estructura se conoce de antemano, est\u00e1 dentro del RDBMS y es la \u00fanica fuente de \u00abverdad\u00bb, y todos los clientes deben obtener copias derivadas de ella. Idealmente, se debe utilizar un generador de c\u00f3digo en este caso.<br \/>\n<br \/>Fuente: <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.2 - 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\/es\/blog\/administrirovanie\/istina-prezhde-vsego-ili-pochemu-sistemu-nuzhno-proektirovat-ishodya-iz-ustrojstva-bazy-dannyh\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.2\" \/>\n\t\t<meta property=\"og:locale\" content=\"es_ES\" \/>\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\/es\/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\udd47La verdad ante todo, o por qu\u00e9 el sistema debe ser dise\u00f1ado desde la estructura de la base de datos | ProHoster","description":"\u00a1Hola, Habr! Continuamos explorando el tema","canonical_url":"https:\/\/prohoster.info\/es\/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":"es_ES","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\/es\/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\/es\/wp-json\/wp\/v2\/posts\/79465","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/es\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/es\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/es\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/es\/wp-json\/wp\/v2\/comments?post=79465"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/es\/wp-json\/wp\/v2\/posts\/79465\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/es\/wp-json\/wp\/v2\/media\/79466"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/es\/wp-json\/wp\/v2\/media?parent=79465"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/es\/wp-json\/wp\/v2\/categories?post=79465"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/es\/wp-json\/wp\/v2\/tags?post=79465"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}