{"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\/en\/blog\/administrirovanie\/istina-prezhde-vsego-ili-pochemu-sistemu-nuzhno-proektirovat-ishodya-iz-ustrojstva-bazy-dannyh","title":{"rendered":"Truth first, or why the system should be designed around the database device","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p>Hello, Habr!<\/p>\n<p>We continue to explore the topic <noindex><a rel=\"nofollow\" href=\"https:\/\/www.piter.com\/collection\/all\/product\/sovremennyy-yazyk-java-lyambda-vyrazheniya-potoki-i-funktsionalnoe-programmirovanie\">Java.<\/a><\/noindex> and <noindex><a rel=\"nofollow\" href=\"https:\/\/storage.piter.com\/upload\/contents\/978544611587\/978544611587_X.pdf\">Spring<\/a><\/noindex>, including at the database level. Today, we offer to read about why, when designing large applications, the database structure, rather than Java code, should be paramount, how it is done, and what exceptions exist to this rule.<br \/>\n<noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex> <br \/>\nIn this somewhat delayed article, I will explain why I believe that in almost all cases, the data model in an application should be designed \"from the database up,\" rather than \"from the capabilities of Java\" (or any other client-side language you are using). Choosing the latter approach leads you down a long path of pain and suffering once your project begins to grow.<\/p>\n<p>This article is based on <noindex><a rel=\"nofollow\" href=\"https:\/\/stackoverflow.com\/questions\/50706556\/kotlin-with-jooq-and-write-table-models-manually-without-code-generation\">a question<\/a><\/noindex>, asked on Stack Overflow.<\/p>\n<p>Interesting discussions on Reddit in the sections <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> and <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>Code Generation<\/h4>\n<p>\nI was quite surprised that there is such a small layer of users who, after getting acquainted with jOOQ, are outraged by the fact that jOOQ seriously relies on code generation. No one prevents you from using jOOQ as you see fit and does not force you to use code generation. But by default (as described in the documentation), working with jOOQ goes like this: you start with an (inherited) database schema, perform its reverse engineering using the jOOQ code generator to obtain a set of classes representing your tables, and then write type-safe queries to these tables:<\/p>\n<pre><code class=\"java\">\tfor (Record2 record : DSL.using(configuration)\n\/\/   ^^^^^^^^^^^^^^^^^^^^^^^ Type information is derived from\n\/\/   the generated code referenced by the condition below\n\/\/   SELECT \n \n       .select(ACTOR.FIRST_NAME, ACTOR.LAST_NAME)\n\/\/           vvvvv ^^^^^^^^^^^^  ^^^^^^^^^^^^^^^ generated names\n       .from(ACTOR)\n       .orderBy(1, 2)) {\n    \/\/ ...\n}<\/code><\/pre>\n<p>\nThe code is generated either manually outside the build or manually on each build. For example, such regeneration may follow immediately after <noindex><a rel=\"nofollow\" href=\"https:\/\/blog.jooq.org\/2014\/06\/25\/flyway-and-jooq-for-unbeatable-sql-development-productivity\/\">the Flyway database migration, which can also be performed manually or automatically.<\/a><\/noindex>.<\/p>\n<h4>Source code generation<\/h4>\n<p>\nDifferent philosophies, advantages, and disadvantages are associated with these approaches to code generation\u2014both manual and automated. I won\u2019t delve into them in detail in this article. However, the core idea of generated code is that it enables us to reproduce in Java the 'truth' that we accept as a given, either within our system or outside of it. In a sense, this is similar to what compilers do when generating bytecode, machine code, or some other form of code based on source files\u2014we obtain a representation of our 'truth' in another language, regardless of the specific reasons. <\/p>\n<p>There are many such code generators. For example, <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.oracle.com\/javase\/8\/docs\/technotes\/tools\/unix\/xjc.html\">XJC can generate Java code based on XSD or WSDL files.<\/a><\/noindex>The principle is always the same:<\/p>\n<ul>\n<li>There exists some truth (internal or external)\u2014for example, a specification, data model, etc.<\/li>\n<li>We need a local representation of that truth in our programming language.<\/li>\n<\/ul>\n<p>\nMoreover, generating such representation is almost always advisable\u2014to avoid redundancy.<\/p>\n<h4>Type providers and annotation processing<\/h4>\n<p>\nNote: Another, more modern and specific approach to code generation for jOOQ involves using type providers, <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/fsharp\/tutorials\/type-providers\/\">as they are implemented in F#.<\/a><\/noindex>In this case, the code is generated by the compiler at the compilation stage. Such code does not exist as source code per se. In Java, there are similar, albeit less elegant tools\u2014these are annotation processors, for example, <noindex><a rel=\"nofollow\" href=\"https:\/\/projectlombok.org\">Lombok.<\/a><\/noindex>.<\/p>\n<p>In a certain sense, the same processes occur here as in the first case, except for the following:<\/p>\n<ul>\n<li>You do not see the generated code (perhaps this situation doesn\u2019t seem so off-putting to some people?).<\/li>\n<li>You must ensure that the types can be provided, meaning that 'truth' must always be accessible. This is easy in the case of Lombok, which annotates the 'truth.' It is a bit more complicated with database models, whose operation depends on a constantly available live connection.<\/li>\n<\/ul>\n<p><\/p>\n<h4>What\u2019s the problem with code generation?<\/h4>\n<p>\nIn addition to the tricky question of whether it's better to start code generation manually or automatically, there's also the viewpoint that some people believe code generation is unnecessary at all. The reasoning I encounter most often is that it complicates the assembly line setup. Yes, it's indeed challenging. There are additional infrastructure costs. If you're just starting to work with a particular product (be it jOOQ, JAXB, Hibernate, etc.), the time spent setting up the working environment is time you'd prefer to spend learning the API itself to later extract value from it.<\/p>\n<p>If the costs associated with figuring out the generator's workings are too high, then indeed, the API has poorly addressed the usability of the code generator (and over time, it turns out that user configuration is also complicated). Ease of use should be the highest priority for any such API. But this is just one argument against code generation. Otherwise, you should completely write the local representation of internal or external truth manually.<\/p>\n<p>Many will say they don't have time to deal with this. They have tight deadlines for their Super Product. We'll tidy up the assembly lines later; there's still time. My response to them is:<\/p>\n<p><img decoding=\"async\" alt=\"Truth first, or why the system should be designed around the database device\" 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;Rourke, Audience Stack<\/a><\/noindex> <\/p>\n<p><b>But in Hibernate \/ JPA, writing code 'for Java' is so straightforward. <\/b><\/p>\n<p>Indeed. For Hibernate and its users, this is both a blessing and a curse. In Hibernate, you can easily write a couple of entities like this:<\/p>\n<pre><code class=\"java\">\t@Entity\nclass Book {\n  @Id\n  int id;\n  String title;\n}<\/code><\/pre>\n<p>\nAnd almost everything is ready. Now Hibernate's duty is to generate the complex 'details' of how exactly this entity will be defined in the DDL of your SQL 'dialect':<\/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 and we start running the application. It's a truly great opportunity to quickly get to work and try different things.<\/p>\n<p>However, hold on. I was being dishonest.<\/p>\n<ul>\n<li>Will Hibernate actually apply the definition of this named primary key?<\/li>\n<li>Will Hibernate create an index on TITLE? \u2013 I know for sure we'll need it. <\/li>\n<li>And will Hibernate indeed make this key identifying in the Identity Specification?<\/li>\n<\/ul>\n<p>\nProbably not. If you're developing your project from scratch, it's always convenient to just discard the old database and generate a new one as soon as you add the necessary annotations. Thus, the Book entity will ultimately look like this: <\/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>\nCool. Generate it again. Again, in this case, it will be very easy at the start.<\/p>\n<h4>But later on, you will have to pay for it.<\/h4>\n<p>\nSooner or later, you will have to go into production. That's when this model will cease to work. Because: <\/p>\n<p>In production, you will no longer be able to just discard the old database and start everything from scratch. Your database will turn into legacy.<\/p>\n<p>From now on, you will have to write <noindex><a rel=\"nofollow\" href=\"https:\/\/blog.jooq.org\/2014\/06\/25\/flyway-and-jooq-for-unbeatable-sql-development-productivity\/\">DDL migration scripts, for example, using Flyway.<\/a><\/noindex>. And what will happen to your entities in that case? You will either adapt them manually (and thus double your workload), or instruct Hibernate to regenerate them for you (what are the chances that the generated ones will meet your expectations?). In any case, you lose.<\/p>\n<p>Thus, as soon as you go into production, you will need hot patches. And they need to be deployed into production very quickly. Since you weren't prepared and didn't organize a smooth pipeline for your migrations in production, you will end up patching everything wildly. Then, you won\u2019t have time to do everything correctly. And you blame Hibernate, since anyone but you is always to blame...<\/p>\n<p>Instead, everything could have been done completely differently from the very beginning. For instance, put round wheels on the bicycle.<\/p>\n<h4>First, the database.<\/h4>\n<p>\nThe real \"truth\" in your database schema and the \"sovereignty\" over it lies within the database. The schema is defined only in the database itself and nowhere else, and each client has a copy of this schema, so it makes complete sense to enforce schema compliance and integrity right in the database \u2013 where the information is stored. <br \/>\nThis is an old, even hackneyed wisdom. Primary and unique keys are good. Foreign keys are good. Constraint validation is good. <noindex><a rel=\"nofollow\" href=\"https:\/\/community.oracle.com\/ideas\/13028\">Assertions<\/a><\/noindex> \u2013 are good. <\/p>\n<p>Moreover, this is not all. For instance, when using Oracle, you might want to specify:<\/p>\n<ul>\n<li>In which tablespace your table is located <\/li>\n<li>What its PCTFREE value is <\/li>\n<li>What the cache size is in your sequence (after the identifier)<\/li>\n<\/ul>\n<p>\nThis may not be important in small systems, but you don't have to wait for the transition to 'big data' \u2014 you can start benefiting from the data storage optimizations provided by the vendor much earlier, such as those mentioned above. None of the ORMs I have encountered (including jOOQ) offer access to the full set of DDL options you might want to use in your database. ORMs provide some tools that help to write DDL.<\/p>\n<p>But, ultimately, a well-designed schema is manually written in DDL. Any generated DDL is merely an approximation of it.<\/p>\n<h4>What about the client model?<\/h4>\n<p>\nAs mentioned above, on the client, you will need a copy of your database schema, the client view. It goes without saying that this client view must be synchronized with the actual model. How best to achieve this? With a code generator.<\/p>\n<p>All databases provide their metadata through SQL. Here's how to obtain all tables from your database in various SQL dialects:<\/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>\nThese queries (or similar ones, depending on whether views, materialized views, and table-valued functions also need to be considered) are also executed via the call <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> from JDBC, or via the jOOQ meta-module.<\/p>\n<p>From the results of such queries, it is relatively easy to generate any client view of your database model, regardless of what technology you are using on the client side.<\/p>\n<ul>\n<li>If you are using JDBC or Spring, you can create a set of string constants <\/li>\n<li>If you are using JPA, you can generate the entities yourself <\/li>\n<li>If you are using jOOQ, you can generate the jOOQ meta-model<\/li>\n<\/ul>\n<p>\nDepending on the volume of capabilities offered by your client API (e.g., jOOQ or JPA), the generated meta-model can be quite rich and complete. Take, for example, the ability for implicit joins, <noindex><a rel=\"nofollow\" href=\"https:\/\/blog.jooq.org\/2018\/02\/20\/type-safe-implicit-join-through-path-navigation-in-jooq-3-11\/\">introduced in jOOQ 3.11,<\/a><\/noindex>, which relies on the generated metadata about the relationships defined by foreign keys between your tables.<\/p>\n<p>Now, any change in the database will automatically lead to updates in the client code. Imagine, for instance:<\/p>\n<pre><code class=\"sql\">ALTER TABLE book RENAME COLUMN title TO book_title;<\/code><\/pre>\n<p>\nWould you really want to do this work twice? Absolutely not. Just capture the DDL, run it through your build pipeline, and get the updated entity:<\/p>\n<pre><code class=\"java\">@Entity\n@Table(name = \"book\", indexes = {\n \n  \/\/ Have you thought about it?\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>\nOr the updated jOOQ class. Most DDL changes also reflect on the semantics, not just on the syntax. Therefore, it can be useful to see in the compiled code which code will (or may) be affected by your database changes.<\/p>\n<h4>The single source of truth<\/h4>\n<p>\nRegardless of the technology you use, there is always one model that serves as the single source of truth for a certain subsystem \u2013 or, at least, we should strive for this and avoid enterprise confusion where \"truth\" is both everywhere and nowhere at the same time. Things can be much simpler. If you are merely exchanging XML files with another system, just use XSD. Look at the INFORMATION_SCHEMA meta-model from jOOQ in XML form:<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 is well understood<\/li>\n<li>XSD marks up XML content very well and allows for validation across all client languages <\/li>\n<li>XSD has good versioning and offers robust backward compatibility <\/li>\n<li>XSD can be translated into Java code using XJC<\/li>\n<\/ul>\n<p>\nThe last point is important. When communicating with an external system using XML messages, we want to ensure the validity of our messages. This can be easily achieved using JAXB, XJC, and XSD. It would be utterly insane to assume that with a 'Java first' design approach, where we make our messages as Java objects, they could somehow be meaningfully converted to XML and sent for consumption in another system. XML generated this way would be of very poor quality, undocumented, and hard to evolve. If there were a service level agreement (SLA) in place for such an interface, we would have failed immediately.<\/p>\n<p>Honestly, this is exactly what constantly happens with JSON APIs, but that's another story; I'll rant about it next time... <\/p>\n<h4>Databases: they are all the same.<\/h4>\n<p>\nWhen working with databases, you understand that they are fundamentally similar. A database owns its data and must govern the schema. Any modifications to the schema must be implemented directly on the DDL to update the single source of truth.<\/p>\n<p>Once the source is updated, all clients must also update their copies of the model. Some clients may be written in Java using jOOQ and Hibernate or JDBC (or all at once). Other clients may be written in Perl (wish them good luck), while others in C#. It doesn't matter. The primary model resides in the database. Models generated with ORM are usually of poor quality, poorly documented, and difficult to evolve.<\/p>\n<p>So don't make mistakes. Don't make mistakes from the very beginning. Work based on the database. Build a deployment pipeline that can be automated. Include code generators to conveniently replicate your database model and push it to clients. And stop worrying about code generators. They are good. With them, you will be more productive. You just need to spend a little time configuring them from the start \u2013 and you will reap years of increased productivity, which will shape your project's history.<\/p>\n<p>Don't thank me yet; later.<\/p>\n<h4>Explanation<\/h4>\n<p>\nFor clarity: This article does not in any way advocate for bending your entire system (i.e., subject area, business logic, etc.) to fit your database model. In this article, I am saying that client code interacting with the database should operate based on the database model, such that the database model is not fully reproduced within the client code as a 'first-class' object. This logic typically resides at the data access level on your client.<\/p>\n<p>In two-tier architectures, which still exist in some cases, such a system model may be the only possible one. However, in most systems, the data access level seems to me to be a 'subsystem' encapsulating the database model.<\/p>\n<h4>Exceptions<\/h4>\n<p>\nEvery rule has exceptions, and I have already mentioned that the approach of database primacy and source code generation may sometimes be unsuitable. Here are a couple of such exceptions (there may be others):<\/p>\n<ul>\n<li>When the schema is unknown and needs to be opened. For example, you are a tool provider helping users navigate any schema. Phew. This cannot be accomplished without code generation. But still \u2013 the database comes first.<\/li>\n<li>When the schema needs to be generated on the fly to solve a specific task. This example seems like a slightly elaborate 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 that you really don't have a clearly defined schema. In this case, one can often not even be sure that an RDBMS will suit 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, resides within the RDBMS, and is the sole source of 'truth,' while all clients must obtain copies derived from it. Ideally, a code generator should be utilized in this scenario.<br \/>\n<br \/>Source: <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\/en\/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=\"en_US\" \/>\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\/en\/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 first, or why the system should be designed based on the structure of the database | ProHoster","description":"Hello, Habr! We continue to explore the topic","canonical_url":"https:\/\/prohoster.info\/en\/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":"en_US","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\/en\/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\/en\/wp-json\/wp\/v2\/posts\/79465","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/comments?post=79465"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/posts\/79465\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/media\/79466"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/media?parent=79465"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/categories?post=79465"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/tags?post=79465"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}