«Истина в последней инстанции» или зачем нужен Database First Design

https://blog.jooq.org/2018/06/06/truth-first-or-why-you-should-mostly-implement-database-first-designs
  • Перевод

В этой весьма запоздалой статье я объясню почему, по моему мнению, в большинстве случаев при разработке модели данных приложения необходимо придерживаться подхода "database first". Вместо "Java[любой другой язык] first" подхода, который выведет вас на длинную дорожку, полную боли и страданий, как только проект начнет расти.


image
"Слишком занят, чтобы стать лучше" Licensed CC by Alan O’Rourke / Audience Stack. Оригинальное изображение


Эта статья вдохновлена недавним вопросом на StackOverflow.


Интересные reddit-обсуждения /r/java и /r/programming.


Кодогенерация


К моему удивлению, одна небольшая группа пользователей похоже была потрясена тем фактом, что jOOQ сильно "завязан" на генерации исходного кода.


При том, что вы можете использовать jOOQ именно так как вам удобно, предпочтительным способ (согласно документации) является начать именно с уже существующей схемы БД, затем сгенерировать необходимые клиентские классы (соответствующие вашим таблицам) с помощью jOOQ, а после этого уже спокойно писать типобезопасные запросы для этих таблиц:


for (Record2<String, String> record : DSL.using(configuration)
//   ^^^^^^^^^^^^^^^^^^^^^^^ Type information derived from the 
//   generated code referenced from the below SELECT clause

       .select(ACTOR.FIRST_NAME, ACTOR.LAST_NAME)
//           vvvvv ^^^^^^^^^^^^  ^^^^^^^^^^^^^^^ Generated names
       .from(ACTOR)
       .orderBy(1, 2)) {
    // ...
}

Код может генерироваться либо вручную вне сборки, либо автоматически с каждой сборкой. Например, такая генерация может происходить сразу же после установки Flyway-миграций, которые также могут запускаться как вручную, так и автоматически.


Генерация исходного кода


Существуют разные философии, преимущества и недостатки в отношении этих подходов к кодогенерации, которые я не хочу обсуждать в этой статье. Но по сути, смысл сгенерированного кода заключается в том, что он является Java-представлением того, что мы считаем неким "эталоном" (как внутри, так и снаружи нашей системы). В некотором роде компиляторы делают то же самое, когда генерируют байт-код, машинный код или какой-либо другой исходный код из исходников — в итоге мы получаем представление о нашем "эталоне" на другом специфичном языке.


Существует достаточно много таких генераторов кода. Например, XJC может генерировать Java-код из файлов XSD или WSDL. Принцип всегда один и тот же:


  • Существует некий эталон (внешний или внутренний), такой как спецификация, модель данных и пр.
  • Необходимо получить собственное представление об этом эталоне на нашем привычном языке программирования.

И почти всегда имеет смысл именно генерировать это представление, чтобы избежать лишней работы и лишних ошибок.


"Type providers" и обработка аннотаций


Примечательно, что еще один, более современный, подход к генерации кода в jOOQ — это Type Providers, (как он сделан в F#), где код генерируется компилятором при компиляции и никогда не существует в исходной форме. Аналогичный (но менее сложный) инструмент в Java — это обработчики аннотаций, например, Lombok.


В обоих случаях происходит все тоже самое что и в обычной кодогенерации, кроме:


  • Вы не видите сгенерированный код (возможно, для многих это уже большой плюс?)
  • Вы должны обеспечить доступность вашего "эталона" при каждой компиляции. Это не доставляет никаких проблем в случае с Lombok, который непосредственно аннотирует сам исходный код, который и является "эталоном" в данном случае. Немного сложнее с моделями баз данных, которые полагаются на всегда доступное "живое" соединение.

В чем проблема с генерацией кода?


Помимо каверзного вопроса, нужно ли генерировать код вручную или автоматически, некоторые люди считают, что код вообще не нужно генерировать. Причина, которую я слышу чаще всего — что такую генерацию сложно реализовать в CI/CD pipeline. И да, это правда, т.к. мы получаем накладные расходы на создание и поддержку дополнительной инфраструктуры, тем более если вы новичок в используемых инструментах (jOOQ, JAXB, Hibernate и др.).


Если накладные расходы на изучение работы кодогенератор слишком высоки, то действительно пользы будет мало. Но это единственный аргумент против. В большинстве остальных случаев совершенно не имеет никакого смысла вручную писать код, который является обычным представлением модели чего-либо.


Многие люди утверждают, что у них нет времени на это, т.к. именно сейчас нужно как можно скорее "выкатить" очередной MVP. А доработать свой CI/CD pipeline они смогут когда-нибудь потом. В таких случаях я обычно говорю: "Ты слишком занят, чтобы стать лучше".


"Но ведь Hibernate/JPA делает Java first разработку гораздо проще"


Да, это правда. Это одновременно и радость, и боль для пользователей Hibernate. С помощью него вы можете просто написать несколько объектов, вида:


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

И все, почти готово. Далее Hibernate возьмет на себя всю рутину по поводу того, как определить этот объект в DDL и на нужном SQL-диалекте:


CREATE TABLE book (
  id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
  title VARCHAR(50),

  CONSTRAINT pk_book PRIMARY KEY (id)
);

CREATE INDEX i_book_title ON book (title);

Это действительно отличный способ для быстрого старта разработки — остается только запустить приложение.


Но не все так радужно. Еще остается множество вопросов:


  • Сгенерирует ли Hibernate нужное мне имя для первичного ключа?
  • Создаст ли необходимый мне индекс на поле TITLE?
  • Будет ли генерироваться уникальное значение ID для каждой записи?

Похоже, что нет. Но пока проект находится в стадии разработки, вы всегда можете выбросить свою текущую базу данных и сгенерировать все с нуля, добавив нужные аннотации к модели.
Итак, класс Book в конечном виде будет выглядеть примерно так:


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

Но вы заплатите за это, чуть позже


Рано или поздно ваше приложение попадает в production, и описанная схема перестанет работать:


В живой и настоящей системе вы больше не сможете просто так взять и выбросить свою базу данных, т.к. данные в ней используются и могут стоить кучу денег.

С этого момента вам необходимо писать скрипты миграций на каждое изменение в модели данных, например, используя Flyway. При этом, что происходит с вашими клиентскими классами? Вы можете либо адаптировать их вручную (что приведет к двойной работе), либо попросить Hibernate генерировать их (но насколько велики шансы того, что результат такой генерации будет соответствовать ожиданиям?). В итоге вас могут ожидать большие проблемы.


Как только код попадает в production, в него почти сразу необходимо вносить исправления, причем как можно быстрее.


И т.к. установка миграций БД не встроена в ваш сборочный конвейер, придется устанавливать такие патчи вручную на свой страх и риск. Чтобы вернуться назад и сделать все правильно уже не хватит времени. Его хватит только на то, чтобы винить Hibernate во всех своих бедах.


Вместо этого вы могли бы поступить совершенно иначе с самого начала. А именно использовать круглые колеса вместо квадратных.


Вперед к "Database First"


Эталон схемы данных и контроль над ней находится в ведомстве вашей СУБД. База данных — это единственное место, где определена схема, и все клиенты имеют копию этой схемы, но не наоборот. Данные находятся в вашей базе данных, а не в вашем клиенте, поэтому имеет смысл обеспечить контроль схемы и ее целостности именно там, где находятся данные.


Это старая мудрость, ничего нового. Первичные и уникальные ключи хороши. Внешние ключи прекрасны. Проверка ограничений на стороне БД замечательна. Assertion (когда они окончательно реализованы) великолепны.


И это еще далеко не все. Например, если вы используете Oracle, вы можете указать:


  • В каком табличном пространстве находится ваша таблица
  • Какое значение PCTFREE она имеет
  • Каков размер кэша последовательности (sequence)

Возможно все это не имеет значения в небольших системах, зато в более крупных системах вам не придется идти по пути "больших данных", пока вы не выжмите все соки из своего текущего хранилища. Ни одна ORM, которую я когда-либо видел (в том числе jOOQ) не позволит вам использовать полный набор параметров DDL, которые предоставляет ваша СУБД. ORM предлагают только некоторые инструменты, которые помогут написать DDL.


В конечном счете, хорошо продуманная схема должна быть написана только вручную с помощью СУБД-специфичного DDL. Весь автоматически сгенерированный DDL являются лишь приближением к этому.


Что насчет клиентской модели?


Как уже упоминалось ранее, вам понадобится некое представление схемы БД на стороне клиента. Излишне говорить, что это представление должно быть синхронизировано с реальной моделью. Как это сделать? Конечно же используя генераторы кода.


Все базы данных предоставляют доступ к своей метаинформации посредством старого доброго SQL. Вот так, например, можно получить список всех таблиц из разных баз данных:


-- H2, HSQLDB, MySQL, PostgreSQL, SQL Server
SELECT table_schema, table_name
FROM information_schema.tables

-- DB2
SELECT tabschema, tabname
FROM syscat.tables

-- Oracle
SELECT owner, table_name
FROM all_tables

-- SQLite
SELECT name
FROM sqlite_master

-- Teradata
SELECT databasename, tablename
FROM dbc.tables

Именно такие запросы (а также аналогичные запросы для представлений, материализованных представлений и табличных функций) выполняются при вызове метода DatabaseMetaData.getTables() конкретного JDBC-драйвера, либо в модуле jOOQ-meta.


Из результатов таких запросов относительно легко создать любое клиентское представление модели БД, независимо от того, какая именно технология доступа к данным используется.


  • Если вы используете JDBC или Spring, вы можете создать группу String-констант
  • Если используете JPA, можете сами создавать объекты
  • Если используете jOOQ, можете создать метамодели jOOQ

В зависимости от количества функций, предлагаемых вашим API доступа к данным (jOOQ, JPA или что-то еще), сгенерированная метамодель может быть действительно богатой и полной. Как пример, функция неявного соединения в jOOQ 3.11, которая опирается на метаинформацию о взаимоотношениях внешних ключей между вашими таблицами.


Теперь любое изменение схемы базы данных автоматически приведет к обновлению клиентского кода.


Представьте, что нужно переименовать колонку в таблице:


ALTER TABLE book RENAME COLUMN title TO book_title;

Вы уверены, что хотите выполнить эту работу дважды? Ни за что. Просто закомитьте этот DDL, запустите сборку и наслаждайтесь обновленным объектом:


@Entity
@Table(name = "book", indexes = {

  // Would you have thought of this?
  @Index(name = "i_book_title", columnList = "book_title")
})
class Book {
  @Id
  @GeneratedValue(strategy = IDENTITY)
  int id;

  @Column("book_title")
  String **bookTitle**;
}

Так же полученный клиентский нет необходимости компилировать каждый раз (как минимум до следующего изменения в схеме БД), что уже может быть большим плюсом!
Большинство изменений DDL также являются семантическими изменениями, а не только синтаксическими. Таким образом, здорово видеть в сгенерированном коде клиента на что именно повлияли последние изменения в БД.


Правда всегда одна


Независимо от того, какую технологию вы используете, всегда должна быть только одна модель, которая и является эталоном для подсистемы. Или, как минимум, мы должны стремиться к этому и избегать неразберихи в бизнесе, где «эталон» есть везде и нигде одновременно. Это делает все намного проще. К примеру, если вы обмениваетесь XML-файлами с какой-либо другой системой, вы наверняка используете XSD. Как метамодель INFORMATION_SCHEMA jOOQ в формате XML: https://www.jooq.org/xsd/jooq-meta-3.10.0.xsd


  • XSD хорошо понятен
  • XSD отлично описывает XML-контент и позволяет осуществлять валидацию на всех клиентских языках
  • XSD позволяет легко управлять версиями и сохранять обратную совместимость
  • XSD можно превратить в Java-код с помощью XJC

Обратим отдельное внимание на последний пункт. При общении с внешней системой через XML-сообщения мы должны быть уверены в валидности сообщений. И это действительно очень легко сделать с помощью таких вещей как JAXB, XJC и XSD. Было бы сумасшествием думать об уместности Java-first подхода в данном случае. Генерируемый на основе объектов XML получится низкого качества, будет плохо задокументирован и трудно расширяем. И если на такое взаимодействие есть SLA, то вы будете разочарованы.


Честно говоря, это похоже на то, что сейчас происходит с различными API для JSON, но это уже совершенна другая история...


Чем базы данных хуже?


При работе с БД тут все тоже самое. База данных владеет данными, и она так же должна быть хозяином схемы этих данных. Все модификации схемы должны быть выполнены посредством DDL напрямую, чтобы обновить эталон.


После обновления эталона все клиенты должны обновить свои представления о модели. Некоторые клиенты могут быть написаны на Java, используя либо jOOQ и/или Hibernate, либо JDBC. Другие клиенты могут быть написаны на Perl (удачи им) или даже на C#. Это не имеет никакого значения. Основная модель находится в базе данных. Тогда как модели, созданные с помощью ORM, имеют низкое качество, недостаточно хорошо документированы и трудно расширяемы.


Поэтому не делайте этого, причем с самого начала разработки. Вместо этого начните с базы данных. Создайте автоматизированный CI/CD конвейер. Используйте в нем генерацию кода, чтобы автоматически генерировать модель базы данных для клиентов при каждой сборке. И перестаньте волноваться, все будет хорошо. Все что требуется — немного первоначальных усилий по настройке инфраструктуры, но в результате вы получите выигрыш в процессе разработки для остальной части вашего проекта на годы вперед.


Не надо благодарностей.


Пояснения


Для закрепления: эта статья никоим образом не утверждает, что модель базы данных должна распространяться на всю вашу систему (на предметную область, бизнес-логику и пр.). Мои заявления заключается лишь в том, что клиентский код, взаимодействующий с базой данных, должен быть лишь представлением схемы БД, но никак не определять и не формировать ее.


В двухуровневых архитектурах, которые по-прежнему имеют место быть, схема БД может быть единственным источником информации о модели вашей системы. Однако в большинстве систем я рассматриваю уровень доступа к данным как «подсистему», которая инкапсулирует модель базы данных. Как-то так.


Исключения


Как и в любом другом хорошем правиле, в нашем тоже есть свои исключения (и я уже предупреждал, что подход database first и генерация кода не всегда является правильным выбором). Вот эти исключения (возможно, список не полный):


  • Когда схема неизвестна заранее и ее необходимо исследовать. Например, вы поставщик инструмента, помогающего пользователям осуществлять навигацию по любой схеме. Само собой, тут не может быть никакой генерации кода. Но в любом случае, придется иметь дело с самой БД напрямую и ее схемой.
  • Когда для какой-то задачи необходимо создать схему "на лету". Это может быть похоже на одну из вариаций паттерна Entity-attribute-value, т.к. у вас нет четко определенной схемы. Также как и нет уверенности, что RDBMS в данном случае это верный выбор.

Особенность этих исключений в том, что они довольно редко всречаются в живой природе. В большинстве же случаев при использовании реляционных БД схема заранее известна и является "эталоном" вашей модели, а клиенты должны работать с копией этой модели, сгенерированной с использованием генераторов кода.

Поделиться публикацией

Похожие публикации

Комментарии 109
    +3
    Просто закомитьте этот DDL, запустите сборку и наслаждайтесь обновленным объектом

    И тут же отправляйтесь в путешествие по устранению ошибок компиляции в места, где использовалось старое имя поля (title), а также исправления названия поля в соответствующем dto объекте, если таковой есть. Вместо этого, можно было просто поменять название колонки в аннотации @Column(«book_title»). Наверное, это не совсем правильно, но иногда такое сильно проще и дешевле.
      0

      Вы так говорите как будто это плохо.
      Лучше получить ошибку компиляции, чем "sql error" в рантайме.

        0
        Я не говорил, что это плохо, но за фразой «запустите… и наслаждайтесь» скрыто еще кое-что, о чем собственно и написал. И часто манипуляций по устранению ошибок много больше чем того, что решается автоматической кодогенерацией.
          0
          sql error в этом случае вылазит, как правило, уже при запуске (spring boot и т. п.) или деплое приложения на сервер и выявляется разработчиком. до продакшена такое приложение не дойдёт.
        +2
        2018ый год. Время микросервисов с микроданными и распределённых баз без схемы, точнее со схемой на уровне кода сервисов. О какой кодогенерации тут говорить? Всё это уже давно стало легаси.
          +4
          «Никогда такого не было — и вот опять». Уже тысячу раз обсуждалось: микросервисы нужны не всегда, не являются серебряной пулей и накладывают весьма значимые ограничения на систему.
          А, кроме того, в них самих может запросто использоваться указанная выше кодогенерация. Почему нет, религия не против.
            0
            Я про то, что как только система сталкивается с нагрузкой, база тут же становится узким местом и уже становятся безраличны вопросы схемы-маппинга на её уровне. Работу с данными начинают разносить подальше от RDBMS и эти все вопросы становятся просто неактуальны. Т.е. актуальны, но в общем объёме работ по поддержанию системы в порядке — совершенно минорны.
              +3

              Не всё так однозначно. И нагрузка разная бывает, и данные, и требования к их целостности, актуальности, и куча ещё всего.
              Т.е. я понимаю, что реляционные БД в принципе не способны на бесконечное горизонтальное масштабирование, но внезапно может оказаться, что бесконечно и не надо.


              Чтобы не быть голословным, есть такой сайтик популярный — stackoverflow.com. Так вот у него в сердце MS SQL Server. Пруф: https://nickcraver.com/blog/2016/02/17/stack-overflow-the-architecture-2016-edition


              SQL не серебряная пуля, также как и noSQL. Каждому гвоздю свой молоток.

                –1
                Именно. Обычно проекты, которые на MSSQL начинались примерно к такой архитектуре и приходят по мере роста нагрузки. Основным звеном становится кеш на NoSQL, а затем и многие данные туда мигрируют, оставляя в MSSQL только ядро. И заметьте, новые проекты делаются у него на ES, а не MSSQL. Понятно, транзакционные вещи на Nosql в трезвом уме делать не будут, но в реальности операций с такими требованиями совсем не так много в реальном мире.
                  0
                  Поиск-то всё равно идёт по Elastic Search, который вполне себе NoSQL db.
                    0

                    Так в статье же речь про single source of truth. Вполне себе нормальная история основные данные держать в одной базе, а для полнотекстового поиска выгружать в другую, сильно более заточенную на, собственно, полнотекстовый поиск.


                    Как там у SO это сделано я не знаю, но вообще, при большом желании Elastic можно дропнуть и перестроить, и сервис скорее всего в это время испытает gracefull degradation — из гуглового поиска попасть можно будет (а это думаю большая часть их трафика), а из внутреннего, пока индекс не перестроится — нет. Ну, а учитывая, что это что-то вроде ЧП, то в общем-то и не страшно сильно.

                  0
                  Вы хотите сказать, что нет никакой разницы, работает одна медленная штука или две медленных штуки, если вторая не медленнее первой?
                    0
                    вторая штука быстра и легко горизонтально маштабируется. первую штуку освобождают от нагрузки, перенося данные и нагрузку во вторую.
                0
                На счет распределенных баз без схемы — например те же самые популярные (или только набирающие популярность) Cassandra, ClickHouse и Cockroach вполне себе поддерживают настоящие схемы.

                А микросервисная архитектура наоборот позволяет использовать каждую СУБД на всю катушку в изолированных сервисах.
                  0
                  Сервисы используют как правило какой-то субсет данных и привязка к схеме целиковой базы лишь уменьшает их жизненный цикл и усложняет разработку. Вообще эта архитектура — база наше всё и от неё и пляшем, уже ни один проект завела в болото.
                    0
                    «Архитектура — база наше всё и от неё и пляшем» — я тоже против такой архитектуры.

                    Статья (насколько я ее понимаю) немного о другом — изолировать использование конкретной СУБД в каком-нибудь изолированном слое или микросервисе и использовать все ее ништяки, которые она предоставляет.
                      –1
                      Статья про то, что почему-то правильно начинать с БД, то есть хранения данных, а не с работы с ними, как будто основная задача — это хранить их, а не совершать над ними какие-то действия. Как по мне такой подход изрядно устарел…
                        +2
                        «Устарел» — это не аргумент. Работает или нет, сколько ошибок, насколько легче рефакторить, как быстро разрабатывать — это аргументы.
                        А hype-driven development пусть идёт лесом.
                          0
                          «Устарел» в моем понимании значит «воспринимается практически, как догмат, хотя его давно пора переосмыслить». Я считаю, что в нынешних реалиях идея БД, как чуть ли не центрального звена, вокруг которого строится остальное приложение, себя изрядно изжила.
                            0
                            Я считаю, что в нынешних реалиях идея БД, как чуть ли не центрального звена, вокруг которого строится остальное приложение, себя изрядно изжила.


                            Согласен, но статья вроде этого и не утверждает.
                            Идея в том, что объектная модель живет и развивается отдельно, а модель БД отдельно, и где-то они встречаются.
                              0
                              но статья вроде этого и не утверждает.

                              Учитывая заголовок про «истину в последней инстанции» я бы не был так уверен :D
                                0
                                Не, ну это касается именно модели БД, не объектной
                                0
                                Генерация объектов по базе мало похожа на «где-то они встречаются». «Где-то они встречаются» — это классический подход DataMapper ORM типа Hibernate без использования средств генерации миграций по метаданным классов сущностей и ко. База живёт отдельно, объекты отдельно, а ORM маппит их друг на друга. Единственное, что в идеале произвольным образом, если он соотвествует критерям однозначности или допустимой многозначности, но на практике встречаются ограничения ORM, которая неспособна тонко тюнить схему конкретного движка СУБД или, например, неспособона замаппить коллекцию ValueObject на отдельную таблицу из-за чего приходится, например, ValueObject превращать в полноценную Enitty, как минимум «под капотом».
                  –2
                  Вместо «Java[любой другой язык] first» подхода, который выведет вас на длинную дорожку, полную боли и страданий, как только проект начнет расти.

                  До чего категорично-то… А как насчет длинной дорожки танцев «от базы», которая в итоге приведет к тому, что приложение зависит от БД чуть более чем полностью, потому что изрядная часть логики переползла именно туда? Я не раз и не два наблюдал, как функционал, который начинался, как «select с обработкой» постепенно превращался сначала в «сложные select с триггерами с последующей обработкой», а потом и в «сложные select с триггерами и прочими джобами, а так же кучей хранимых процедур с последующей обработкой или даже без оной». После чего приложения вырождались по сути дела до гейтов к базе, в которой была сосредоточена вся логика. Естественно никто уже толком не понимал, как это все работает, и уж точно не мог нормально наращивать функционал. Вот такая по моему опыту цена проектирования «от базы».
                    0
                    Ну тут из крайности в крайность получается ) В статье про бизнес-логику на стороне БД ничего не сказано, только про эффективную схему (констрейнты, ключи, параметры хранения). В том же самом Oracle сухое описание оператора CREATE TABLE занимает 20 стр., другие БД тоже не сильно отстают. Так почему этим не пользоваться.
                      –1
                      В статье не просто про схему, там как противопоставление подходу code-first, который якобы несет боль и страдание. Ну, так db-first несет не меньше боли, поскольку очень сложно танцуя «от схемы» не начать закладывать в эту схему логику. В том же Оракле у нас получалось примерно так — создадим схему, придумаем таблички, свяжем их ключами… А как проверить, что схема вышла удачной? Ну, селектом же. Вот уже появился первый селект, который выбирает данные, а то и несколько. Вы еще ни строчки кода не написали, а уже связаны неким контрактом, который был рожден учитывая только одни интересы — интересы БД, поскольку интересы кода никем представлены не были ввиду отсутствия такового. Не велика беда? Не велика. До тех пор, пока благодаря такому проектированию не случиться ситуация, когда «вот эту предобработку удобнее сделать на стороне БД, заведем пару триггеров на инсерт и апдейт...» — готово дело! У вас есть триггеры, а, скорее всего, еще и хранимка. И как теперь отвечать на вопрос «Как работает эта функциональность?» Правильный ответ — в болью, поскольку для нахождения ответа вы пойдете в код, размотаете его до какого-то уровня, ВНЕЗАПНО провалитесь в БД, где есть своя логика, размотаете ее, вернетесь в первый код… В моем случае весь этот психоз был накрыт еще web-частью, так что бутерброд получался совершенно не съедобным.
                        0
                        очень сложно танцуя «от схемы» не начать закладывать в эту схему логику


                        А как же архитектура, code review и все такое?
                          +1
                          Розовые пони разбегаются после 5ти лет жизни любого проекта.
                            0
                            А что архитектура? В реальности «продумать логику еще на этапе создания схемы БД» большинству разработчиков и архитекторов покажется хорошей идеей, и не поможет пяток предыдущих проектов. Привычка — страшная сила, а «Oracle головного мозга» тем более.
                              0
                              Ну это уже какие-то психологические проблемы.
                              Если у человека «Oracle головного мозга» — то тут и «code first» не поможет, он все равно сможет делать всякие штуки на стороне БД (только еще более костыльнее).
                            +2
                            из того что я наблюдал — при наборе некоторой критической массы (размера базы данных или размера критичных таблиц), все равно часть (да, не всё, но какие-то части) кода коротрый генерится ORM заменяется на хранимые процедуры. И я не вижу в этом вообще ничего плохого. Потому что код который был сгенерен ORM не использует все возможности диалекта SQL используемой СУБД, а частенько попросту паршив, просто это не чувствуется на малых данных.
                            ну и еще нанимают ДБА или SQL разработчика, потому что <подставьте язык сами> разрабочики плохо знают тонкости используемой на проекте СУБД, а бывает что и вовсе разучились SQL запросы писать.
                            Так что учитывать интересы базы рано или поздно придется, от этого никуда не уйти, но тут важно найти разумный компромис.
                              0
                              но тут важно найти разумный компромис

                              Скорее речь идет об изоляции. То есть программируя на объектно-ориентированных языках мы почему-то свято чтим наследование-инкапсуляция-полиморфизм, и инкапсулируем сложность «под капот», всеми силами обеспечиваем слабую связность. Но когда дело доходит до баз данных это внезапно куда-то пропадает, и становится совершенно нормальным размазать одну логическую единицу тонким слоем от кода и до хранимок в БД, вместе с триггерами и джобами. Я к тому, что я не фанатик, и совершенно не против того, что бы использовать мощь БД на полную, но делать это так, как это делается en masse — значит закладывать под фундамент своего приложения жирнющую мину. И данная статья такой подход мало того, что пропагандирует, так еще и называет «единственно верным».
                                +2
                                Скорее речь идет об изоляции. То есть программируя на объектно-ориентированных языках мы почему-то свято чтим наследование-инкапсуляция-полиморфизм, и инкапсулируем сложность «под капот», всеми силами обеспечиваем слабую связность. Но когда дело доходит до баз данных это внезапно куда-то пропадает, и становится совершенно нормальным размазать одну логическую единицу тонким слоем от кода и до хранимок в БД, вместе с триггерами и джобами.

                                рискну предположить (хотя случаи могут быть разными) — все это размазывание было проведено в ходе N-го количества оптимизаций со стороны базы данных. Дело в том, что я многократно наблюдал как при написании логики в базе данных пытались использовать часть принципов ООП — оно все хорошо, красиво, удобно, проще поддерживать… НО, рано или позно приходят они… тормоза…
                                То ли это какой-то отчет использующий очень навороченую вьюху на все случаи жизни, в которой есть все что нужно и не нужно париться и особо мудрить. Или же это какая-то аггрегация которая использует функции для каких-то вычислений, в которые упрятанна с глаз долой забористая логика. И все, приехали, вариант один — разворачивать функции-вьюхи, выкидывать все ненужное, а все нужное писать так что бы было максимально быстро. вот после таких оптимизаций логика реализованая в БД может быть весьма тяжело читаемой.
                                Впрочем, причины могут быть и более тривиальные — в целях экономии времени в процедуру\функцию\триггер вставляют if else с нужным кодом, вместо того что бы переписать процедуру под новые требования. И со временем почти вся процедура слеплена из таких кусков, и без вдумчивого дебага уже непонять что там происходит. Но почему такое происходит не загадка — лень и дедлайны, на то что бы сделать по нормальному, а потом еще и провести хорошее тестирование может потребоваться слишком много времени. Справделивости ради стоит сказать что такое характерно и для ООПшных языков программирования.
                                В общем, возвращаясь к исходной точке — для БД либо шашечки, либо ехать (либо принципы ООП, либо скорость выполнения)
                                З.Ы. это моя ИМХА за 13 лет собраная на своем и опыте коллег
                                  0
                                  Все верно, только добавлю, что параллельно может происходить еще и наращивание функционала в коде сервера, примерно по тем же причинам. В итоге имеем ктулху-код в, например, java, ктулху-код а БД, и все это как бы один непрерывный кусок логики, разбираться в котором ну никак не из мечты.
                        0
                        Работаю в компании, где используется самописная ORM. Ну за основу был взят довольно древний проект под названием Quasar. И мы генерируем схему из классов с аннотациями похоже, как в Hibernate. Т.е. у нас code first. Перед стартом система сравнивает схемы — что у нас в БД и что в классах и делает апдейт базы данных при необходимости. Есть механизм, где можно сделать дополнительные действия при обновлении схемы — копирование/переформатирование данных. Действия накапливаются, т.е. можно сделать обновление с перескоком через несколько версий. Используется это все в разных продуктах, которые состоят из модулей и разные комбинации модулей могут приводить к разным схемам БД — поскольку там разные наборы объектов. Ну и поддерживается Oracle, MS SQL и Sybase. И я бы сказал что работает это при таком разнообразии практически без проблем. Я не в качестве рекламы это пишу. Поскольку мы эту ORM никому не продаем. Но code first тоже может замечательно работать.
                          0
                          Ну и поддерживается Oracle, MS SQL и Sybase

                          Т.е. поддерживается только какая-та общая функциональность всех этих СУБД? (Без специфичных опций каждой СУБД)
                            0
                            Да где то так. Ну подобные, но отличающиеся в деталях вещи поддерживаются, через разные реализации. Но если что-то уникальное для одной СУБД и не существует в других, то конечно нет.
                          0
                          ORM обычно проецируют записи базы данных на объекты, представляющие сущности предметной области. При таком подходе, похоже, толком бизнес-логику в эти объекты не поместить. То есть объектная сторона маппинга будет являться не полноценными сущностями а какими-то DTO и для использования полноценных сущностей нам будет нужен Object Object Mapper, который будет на основе DTO из ORM создавать сущности и наоборот. Я правильно понимаю?
                            +1
                            Я когда-то прочитал книгу Java BluePrints от Sun (был даже перевод на русский язык) в которой был специальный раздел посвященный DAO и там дается понимание такого объекта скорее как прослойки между базой данных и бизнес-логикой с рекомендацией не делать эти объекты слишком тяжелыми. Я этой логике стараюсь следовать и добавляю в эти объекты методы которые делают примерно то что делают триггеры и constraint на стороне базы данных. То есть по минимуму.
                              0
                              Вот, да, напомнили название паттерна DAO. То есть по сути всю мощь ORM типа Hibernate со всеми их DataMapper, IdentityMap, UnitOfWork и т. п. предлагается использовать для довольно простого DAO, а также вводить дополнительный маппинг с DAO на сущности предметной области или отказаться от использования сущностей, перенося бизнес-логику в сервисы, очень часто нарушая присущую предметной области инкапсуляцию сущностей.
                            +3
                            У подхода DB-first есть недостаток: нельзя создавать произвольные типы и добавлять в них произвольные свойства (например права доступа для разных групп, валидации и сообщения об ошибках, имена на разных языках для отображения на форме, и т.п.) Поэтому последнее время использую подход, котрый можно назвать Structure first: свойства полей и структуры таблиц описаны в json-файле, на основании которого самописный скрипт генерирует код на разных языках (SQL, PHP, Typescript, etc). Основная идея состоит в том, чтобы если надо добавить какое-то поле в таблице, то делается это один раз в json-файле, а скрипт создает все базовые структуры данных и абстрактные классы default-форм, которые потом кастомизируются наследованием.
                              +1
                              Возможно вам стоит взглянуть на liquibase.
                              0
                              Если кто-нибудь может рассказать о проблемах миграции изменений в code first решениях — расскройте пожалуйста, что же автор имел ввиду?
                              Я не понимаю как ситуация принципиально ухудшается при использовании ORM ведь всегда можно изменения внести в production и ручным способом (как в database first). У тебя же никто не забирает любимый мячик, а просто еще один подкидывают (сгенерированные миграции для простых случаев).
                                0
                                Я как раз чуть ниже сделал комментарий о том что есть удобные инструменты для автогенерирования миграций. Я знаю php/symfony/doctrine. Но к сожалению по своему удобству я ничего не могу найти под node.js. То есть проблем нет если есть хороший инструмент такой как docrtine
                                  0
                                  Вероятно, имеются в виду инструменты миграций, который позволяют генерировать или создавать только миграции, довольно сильно абстрагирующиеся от конкретного движка SQL и не позволяющие добавлять в них произвольный SQL. С ними для поддержания целостности нужно использовать ещё один механизм миграций (ручным способом в CI/CD практиках делать ничего нельзя обычно) и синхронизировать их между собой, что больно. Или отказываться от автогенерации в принципе и все миграции делать не штатным механизмом ORM, а более низкоуровневым, оставляя на себе ручную синхронизацию метаданных ORM и схемы, что тоже не очень приятно.
                                  0
                                  Идея database-first в самом общем смысле очень мне нравится. Встречался со случаями когда раработчики сразу начинают «мыслить объектами». В результате чего полученная схема базы данных не выдерживает никакой критики. Тут уже могут быть варианты или сначала разработать структуру и потом перевести ее в объектную схему или же продолжать «мыслить объектами» но параллельно подверять какую структуру базы данных это будет генерировать.

                                  По кодогенерации сам пользуюсь php / symfony / doctrine. Очень удобно делать миграции. Т.к. сравнивается текущая структура базы данных и генерируется код который из текущей струткуры преобразует в нужноую структуру. Кстати был (и есть) у меня один проект который разработчики начали базу менять без автогенерируемых миграций и на тот момент кодга я это проект принял база уже в нескольких объектах разошлась с кодом. (В основном оставались лишние поля и таблицы) После этого првел все один раз в порядок и пользуюсь только автогенерируемыми миграциями.

                                  Но например я не знаю аналогичной функциональности для node.js. Аналогично я как понял из статьи что аналогичного функционала нет и в java ORM-ках.
                                    +2
                                    При мышлении объектами, эти объекты никак не должны влиять на схему данных в базе при использовании DataMapper паттерна. Увы, та же Doctrine не позволяет малой кровью маппить некоторые распространенные схемы типа наличия у сущности поля, содержащего значение, являющееся коллекцией ValueObject на неподдерживаемые схемы типа отдельную таблицу для таких ValueObject, да даже простого хранения ValueObject в отдельной таблице, только встраивание, да и то с ограничениями на, например, null значения. А если пойти путём доработки маппинга Doctrine (есть возможности расширения), то теряем возможность автогенерации, надо, как минимум, её дорабатывать, а с ней ещё больше проблем может вылезти, в частности github.com/doctrine/dbal/issues/1110 — больше трех лет висит и только несколько дней назад вроде появился PR, решающий проблему. Ну и ручной маппинг остаётся, но врядли кто будет писать для него полноценные миграции.

                                      0
                                      А если пойти путём доработки маппинга Doctrine (есть возможности расширения), то теряем возможность автогенерации


                                      Все записит от того как дорабатывать маппинг. Например когда мне потребовались json документы то я заюзал github.com/dunglas/doctrine-json-odm Все ралидовант там так что автогенерация работет правильно (даже не могу понять почему т.к. код бандла очень лаконичный и не имеет прямого выхода на генерацию SQL видимо автор очень удачно использовал какие-то умолчания действующие в doctrine)
                                      0
                                      Аналогично я как понял из статьи что аналогичного функционала нет и в java ORM-ках.


                                      Почему же, Hibernate умеет.

                                      Еще есть всякие интересные штуки (https://github.com/perseas/Pyrseas и github.com/CourseOrchestra/2bass), которые позволяют выгрузить в какой либо формат, подправить там что нужно, а потом эта штука найдет изменения и сгенерит нужные alter'ы.
                                        0
                                        Собственно Doctrine 2 можно назвать портом Hibernate на PHP.
                                          0
                                          Я не знаю насколько удобны эти средства в hibernate и знаю что ниего подобного нет к сожалению под node.js. Я просто расскажу почему это удобно в doctrine
                                          1. Вы изменяете программный код Entity
                                          2. Вы запускаете консольную команду которая генерирует скрипт с двумя функциями up() и down() сравнивая вашу текущую реальную базу данных с Entity
                                          3. Вы по желанию добавляете дополнительно свой код (например задаете значения для добавленных полей и т.п.)
                                          4. Вы при деплое запускаете скрипт миграции
                                          5. Скрипт запускает все текущие миграции в порядке их определения. И следит чтобы это сделано было ровно один раз.
                                          6. Также можно откатить миграцию.

                                          То есть вопрос не в том что механизм миграций есть, а в том что он надежный и удобный. Если бы я не был уверен в каком-то из шагов что он пройдет без проблем я бы не рискнул пользоваться встроенными средствами.

                                          Я не утверждаю что этого нет нигде больше. Я просто хочу показать в каких случаях это действительно бывает удобно. Я например сейчас в процессе поиска чего-то подобного на Go и если кто-нибудь значет где модно найти подобный встроенный функционал то прошу подсказать.

                                          WARNING: AutoMigrate will ONLY create tables, missing columns and missing indexes, and WON’T change existing column’s type or delete unused columns to protect your data.


                                          Вот например доки к GORM. И в чем тут прикол? Что вам фреймверк перестроит схему ничего не гарантируя и н давая контролль над процессом
                                            0
                                            Я достаточно много работал с Doctrine. Увы, этот флоу годится только для простейших случаев. И за сгенерированными миграциями нужен глаз да глаз. тут уже кидал ссылку на issue 2015-года, который ещё не закрыт — каждую миграцию приходилось «чистить» вручную из-за того, что доктрина не могла корректно сравнить текущую схему и схему из метаданных в PostgreSQL и на каждую генерацию упорно добавляла CREATE SCHEMA public, а на откат DROP.

                                            Ну и в целом при работе с Doctrine нельзя просто забыть, что под капотом СУБД. Нужно помнить и о СУБД, и о самой Doctrine. И о том, что Doctrine не позволяет описать точную схему целевой БД. А иногда настойчиво мешает, пытаясь удалять то, что она не видит в своем описании, но что нужно нам, и наоборот.
                                              0
                                              Тут уже вопрос такой. Если я пользуюсь doctrine то полагаюсь на ее внутренние механизмы и не добавляю ничего не предусмотренного (А там есть многое из необходимого. Например, те же переводы, работа с датами). Если мне чего-то не хватало в доктрине то я искал бандл который дополняет эту функциональность.
                                              «Лишние» миграционные скрипты у меня тоже когда я принял проект были. Потом посмотрел почему этот код появлялся. В некоторых случаях просто добавил нужную аннотацию в Entity. В некоторых случаях рабобрался что та таблица которую предлоагали удалить дйствительно нигде не исползуется ( и к том же была пустая). Но если уже перезодить на «ручное управление» — то тогда все преимущества превращаются в недостатки. Т.к. где-то появилось напрмер очень важное но отсутсвующее в Entity поле и ничего не ведающий человек которому получили таск очередной раз забудет закомментировать удаление этого поля в миграции. Ничего зхорошего. Одни только траблы.
                                                0
                                                1) как минимум есть зафиксированные баги без подходящих в конкретной ситуации путей обхода и на один из них мы попали. Пытались исправить, но через несколько дней даже оценки не было сколько ещё времени понадобится.

                                                2) речь прежде всего не о полном ручном управлении, а о полноценном ревью сгенерированных миграций и создания своих миграций, тюнящих схему вне возможностей доктрины.

                                      0
                                      Подход DB first имеет смысл применять, по-моему, только если изначально понятно, что проект потребует очень много низкоуровневой работы с СУБД или у БД будет несколько логических владельцев, каждый из которых будет кроить схему под свои нужды и нужно будет синхронизировать объектные или подобные модели между владельцами.

                                      В остальных случаях это будет преждевременная оптимизация, преждевременное решение потенциальных проблем. Когда подобные проблемы появятся, тогда и надо будет думать о путях их решения, в том числе как один из вариантов переход на DB first подход для владельцев, переход на единого владельца схемы БД — не обязательно, кстати, владельцем базы должна быть сама база или отдельный проект, можно взять одного из существующих владельцев, сделав его авторитарным для остальных, заставляя N-1 клиентов СУБД синхронизироваться с 1 клиентом явно или через внесённые им изменения в схему, или вообще запретив им работать с базой, а только с API единственного владельца. Всяко лучше, чем синхронизация «всех-со-всеми», даже когда только 2-3 владельца появляются.
                                        +1
                                        проблемы с code-first это перформанс базы если она не оптимально згенерилась, но база обычно скейлиться дешевле чем потраченное время на разработку кода который лишь отдалённо соответствует бизнес логике, а для новичков время вживания в проект может сильно затянуться и будет жопа если основные девелоперы вдруг решат уйти на другую работу, вроде спека бизнес логики есть, а что и почему так мапиться хрен разберёшь
                                          0

                                          База обычно скейлиться дешевле? Весьма смелое утверждение.
                                          Для кода практически всегда есть опция запустить несколько экземпляров, спрятать их за какой-нибудь фасад, вроде балансировщика и писать/читать в одну базу. А вот что делать, если вертикальный предел по железу достигнут, а производительности хранилища по-прежнему не хватает — это большой-большой вопрос.
                                          И если в случае с какой-нибудь монгой еще остаётся пространство для манёвра, то что делать, если датасет из сильно реляционных данных не входит — не очень понятно.

                                            +1
                                            репликация, шардинг, использование совершенно другой технологии для хранения данных, вообщем куча способов повысить производительность, некоторые очень просто сделать некоторые чуть сложнее, что все так бояться скейлить базу я не пойму, ведь это должно быть заложено в архитектуре проекта, возможность расширяться и переезжать на другие технологии, а не затачивать весь проект под конкретное решение, разве нет?
                                              0
                                              что все так бояться скейлить базу я не пойму, ведь это должно быть заложено в архитектуре проекта, возможность расширяться и переезжать на другие технологии, а не затачивать весь проект под конкретное решение, разве нет?


                                              Не поспоришь, но много БД были спроектированы и разработаны десятки лет назад, когда всех этих модных слов еще не было (а если были, то не так широко распространены как сейчас). А данные в таких БД составляют оргомную ценность. Вот и приходится работать с такими динозаврами.
                                              0
                                              > База обычно скейлиться дешевле? Весьма смелое утверждение.

                                              Сильно зависит от предметной области, бизнес-задач и ограничений, а так же от уровня протечки абстракций над базой данных в слой модели предметной области. Скажем, есть много задач, где легко скейлить чтение путем простой асинхронной master-slave репликации.
                                            0
                                            Я полностю согласен с автором статти. Я само хотел написать что-то похожее, но изложение мислей не мой конек. Возможно когда ви проектируете игру — база даних и не стоит на первом месте. Но даже в етом случае если начать проектировать с БД можнополучить профит. Но если ви проектируете програму для бизнеса — итог ее работи практически всегда должен бить: коректние, согласование дание в БД.
                                              0
                                              Можно получить профит и если сначала проектировать решение нужных пользователю или бизнесу задач, а лишь потом прикручивать базу для хранения данных, необходимых для решения этих задач. Обычно «записать данные в базу» или «прочитать данные из базы» не является задачей ни пользователя, ни бизнеса, они о ней знать не знают, если им не втюхавали какой-нибудь Оракл. База для них инкапсулированная деталь реализации, то есть не существует.

                                              Если заведомо не известно, что задача потребует очень больших скоростей записи и чтения данных, их поиска и агрегации, с которыми без БД не справиться за разумное время, и то каждый байт в схеме должен быть максимально оптимизирован, то начинать проектировать с БД очень похоже на попытку преждевременной оптимизации.

                                              P.S. Что будут меняться бизнес-требования к разработке — это практически аксиома, что будут проблемы из за не того начала разработки, в частности от бизнес-задач, а не от базы данных — это ещё бабушка надвое сказала.
                                                0
                                                Я и не спорю. Сначала бизнес-задача, ТЗ. Так и стаття о том как проектировать програму, а не решать бизнес задачу. Ето уже подразумевает наличие ТЗ в том или ином виде.
                                                Если заведомо не известно, что задача потребует очень больших скоростей записи и чтения данных, их поиска и агрегации, с которыми без БД не справиться за разумное время, и то каждый байт в схеме должен быть максимально оптимизирован, то начинать проектировать с БД очень похоже на попытку преждевременной оптимизации.

                                                Вот так и рождаются програмы с которими работать не возможно. Я обясню что я имею ввиду, говоря правильная програма: Откритие журналов -до 3 с, отркритие документа до 2 с, Отчети до 5с (в некоторих очень редких случаях до 40с иначе регулярние отчеты и OLAP). И неважно что у вас несколько лет в БД, в таблицах милиарды строк и размер в терабайтах.
                                                Приступая к разработке задачи ви должни четко понимать какой обем может быть через год и через 5 лет с учотом роста компании. И здесь нет преждевременной оптимизации, Разници в скорости проектирования модели в БД или JAVA\C#\… нет в принципе. Ибо нужно проектировать структуру учитивая возможние изменения ТЗ, и разширение БЛ.
                                                И как уже говорилось — сгенерировать клас на основании обекта БД проще-простого.
                                                Здесь беда в другом сейчас слишком много разработчиков и груп разработчиков, которие вообще не хотят заморачиватся елементарними знаниями БД. И считают что програма должна бить БД независимая. И ето чуть ли не ее главная фича.

                                                P.S. И да, я старовер. Я не люблю ORM, ибо не представляю что под капотом, и что он сгенерит, и я считаю что реализация бизнес логики на БД наиболее правильное место(самое ефективное). Приетом скорость разработки и скорость работи више.
                                                Вот вам аргументи
                                                1) Поддерка целосности на БД делается елементарно.
                                                2) БД неплохо так паралелит задачи.
                                                3) БД (Оракл) имет так званий result cache для запросов и функций, которий отслеживает изменения таблиц и всегда видает достоверний результат. При етом доступно всем пользователям. Я даже не представляю сколько нужно потратисть усилий чтоб достичь такого результата самостоятельно.
                                                4) Ну и в Pl/sql есть возможнось компиляции в нативний код.
                                                Из минусов только GUI для C#/JAVA покруче и тесты писать сложнее. Отладка кода на БД ничем не хуже/сложнее чем в VS.

                                                  +1
                                                  Отладка кода на БД ничем не хуже/сложнее чем в VS.

                                                  А какой отладчик PL/SQL нормально показывает коллекции? plsqlDeveloper не умел, когда я держал его в последний раз.
                                                    0
                                                    Да целую колекцию не посмотриш. Только елемент. Даже FIRST елемент не покажет.
                                                    Етому есть наверное логичное обеснение.
                                                    Согласен. отладка в VS намного удобнее и мощнее.
                                                    С етим пунктом я ввел в заблуждение. Можно сказать что отладка присутствует но ограничена.
                                                      +1
                                                      Да целую колекцию не посмотриш. Только елемент

                                                      Ну, вот и закончился миф «Отладка кода на БД ничем не хуже/сложнее чем в VS» не успев начаться.

                                                      Можно сказать что отладка присутствует но ограничена.

                                                      Сразу вспомним, что в случае Оракла с его pl/sql мы еще на уровне БД имеем сплав двух языков — императивного pl/sql и декларативного sql, при этом худо-бедно дебажится только первый, дебага sql не предполагается. Все это мягко намекает, что утверждение
                                                      реализация бизнес логики на БД наиболее правильное место

                                                      несколько сомнительно.
                                                        0
                                                        декларативного sql
                                                        дебага sql не предполагается

                                                        У меня даже теоретической идеи нет как можно дебажить декларативний язык.
                                                        План запроса ето и есть, как по мне, его дебаг. И чуть ли не базис скорости работы.
                                                        реализация бизнес логики на БД наиболее правильное место

                                                        несколько сомнительно.


                                                        Я даже скажу больше очень немодно. :) и ета мысль часто жостко минусуется на хабре. Но тем неменее, в целом код написаний на ХП короче аналогичного JAVA\C# значить проще тестится. Проще поддерживается, В случае простых изменений не требует переинстеляции на клиенских машинах. Значит изменения можно внедрять быстрее.
                                                        И как я уже писал ниже я не агетирую за БЛ на ХП я агитирую Что выбор Базы и знание нюансов не менне важен за выбор язика програмирования. А по большому счету даже важнее.
                                                          +1
                                                          У меня даже теоретической идеи нет как можно дебажить декларативний язык.
                                                          План запроса ето и есть, как по мне, его дебаг. И чуть ли не базис скорости работы.

                                                          Речь шла про удобство и ограниченность дебага. Ну, вот оно, ограничение — прямо на дебаге понять почему тормозит запрос невозможно.

                                                          Но тем неменее, в целом код написаний на ХП короче аналогичного JAVA\C# значить проще тестится

                                                          *глядит в портянки pl/sql, потом в свой уютненький java-код* Ээээ… Ладно, проехали :D

                                                          выбор Базы и знание нюансов не менне важен за выбор язика програмирования.

                                                          С этим никто спорить и не думал.
                                                            0
                                                            Речь шла про удобство и ограниченность дебага. Ну, вот оно, ограничение — прямо на дебаге понять почему тормозит запрос невозможно.

                                                            Никогда в таком русле не думал. Всегда смотрел план отдельно в Тоде там ето получше сделано. Согласен прям в дебагере — удобнее.
                                                            Я согласен — дебаг и GUI похуже Студии еклипса и тд.
                                                            *глядит в портянки pl/sql, потом в свой уютненький java-код* Ээээ… Ладно, проехали :D

                                                            В девелопере достаточно удобная навигация по коду. Ну а портянки ето уже стиль :) а не маст хев. Но ето уже кому что глазу мелее. Мне например не нравится в JAVA\C# многословие в описаниях класа (public, і тд.). Спасибо хотя б в JAVA var добавили :) Наследование, интерфейси итд часто отвлекают от сути, и получается что обертка становится важнее сути. Не подумайте что я не люблю ООП. Я очень любил с++, Сейчас пишу на c#. Но заглянув в grid(C#), я больше не задавался вопросом почему он такой тормознутый. Мне кажется что ето уже слишком. Я понимаю что там все красиво, канонически. Но работать бысто ето не может… Да Я не знаю как сделать так же уневерсально и быстро. Наверно ето и невозможно. Поетому и появлялись пачками самописние grid. Но ето уже другая история.
                                                              +2
                                                              многословие в описаниях класа (public, і тд.).

                                                              То есть public — это многословно, а разделение pl/sql пакета на спеку и тело, в которых код практически дублируется — это не многословно? :D А куча begin'ов и end'ов вместо скобок? Код на pl/sql совсем не лаконичный. Кстати, с появлением в java'е стримов перебирать коллекции стало одно удовольствие.
                                                            0
                                                            Но тем неменее, в целом код написаний на ХП короче аналогичного JAVA\C#

                                                            А можно пример?) А лучше парочку.

                                                              0
                                                              Какая информация на хабре проскакивала несколько раз в холиваре БЛ БД или вне БД.
                                                              Мы все ето вместе можем легко проверить. Вы предлагаете сценарий БЛ и реализацию JAVA\С#. Я код PL\sql. Желательно что то посложнее, ибо 90% логики можно реализовать оператором (select\merge\update\insert) или их групой. Но сразу оговорим, что задача должна включать многопользовательский режим, и возможность совмесной работы и контроль прав ограничения на даные.
                                                                0
                                                                Я правильно понял, что данный кусок кода должен включать И работу с данными, И «многопользовательский режим», что бы это ни значило, И контроль прав? Эту шутка такая? Три совершенно разные обязанности в одном куске кода могут встретиться только в виде обращений к трем нижележащим подсистемам реализующим эти обязанности, то есть в виде:
                                                                checkRight();
                                                                result = getData();
                                                                  0
                                                                  Мы уже дискутировали с michael_vostrikov по поводу БЛ в БД, я ему на простейшем примере показал насколько код в БД работает быстрее и насколько он проще
                                                                  habr.com/post/312134/#comment_9853420

                                                                  И это всего лишь одно место. А так вся программа как комплекс может работать в десятки раз быстрее, если она реализована в БД, прекрасно масштабироваться и не требовать скалинга очень долгое время.
                                                                    0
                                                                    насколько код в БД работает быстрее

                                                                    Что значит «быстрее»? В контексте всего приложения может запросто получиться так, что имея локальный максимум производительности в БД вы в итоге получите глобальный минимум, поскольку бутылочным горлом станет коннект к БД.

                                                                    А так вся программа как комплекс может работать в десятки раз быстрее, если она реализована в БД

                                                                    Это ровно до тех пор, пока вся задача программы замыкается на данных из БД. То есть пока это «вещь в себе», к которой изредка кто-то обращается снаружи и просит дать кусочек результата работы — да, будет быстро и не напряжно. Все сломается об колено, как только появится потребность взаимодействовать с внешним миром, забирая оттуда данные, и совершая над ними действия, которые плохо ложаться в термины БД. Как на хранимках написать логику: «пойди к менеджеру ресурсов, возьми у него адрес актуального на данный момент поставщика данных, сходи к поставщику, запроси по websocket соединение, подпишись на такое-то событие, и жди его наступления в течение пяти секунд, после чего обработай полученные данные и сообщи, что они получены. Если за 5 секунд событие не наступило, то тихо закрой коннект и… ну, и так далее». Я, конечно, представляю, что все это можно реализовать через Аляску, с маппингом всего и вся в таблички, но поддерживать и развивать ЭТО будет невозможно.
                                                                      0
                                                                      Как на хранимках написать логику: «пойди к менеджеру ресурсов, возьми у него адрес актуального на данный момент поставщика данных, сходи к поставщику, запроси по websocket соединение, подпишись на такое-то событие, и жди его наступления в течение пяти секунд, после чего обработай полученные данные и сообщи, что они получены. Если за 5 секунд событие не наступило, то тихо закрой коннект и… ну, и так далее».

                                                                      В Оракле всякую экзотику, которой в приложении не более 5%, типа вебсокетов, можно написать на java, а обработку данных сам бог велел делать на SQL и PL/SQL
                                                                        +1
                                                                        Откуда данные про 5%? Это выдавание желаемого за действительное — вся эта «экзотика» может занимать изрядную долю приложения.

                                                                        можно написать на java, а обработку данных сам бог велел делать на SQL и PL/SQL

                                                                        А почему бы не пойти дальше, и не написать на той же java за пределами Оракла? Нагружать сервер базы данных еще и работой jvm? Зачем? И чем вы будете пользоваться в «java over oracle» для менеджмента соединений? Куда будете логгировать? Все туда же, на сервер базы данных? А зачем себя так не любить, если можно взять Spring Boot, который замечательно запустится на соседнем сервере и не будет нагружать БД? Сделав этот первый логический шаг совершенно внезапно оказывается, что в java есть стримы, которые замечательно реализуют перебор коллекций, то есть то, что до сих пор делал pl/sql. Вот и БЛ переехала из хранилища данных поближе к внешним запросам от пользователей, что логично. Ну, и так далее… Это эволюция, времена, когда сервера приложений были дорогим и сложным удовольствием, а pl/sql и иже с ним простым и дешевым, прошли и теперь можно вернуть БД изначальное значение — хранить данные, поскольку для их лобработки уже есть более удобные средства.
                                                                          0
                                                                          Только когда всё это начинает тормозить, то выясняется что обработка данных на сервере, там где они хранятся, гораздо быстрее и логика опять переползает в БД или начинается горизонтальный скалинг и монструозная инфраструктура с балансировщиками нагрузки, на поддержку которой тратиться неимоверное количество ресурсов.
                                                                            0
                                                                            Почему оно должно тормозить? БД сама по себе нагружает сервер, в то время, как голое Spring Boot приложение жрет около 15 мегабайт оперативки и 0 процессора. Запущенное на железе сопоставимом с сервером БД тормозить оно начнет весьма и весьма не скоро. Гораздо чаще мне попадались случаи, когда запросы вставали в очередь к БД и ждали, пока там раскочегарится pl/sql, поворочает данными, и таки уже вернет пару значений.
                                                                      0
                                                                      Извините, но вы не показали. Ни что проще, так как я не стал на ORM переписывать, так как разговор был про производительность, ни что быстрее, так как я добавил стороннее кэширование, что с базой сделать проблематично. Я тот репозиторий уже удалил, но если интересно, могу переписать на ORM, и сравним читаемость.
                                                                        0
                                                                        Кеширование это лишние костыли которые, как я писал, могут привезти к ошибкам, которые даже юнит тестами не покроешь.
                                                                        А в Оракле прекрасно кешируются данные RESULT_CACHE или MATERIALIZED VIEW в концепции которых нет места изменениям кода бизнес логики, а значит самым критичным будет сломанный кеш, а не сломанные данные.
                                                                          0
                                                                          Кеширование это лишние костыли которые, как я писал, могут привезти к ошибкам, которые даже юнит тестами не покроешь.

                                                                          Кеширование — это не костыль, а потребность. Правильно выбранный кэш решает кучу проблем. К примеру hazelcast нам дает не просто кэш, но распределенный кэш. И это прекрасно!
                                                                            0
                                                                            решать-то проблемы он может и решает, но этих проблем может и не возникнуть когда у вас приложение и без кеша работает прекрасно. И вместо того чтобы тратить время и ресурсы на поддержание логики кеша, Вы тратите время на разработку приложения.
                                                                              0
                                                                              Скажу честно, на поддержание работы хазелькаста у меня уходит ресурсов в разы меньше, чем уходило на поддержание схемы БД. Собственно когда окончательно достало, то OracleXE, который мы чисто по привычке вклячили в приложение, был выкинут нахрен, данные переведены в json и складированы в редиску, откуда забираются в хазелькаст, откуда уже заходят в код. С тех пор в моей душе мир и покой, поскольку хранение данных стало для меня прозрачным, понятным, а главное не требующим внимания. Понимаю, что мой случай не показатель, все же данных у меня, как таковых, не много — тысячи и десятки тысяч записей, а не миллионы и миллиарды, но, сдается мне, почти все приложения именно таковы…
                                                                                0
                                                                                Рад за Вас. А как в этой связке ACID? Просто не знаком ни с Redis, ни с хазелькастом.
                                                                                  0
                                                                                  А как в этой связке ACID?

                                                                                  Да так же, как везде — работает, пока не случается какой-нибудь вывернутый случай :D Если серьезно, то, к примеру, если мне нужно изменить какую-то сущность, то я вынимаю ее из hz, лочу, что б никто в параллельном потоке в это время мне не мог помешать, меняю все, что надо, укладываю обратно в hz. Разлочиваю. На всякий случай лок сам снимется через, к примеру, 500 миллисекунд, мало ли что-то встало колом. Как-то так. Нужно проапдейтить разом несколько сущностей — сначала лочу их все, потом все разлочиваю. try{}finally{} тут помогает. А редиска тут только для персистинга, на случай полного выключения сервера — где-то же данные нужно хранить долговременно…
                                                                                    0
                                                                                    А как с согласованностью на чтение? Например, я открыл цикл по данным за вчерашний день, прочитал 1ю строчку, в этот момент кто-то изменил 2ю строчку. Мой цикл извлечёт значение на момент старта цикла или уже изменившееся?
                                                                                      0
                                                                                      Итерирование будет происходить по тому результату, который вернула вам операция чтения. Если изменение произошло после этого, то вы его увидите только перечитав данные. Что, как мне кажется, логично.
                                                                                        0
                                                                                        Ну условно пример, задача посчитать бух баланс предприятия, который должен быть равен 0. Делаем запрос по всем счетам и суммируем все проводки. Оракл гарантирует, сколько бы долго не выполнялся запрос, что данные будут согласованными и транзакции на изменение данных, которые были выполнены после запуска запроса, не повлияют на результаты запроса, более того извлечение данных в версионных СУБД не блокируют модификацию данных.
                                                                                        А я так и вижу логику приложения на Java, написана функция для получения суммы проводок по счёту. Далее открываем цикл по счетам и получаем этой функцией сумму по счёту, потестировали, суммируем всё хорошо, кинули в продакшн. Потом показывают отчёт где баланс не сходится. Потому что в многопользовательской системе пока в цикле мы посчитали первый и второй счёт, а вторая сессия уменьшила сумму первого и увеличила сумму третьего. Изменения 3го счёта попадут в отчёт, а 1го не попадут. Приходим к выводу что на время выполнения отчёта надо блокировать всё и запрещать модификацию данных.
                                                                                          0
                                                                                          Приходим к выводу что на время выполнения отчёта надо блокировать всё и запрещать модификацию данных.

                                                                                          Нет, не так. В вашей логике вы строите отчет по тем же данным, которые кто-то меняет, а это не так. Вы атомарно получили данные, у вас есть датасет, и вы по нему строите отчет никому не мешая и вам никто не мешает.

                                                                                          В конце-концов речь идет о продакшн-реди инструменте, который показал себя на больших проектах, хотя бы у Яндекса: habr.com/company/yamoney/blog/332462
                                                                                            0
                                                                                            Нет, не так. В вашей логике вы строите отчет по тем же данным, которые кто-то меняет, а это не так. Вы атомарно получили данные, у вас есть датасет, и вы по нему строите отчет никому не мешая и вам никто не мешает.

                                                                                            Что-то я не понимаю… т.е. сначала заблокировать всю базу, извлечь ВСЕ данные из БД, т.к. я могу не знать какие данные мне понадобиться, разблокировать БД, а потом уже строить отчёт по извлечённым данным…

                                                                                            Я хочу сказать что, запрос
                                                                                            SELECT УИД
                                                                                            , CASE
                                                                                               WHEN GROUPING(НАИМЕНОВАНИЕ) = 1 THEN 'Итого:'
                                                                                               ELSE НАИМЕНОВАНИЕ
                                                                                              END НАИМЕНОВАНИЕ
                                                                                            , SUM(ОПР_СУММУ_БУХГАЛТЕСКОГО_СЧЁТА(УИД)) СУММА
                                                                                            FROM БУХ_СЧЕТА
                                                                                            GROUP BY ROLLUP ((УИД, НАИМЕНОВАНИЕ))
                                                                                            

                                                                                            в Оракле всегда даст ожидаемый результат, а если функцию ОПР_СУММУ_БУХГАЛТЕСКОГО_СЧЁТА написать на java, которая может оперирует данными из 100 таблиц, то пока мы не заблокируем все эти таблицы на момент исполнения, то мы не добьёмся согласованных данных и в строчке «Итого:» мы можем получать неожиданные данные.
                                                                                              0
                                                                                              Что-то я не понимаю… т.е. сначала заблокировать всю базу, извлечь ВСЕ данные из БД, т.к. я могу не знать какие данные мне понадобиться, разблокировать БД, а потом уже строить отчёт по извлечённым данным…

                                                                                              Зачем что-то блокировать на чтение? За тебя все сделает хазелькаст ровно так же, как делает Оракл. Блокировать что-то нужно на изменение. В общем очевидно, что я могу только отправить к документации.

                                                                                              Кстати, ситуация когда «т.к. я могу не знать какие данные мне понадобиться» явно показывает, что что-то ты делаешь не так…
                                                                                                0
                                                                                                Кстати, ситуация когда «т.к. я могу не знать какие данные мне понадобиться» явно показывает, что что-то ты делаешь не так…

                                                                                                Нет, всё не так. Я же не знаю какие данные и таблицы под капотом у функции ОПР_СУММУ_БУХГАЛТЕСКОГО_СЧЁТА, да мне и не нужно этого знать

                                                                                                Зачем что-то блокировать на чтение? За тебя все сделает хазелькаст ровно так же, как делает Оракл. Блокировать что-то нужно на изменение. В общем очевидно, что я могу только отправить к документации.

                                                                                                Я вроде не говорил что на чтение, на запись конечно. И Оракл ничего не блокирует при чтении. Ну т.е. мы пришли к ситуации что для получения сводного согласованного отчёта по всем данным надо заблокировать за запись всю БД.
                                                                                                  0
                                                                                                  Нет, всё не так. Я же не знаю какие данные и таблицы под капотом у функции ОПР_СУММУ_БУХГАЛТЕСКОГО_СЧЁТА, да мне и не нужно этого знать

                                                                                                  Я про ситуацию, когда «запрашиваю все, потому что не знаю, что из этого мне надо».
                                                                                                  Ну т.е. мы пришли к ситуации что для получения сводного согласованного отчёта по всем данным надо заблокировать за запись всю БД.

                                                                                                  Никуда мы не пришли, это вы упорно натягиваете сову на глобус. На запись что-то блокирует для себя программист, точно так же, как блокирует любые объекты в конкуррентном окружении — Lock, CountdownLatch, и прочие мютексы с семаформаи делают ровно это. Ровно в этой парадигме программист лочит объекты Хазелькаста, лочит заботясь о себе, а не о Хазелькасте и его внутренней согласованности данных. То есть лочить прогер должен когда хочет монопольно изменять объект ковыряя при этом пальцем в носу. Случай если кто-то что-то запросил на чтение, и в этот момент кто-то что пишет Хазелькаст разруливает самостоятельно точно так же, как Оракл, прозрачно для программиста.
                                                                            0
                                                                            Логика в базе это лишние костыли которые могут привезти к ошибкам, которые даже юнит тестами не покроешь.

                                                                            Дело же не именно в кэшировании, а в управлении кодом. Да, результат запроса вы закешируете. А результат вызова Web-API уже нет.

                                                                            Сломанные данные это ошибка в логике. И как раз логикой проще управлять на специализированных языках.
                                                                              0
                                                                              Логика в базе это лишние костыли которые могут привезти к ошибкам, которые даже юнит тестами не покроешь.

                                                                              Утверждение которое ни имеет ничего общего с реальностью. В комментариях к своей статье я писал как элементарно написать юнит-тесты к логике в mysql
                                                                              habr.com/post/312134/#comment_9849654
                                                                              в Oracle использую utPLSQL

                                                                              Сломанные данные это ошибка в логике. И как раз логикой проще управлять на специализированных языках.

                                                                              Опять какое-то голословное утверждение. чем SQL не специализированный язык? Как раз специализированный язык для обработки данных
                                                                                0
                                                                                чем SQL не специализированный язык? Как раз специализированный язык для обработки данных

                                                                                Ни в коем случае! Он, конечно, специализированный, но не для обработки — извлечение и обновление это не обработка. Это именно что подготовка data set для дальнейшей работы с ним. Pl/sql ведь так и возник — данные нужно было обрабатывать, а sql мог ими только манипулировать, да и то ограниченно.
                                                                                  0
                                                                                  Вот тут очень люблю цитировать Кайта, SQL самый первый язык для обработки данных и самый быстрый, далее если на нём не можем сделать что-то, то используем PL/SQL ну и далее, по возрастанию времени обработки и сложности программирования.
                                                                                  • если можно, сделай это с помощью одного оператора SQL;
                                                                                  • если это нельзя сделать с помощью одного
                                                                                    оператора SQL, сделай это в PL/SQL;
                                                                                  • если это нельзя сделать в PL/SQL, попытайся использовать хранимую процедуру на языке Java;
                                                                                  • если это нельзя сделать в Java, сделай это в виде внешней процедуры на языке C;
                                                                                  • если это нельзя реализовать в виде внешней процедуры на языке C, надо серьезно подумать, зачем это вообще делать...

                                                                                    0
                                                                                    У Кайта, уж прости, Оракл головного мозга, профессиональный перекос, да и говорил он это все уже давно. В текущих реалиях совет писать хранимку на джаве, а внешку на С, выглядит минимум странно — Spring со своим jdbcTemplate настолько упростил работу с БД, что даже странно смотреть куда-то еще :)
                                                                                  0
                                                                                  Утверждение которое ни имеет ничего общего с реальностью.

                                                                                  Ага, я о том же. У вас тоже обобщенное необоснованное утверждение.


                                                                                  В комментариях к своей статье я писал как элементарно написать юнит-тесты к логике в mysql

                                                                                  Ага, только это не юнит-тесты, а интеграционные, и они точно также пишутся для логики в коде.


                                                                                  чем SQL не специализированный язык? Как раз специализированный язык для обработки данных

                                                                                  Тем, что специализирован он не для написания логики. Как там со структурированием и переиспользованием кода? Объекты, наследование, типизация, ассоциативные массивы произвольной структуры.

                                                                                    0
                                                                                    Ага, только это не юнит-тесты, а интеграционные, и они точно также пишутся для логики в коде.

                                                                                    Самые что не наесть юнит тесты
                                                                                    unit testing — процесс в программировании, позволяющий проверить на корректность единицы исходного кода

                                                                                    Единицей исходного кода в БД может являться функция или процедура.

                                                                                    Тем, что специализирован он не для написания логики.

                                                                                    А по мне так он прекрасно для этого подходит, я в своём примере это показал.
                                                                                      0
                                                                                      Самые что не наесть юнит тесты

                                                                                      Там дальше написано "Это ошибка, поскольку тест не должен выходить за границу класса". У вас работает вся система управления БД полностью.
                                                                                      В общем, то, что вы делаете (работа с тестовой БД), при логике в коде называется интеграционным тестированием, и абсолютно ничем не отличается.


                                                                                      я в своём примере это показал

                                                                                      Если вы про нашу беседу, то мы не сравнивали читаемость. Понятно, что на любом Тьюринг-полном языке можно написать логику обработки. Вопрос только в количестве и качестве написанного (поддерживаемости).

                                                                                        +1
                                                                                        Хорошо, давайте назовём тестирование детерминированных функций БД — юнит тестированием, а не детерминированных — интеграционным тестированием, суть от этого не изменится. Юнит тесты пишутся и используются прекрасно в БД.

                                                                                        Если вы про нашу беседу, то мы не сравнивали читаемость. Понятно, что на любом Тьюринг-полном языке можно написать логику обработки. Вопрос только в количестве и качестве написанного (поддерживаемости).

                                                                                        Для меня язык SQL простой и лаконичный, его просто поддерживать, а что там и как нагорожено в ОРМ одному чёрту известно + надо учить синтаксис конкретной ОРМ + синтаксис меняется от ОРМ к ОРМ + ОРМ в конечном итоге всё-равно создаёт SQL в котором приходится разбираться в случае возникновения проблем
                                                                                          0
                                                                                          а что там и как нагорожено в ОРМ одному чёрту известно

                                                                                          Код ОРМ не является кодом, который надо писать и поддерживать. То есть его кто-то пишет, но это сторонний код, в отличие от бизнес-логики.


                                                                                          надо учить синтаксис конкретной ОРМ + синтаксис меняется от ОРМ к ОРМ

                                                                                          Зачем? Открываем код или доки, смотрим конкретное название и параметры. Ничем не отличается от работы с сотней других библиотек.


                                                                                          Для меня язык SQL простой и лаконичный

                                                                                          "Для вас простой" означает "специализирован для написания логики"?
                                                                                          Он для вас простой потому что вы постоянно с ним работаете. По лаконичности я приводил другие примеры в той статье, кода там гораздо меньше.


                                                                                          ОРМ в конечном итоге всё-равно создаёт SQL в котором приходится разбираться в случае возникновения проблем

                                                                                          Ну возникла проблема, разобрались. Зачем постоянно то на нем писать?

                                                                                            0
                                                                                            У SQL нет версий и диалектов?
                                                                0
                                                                Если вы роль приложения или сервера приложений сводите к тонкой прокладке между пользователем и СУБД, а я роль СУБД свожу к тупому, пускай и быстрому хранилищу, то дискутировать о том какой подход лучше, я особого смысла не вижу из-за явного различия критериев «лучше»
                                                                  +2
                                                                  Если б был идеально правильний вариант «серебряная пуля» все так бы и работали, ну или почти все :). У каждого подхода есть свои плюсы и минусы. Ведь не секрет что на сегоднишний день в БД является узким местом во многих задачах. И оно узко именно через то, что БД используют неправильно, или вибор БД бил ошибочен. Проще всего через неделю, месяц или год когда система становится «ватной» или начнет «залипать» сказать: «Ми же все зделали идеально: используем наимоднейшие технологии и последние фичи языка, абстракции, SOLID, покрыли все тестами, і тд. Ето все криворукее админы БД ведь БД ето только быстрое хранилище, а почему оно не достаточно быстрое ето их забота, пусть разбираются и добавят какието там индексы». А админы разведут руками и скажут — ведь там блокировки, борьба за ресурсы, тисячи запросов вместо одного. И дай бог им общими усилиями найти выход или полувыход из ситуации.
                                                                  Я участвовал в разработках где вся БЛ на БД, и где на БД запрещены ХП (Слава богу хотя foreign key разрешили). Я скажу так, не важно какой путь вы вибрали. Всегда нужно помнить: Каждая БД имеет свой «характер» И то что на MSSQL\Oracle\MySQL\Postgresql\… работает на ура в другой БД может тупить, или вообще не работать. Если у вас OLTP система — не знание етих нюансов ето смерть в продакшине. Я не агетирую за БЛ на БД. Я агитирую за то что разработчик должен знать особенности БД которую будет использовать. И ето значительно важнее для качественного продукта чем знатие всех фич язика или нових фреймворков.
                                                                  P.S. И да для меня для меня скорость и отзывчевость програмы ето один из главнейших критетеев програмы на равне с юзебилити.
                                                                  И я видел людей, для которых отритие документа в 1 минуту уже не проблема, они смирились и считают ето нормой. Видя такой софт грусно становится…
                                                                    0
                                                                    Про характер я согласен, и наверное правильно его учитывать, если задача изначально не подразумевает «кроссбазаданность», но по умолчанию только одно место для его учёта должно быть, по-моему: инфраструктурный уровень приложения, подсистема хранения.
                                                                    +1
                                                                    То о чем эта конкретная статья — это неявно касается конкретно ORM и SQL баз данных. Конечно, последняя версия PostgreSQL очень неплохо поработала над кластеризацией (хотя запись в мастер нужно вести всегда в один по-прежнему) — так или иначе приложения очень быстро упираются именно в производительность базы данных. Как это обычно выглядит процесс. Нужно сделать сайт. Берем фреймверк, ОРМ, не паримся. После этого в какой-то момент все начинает тормозить. И огказывается что все нужно переписывать сначала. Ина совсем других основах. Впрочем. Если посещаемость у сайта нулевая, то все вроде бы довольны. Радота сделана, клиент доволен. Можно в том же стиле дальше продолжать. Другое дело, что когда зайдет высокопроивзоительный проект вдург внезапно оказывается что ни у кого нет наработок в этом направлении.

                                                            Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                                            Самое читаемое