Pull to refresh

Ползучая гадость, или о проблемах с отдельно взятой БД отдельно взятого приложения

Reading time5 min
Views2.1K
Сегодня я хочу озвучить одну проблему, с которой сталкивается разработчик, как только в поле его зрения попадает работа с БД. Самое грустное заключается в том, что я не знаю, как решать ее правильно, и что делать. Вернее, знаю что, но мне это не помогает и не поможет. Думаю, что и вам тоже.
Ниже будет длинная ввводная, по результатам коей, я не сомневаюсь, можно наговорить про меня много интересных вещей, которых хватит на несколько формуляров по 7-Б и направлений на пожизненное принудительное лечение, но вы уж дочитайте.

Это энтерпрайз, детка


На входе у нас — ПО, продукт со сложной бизнес-логикой и развитой схемой данных: данных много (десятки гигабайт), в отношениях между ними наблюдается некоторый порядок. Некоторое количество клиентов совершенно извращенно пользуют бизнес-логику и логику данных, и по сути выставляют к ней различные требования.

«Различные требования» означает еще и то, что им всем (клиентам) нужны отличающиеся данные. В здравом уме никто не будет использовать разные БД под разные клиенты, потому что это brain damage, поэтому все данные так или иначе лежат в одной БД. Кроме различающихся данных, нужен также и различающийся UI — определяется клиентом, ролями, еще десятком параметров логики и фазы луны.

Первый такой подсад — долбаные поля в БД сами по себе. Вы видели когда-нибудь типичное «окно ввода данных для запуска бизнес-процесса»? Да, да справо оно самое, будь оно неладно. 10 закладок, 100 полей на каждом. Каждое из них как-то (ахтунг, magic!) попадает в БД, каждое из них было добавлено за долгие годы работы программы — некоторые сами по себе, некоторые — для аналитики. Они добавляются и добавляются, пока уже никто не помнит, зачем оно было нужно: понадобилось новое поле — дописали его везде, добавили в отчет, поменяли аналитику. Хорошо, если аналитика считается в один sum/count/group_by.

B прежде всего получается, что работа по добавлению нового поля в таблицу состоит в основном из
— «добавить в БД, добавить в таблицу, добавить в объекты, добавить в UI».
— напедалить очередной каши в аналитику/app workflow.

С учетом того, что, например, sql не подлежит компиляции, нужно быть очень аккуратным, чтобы обновить относящееся к делу во всех местах, где надо. На практике «быть аккуратным» означает, что после первой компиляции и развертывания с вероятностью 99% система не заработает. Скорее всего, и после вторых тоже — будут падения, ошибки и т.п. Это возможно до какой-то степени держать под контролем при помощи автотестов; но морально душит очень сильно.

И, наконец, второй подсад состоит вот в чем: надо добавить два поля — в 2 раза больше работы. 5 полей — … ну, вы поняли.

Постепенно эта гадость расползается по всему приложению; после года-двух приложение становится подчинено только одной задаче — разобраться, какие поля и где надо загружать/сохранять/обновлять. Примерно так: govnokod.ru/1071

Как такое могло случиться со мной? Как теперь жить дальше?


Оформлять это наследованием (Active Record и не только) — не выйдет, чаще всего просто невозможно выстроить иерархию, не сойдя с ума от поиска корректных отношений. Если забить на правильность объектной модели, то мы получим костыли, выражающиеся в том, что в каждом месте необходимо проверять, с тем ли типом объекта мы работаем — и работать по-разному. Приходим к варианту, что после «добавить в объекты» на надо будет еще и разбираться с иерархией. Кроме того, мое персональное подсознание против наследования в этом месте.

Если попытаться красиво разнести модель, например, в Entity Attribute Value, то выходит полная задница с запросами:
— писать вручную их еще хуже, чем просто понять, что происходит.
— производительность вылетает в трубу: сервер СУБД увлеченно занят join-ами, а остальное безобразно простаивает.

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

Помимо избыточности по данным и схеме, тут нас поджидает ад обновления БД, а так же то, что с самими метаданными нужно тоже работать в реляционной манере, что крайне вредно для ранимой психики разработчика: чтобы понять рекурсию, нужно понять рекурсию. Попытки втиснуть туда какое-то более-менее сложное поведение UI обречены на героический провал и комбинаторный взрыв количества метаданных, которые нужно понять/помнить (Знаете, как у нас в системе было сделано оповещение об изменении полей? Крутился поток, который по таймеру опрашивал все поля и сравнивал с закешированными значениями, выставляя флаги. Как только выбрасывалось исключение — а предыдущий разработчик даже NullReference считал номальным бизнес-кейсом — поток улетал в корку, и оповещения переставали приходить. Не торопитесь кидаться калошами, я убрал это, как только узнал. Но осадок остался, да). Да и циклы «update this — update those» никуда не делся. Fail.
А есть еще и рассинхронизация метаданных и кода логики. А есть еще и DBA у заказчика, для которых обновление становится ой-ой-ой с колбасой игрой в русскую рулетку.

И еще один опробованный вариант (advanced) — самописный ORM + своя объектная система. Хрен с тем, что он плохо приспособлен для работы с реляционными данными, нас по-прежнему ожидает засада с обновлением схемы БД. Да и код становится противным — нужно сидеть и реализовывать объекты поверх объектов еще раз. От написания такого совсем мало радости прибавляется. Хорошо, если это — просто справочник. А если это — лукап-таблица или сегмент онтологического классификатора? И самое интересное — как быть с отчетами?
В поисках истины мы дошли до того, что при старте сервера приложений по описанию метаданных кодогенератор выдает семейства объектов, через Aggregation Root которых и происходит работа с ними. А на клиент при коннекте передается библиотека, которая и содержит нагенерированный инструментарий. Жесть, как она есть, короче.
Про бизнес-логику валидации, процесса и прочее я вообще молчу, там что-то страшное со скриптами на «CustomSharp» (это язык такой для запутывания описания бизнеслогики).

Злоключение


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

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

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

Кто-нибудь знает о таком волшебном языке или системе? SAP/MDM не предлагать, это фэйл эпической силы чистой воды бренд ради бренда, практически неюзабельный. Да и не потянуть мне 1С или SAP в обозримом будущем :)

Как, как вы воюете эту чертову дрянь руками?

Tags:
Hubs:
Total votes 93: ↑83 and ↓10+73
Comments183

Articles