За 20 с лишним лет работы профессиональным разработчиком программного обеспечения я изучил множество языков и инструментов. Конечно, есть еще много языков и инструментов, которые я решил не изучать или никогда не имел возможности. Сожаления? У меня было несколько…

Я не скрывал, что в основном я разработчик-самоучка. Хотя я понимаю, что отсутствие формального опыта в сфере CompSci лишает меня права на некоторые должности, я не возражаю против этого. Я хорошо зарабатывал на жизнь более 20 лет благодаря навыкам, которые у меня есть. Что еще более важно, я проделал хорошую работу и принес большую пользу своим работодателям и клиентам.

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

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

Одна вещь, которую я узнал от Скотта, — это теория реляционных баз данных. В то время, в конце 90-х, то, что мы сейчас называем «постоянным уровнем» — я не думаю, что у нас тогда было такое название, это была просто «база данных»; если и существовала n-уровневая архитектура, то я о ней не слышал — все было на SQL. Старые дореляционные системы, такие как IMS, все еще существовали, но для новых приложений не было никакой устойчивости, кроме как с помощью SQL.

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

Теория на практике

Конечно, знать теорию — это здорово, но Йоги Берра, вероятно, на самом деле не сказал: «В теории нет разницы между теорией и практикой. Но на практике есть». У меня было много возможностей применить эту теорию на практике за эти годы, и, ну, две вещи. (1) Практическая реализация реляционных баз данных часто существенно отличается от теории, но (2) если бы я не знал теории — если бы все, что я знал, было «как», а не «почему» — это было бы мне гораздо труднее понять и справиться с этими различиями.

Иногда вы будете слышать термин ANSI SQL в связи с конкретной реализацией SQL, обычно как способ объяснить, чем эта реализация отличается от стандартной. Да, есть стандарт, на самом деле теперь это стандарт ISO, а не только ANSI. И нет, никто не следует стандарту. Но как я уже говорил ранее, стандарт по-прежнему служит полезной цели.

Недавно я писал о своих ранних днях работы с Access. Да, опять ковыряюсь в Access. Одна из самых больших проблем при изучении Access заключалась в том, что для создания запроса у вас был выбор из двух не очень хороших вариантов. По умолчанию был графический редактор. Когда я говорю не очень, я имею в виду, что это было хорошо для первых 70% вещей, которые вы хотели бы сделать, но ужасно для остальных. Если то, что вам нужно было сделать, нельзя было сделать в графическом редакторе, у вас был еще более не лучший вариант — перейти в SQL. Обратите внимание на пугающие кавычки вокруг SQL. Я повторю еще раз: вы работали с SQL, а не с реальной версией языка структурированных запросов, переданной нам от наших предков, а с игрушечной версией, которая требовала множества раздражающих лишних скобок и имела действительно ужасный текстовый редактор. вместе с ним.

Честно говоря, Access на самом деле не пытался стать полноправным членом клуба реляционных баз данных. Его конкурентами были другие настольные базы данных, в частности dBase и FoxPro, обе из которых в конечном итоге в какой-то степени поддерживали SQL, но ни одна из них не была по существу реляционной базой данных. Так что я думаю, что он не более виноват, чем кто-либо другой. Это было ужасное время в мире настольных баз данных.

Порция алфавитного супа…

В настольных базах данных по большей части ваш код приложения просто обращался к локальной базе данных, той самой, в которой вы работали, поэтому подключение к базе данных обычно не было проблемой. Но когда вы обращались к железным базам данных — SQL Server, Sybase, Oracle, DB2 — они находились на реальном сервере (вероятно, на сервере Unix или даже мэйнфрейм (!), если это не был SQL Server), поэтому вам нужно было каким-то образом подключиться к ним.

Когда-то это означало использование собственной клиентской библиотеки для доступа к базе данных. Если бы вы программировали на C, вы могли бы принять это спокойно. Это было не сложнее, чем все остальное. Вы вытащили заголовочный файл, который предоставил функции, которые вам нужно было вызвать для создания соединения, выполнения команды, обработки результатов и т. д. Это был всего лишь день из жизни программиста на C.

Но если бы вы были программистом VB, это был бы не вариант. Я имею в виду, что для некоторых баз данных могла существовать какая-то родная COM-библиотека, но кто хотел пройти через все это. Итак, надо отдать должное, Microsoft возглавила усилия по созданию ODBC, стандарта Open DataBase Connectivity. ODBC обеспечивает уровень перевода между приложениями и базами данных. Если поставщик базы данных или третья сторона предоставили соединитель ODBC, ваше приложение может просто ориентироваться на него.

Ну, я говорю просто, но есть еще кое-что. Большинство программистов, не владеющих C, на самом деле не ориентировались на ODBC. Сверху всегда был еще один слой. В Java этот уровень назывался JDBC, что было своего рода умным способом взять что-то из мира Microsoft (да, кража всегда шла в обе стороны) и добавить к этому букву «J». За исключением того, что JDBC не был точно эквивалентен ODBC. На самом деле, в некоторых случаях он использовал ODBC. Это было больше похоже на ADO («Объекты данных ActiveX»), что использовали программисты VB.

ADO был следующей эволюцией DAO («Объекты доступа к данным»), которая была следующей эволюцией RDO («Удаленные объекты данных»), оба из которых были COM-объектами, обертывающими ODBC. На самом деле ADO не обертывал ODBC; он обернул OLE DB, который был своего рода предполагаемым преемником ODBC, но на самом деле не был так уж успешен. На самом деле у Microsoft даже был поставщик OLE DB для ODBC, поэтому, если ваша целевая база данных реализует ODBC, но не OLE DB, вы можете использовать ADO для подключения через OLE DB через ODBC. Если вы вдруг не помните, OLE («связывание и встраивание объектов») — это торговая марка Microsoft для широкой категории технологий, использующих COM, например, для интеграции редактируемой электронной таблицы Excel в презентацию PowerPoint. Может быть, кто-нибудь знает, какое отношение это когда-либо имело к базам данных (ActiveX также был торговой маркой Microsoft и так же мало имел отношения к базам данных).

А затем, когда появился .NET, ADO превратился в ADO.NET, что было тем же самым, только совершенно другим. На самом деле намного лучше во многих отношениях. На самом деле, настолько лучше, что я не вижу, что они сделали для улучшения с начала до середины нулевых. Просто нечего было улучшать.

…и спагетти к нему

Не так много улучшений, за исключением, конечно, фактической части «что делать после подключения». Вот пример того, как реальный веб-сайт мог отображать некоторые данные из базы данных в классические дни ASP:

Обратите внимание на SQL внутри VBScript внутри HTML-подобного синтаксиса шаблона. Также обратите внимание на конкатенацию в предложении WHERE. Это сверхтривиальный пример. Большинство приложений будут использовать гораздо более сложный SQL. Просто глядя на код, очень сложно понять, как именно будет выглядеть окончательный SQL для всех возможных входных данных. Конечно, это тоже напрашивается на атаку SQL-инъекцией.

Я использовал классический ASP в качестве крайнего примера, но этот вид спагетти-кода SQL, встроенный в другой язык, появлялся почти везде. Единственный язык, с которым я когда-либо (вроде как) работал, где это не было проблемой, был Cobol, у которого есть действительно изящный препроцессор SQL, который позволяет вам писать (DB2) SQL прямо в вашем коде Cobol и пре- процессор преобразует его в настоящий Cobol перед этапом компиляции. И я думаю, что JDBC, возможно, имел режим, в котором вы могли выполнять какой-то SQL-в-коде, и я, кажется, припоминаю, что C тоже имел что-то подобное, теперь, когда я об этом думаю, но были некоторые серьезные недостатки и ограничения. в обоих случаях.

Итак, как избежать спагетти-кода SQL? Было (до сих пор есть, это все еще актуально) пара вариантов. Самым простым и наименее эффективным был параметризованный SQL. Вместо того, чтобы объединять строку, вы вставляете заполнитель, либо число, либо специальный маркер, а затем вызываете функцию с этим SQL и параметрами для заполнения, и вы возвращаете фактический SQL. Это безопаснее, но у вас все еще есть один язык, закодированный как строка внутри другого языка, и нет возможности проверить его перед выполнением.

Следующим вариантом будут хранимые процедуры. Большинство СУБД поддерживают один или несколько способов написания процедуры либо на версии процедурного языка SQL производителя (T-SQL, PL-SQL и т. д.), либо на языке общего назначения, таком как C, Java или C#. Вы пишете и тестируете хранимую процедуру полностью вне приложения, а затем просто ссылаетесь на нее из своего приложения, предоставляя любые необходимые параметры. Хранимые процедуры прекрасно решают проблему спагетти-кода, хотя и с парой больших компромиссов, которые я не буду здесь подробно описывать.

А еще есть ORM или объектно-реляционное сопоставление. Инструменты ORM различаются по своему подходу и деталям, но суть в том, что вы пишете собственный код приложения (C#, Java и т. д.), который транслируется или компилируется в SQL либо во время компиляции, либо во время выполнения, либо в какой-то комбинации этих двух способов. Код приложения может выглядеть как SQL, как в Microsoft Linq, или он может просто использовать стандартные языковые конструкции. Вот два примера C#, которые делают одно и то же.

Оба приводят к выполнению оператора SQL, подобного этому:

Однако у ORM есть и свои недостатки. Производительность иногда может быть хуже. А есть дырявые абстракции. Но есть более фундаментальная проблема.

То чувство, о котором я говорил ранее, захватывающая головоломка захвата области реального мира в коде, которая также существует с объектно-ориентированным программированием. Но есть действительно огромные различия. Большинство реляционных баз данных в основном имеют дело со скалярными (однозначными) примитивными (строки, числа и т. д.) типами данных. Некоторые из них имеют настраиваемые типы, некоторые допускают векторные (массивные) значения, некоторые позволяют сохранять настраиваемый контент в формате XML или JSON, но все они зависят от производителя. По большей части вам приходится использовать промежуточные объединяющие таблицы и слабые сущности для моделирования сложных отношений. Языки OO, с другой стороны, обычно предоставляют очень широкие возможности для определения гораздо более сложных сущностей и отношений в одном объекте.

Эту разницу часто называют «несоответствием импеданса». Он существует независимо от того, используете ли вы спагетти-код SQL, параметризованный SQL, хранимые процедуры или что-то еще. Но это более очевидная проблема с ORM. Инструменты позаботятся об этом, но вам придется сделать выбор. Сначала спроектируйте базу данных, а инструменты должны будут создать более сложную объектную модель или, по крайней мере, более сложные переводы. Сначала разработайте объектную модель, и запрос к базе данных может оказаться не таким простым.

Смена парадигм обмена данными

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

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

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

Избавившись от всего удовольствия

На самом деле, если нас не волнует, как выглядит база данных, давайте сделаем еще один шаг и просто сохраним наши объекты непосредственно в базе данных. Это то, что делает MongoDB. И знаешь, что? Меня это вполне устраивает. Я имею в виду, что часть меня упускает этап рисования и работы с диаграммой отношений сущностей, получения гусиных лапок, маленьких линий и кругов в нужном порядке, упорядочивания и перераспределения, пока вы не сможете посмотреть на все это в одном представлении, и все это делает смысл. Но есть и другая часть меня, которая любит простоту хранения объекта и извлечения его обратно по мере необходимости.

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

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