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


19

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

Я использую myBatis в качестве ORM, что может быть не лучшим выбором в моем случае, так как база данных плохо спроектирована, а mybatis - это инструмент, более ориентированный на базу данных.

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

получить данные (p1, ..., р);

получить данные B (p1, ..., pi);

получить данные C (p1, ..., pi);

получить данные D (p1, ..., pi); ...

И это скоро взрывается, когда у нас разные таблицы с разными столбцами.

Это также добавляет к сложности тот факт, что я использую «калитку», которая, по сути, представляет собой отображение объектов на элементы HTML на странице. Таким образом, мой Java-код становится адаптером между базой данных и внешним интерфейсом, в результате чего я создаю много связующего, стандартного кода с некоторой логикой в ​​нем.

Правильным ли решением было бы обернуть средства отображения ORM дополнительным слоем, который представляет более однородный интерфейс для БД, или есть лучший способ справиться с этим кодом спагетти, который я пишу?

РЕДАКТИРОВАТЬ: Больше информации о базе данных

База данных содержит в основном информацию о телефонных звонках. Плохой дизайн состоит из:

Таблицы с искусственным идентификатором в качестве первичного ключа, который не имеет ничего общего со знанием предметной области.

Никаких уникальных триггеров, проверок или внешних ключей.

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

Записи, которые можно классифицировать только путем скрещивания с другими таблицами с другими условиями.

Столбцы, которые должны быть числами или датами, хранятся в виде строк.

Подводя итог, грязный / ленивый дизайн вокруг.


7
Корректирует ли дизайн базы данных вариант?
RMalke

1
Пожалуйста, объясните, как база данных плохо спроектирована.
Тулаинс Кордова

@Renan Malke Stigliani К сожалению, нет, поскольку существует устаревшее программное обеспечение, которое зависит от него, однако я отразил некоторые таблицы с немного другим дизайном и заполнил их, что упрощает код. Однако я не горжусь этим, и я бы предпочел не дублировать таблицы без разбора
DPM

1
Эта книга может дать вам некоторые идеи о том, как вы можете начать исправлять проблему с базой данных и поддерживать работающий код: amazon.com/…
HLGEM

4
Большинство проблем вы перечислите. , , нет. В настоящее время использование суррогатных ключей, а не естественных ключей, на самом деле является довольно стандартной рекомендацией; совсем не "плохой дизайн". Отсутствие ограничений и использование неподходящих типов столбцов - лучший пример, если говорить о «плохом дизайне», но это вообще не должно влиять на код вашего приложения (если вы не планируете злоупотреблять этими проблемами?).
Руах

Ответы:


53

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

Реальный вопрос здесь в том, где вы заключаете эту сложность?

Итак, позвольте мне сделать шаг назад и поговорить о том, на какую «сложность» я здесь ссылаюсь. Ваша проблема (насколько я понимаю; исправьте меня, если я ошибаюсь) - это модель постоянства, которая не является эффективно используемой моделью для задач, которые необходимо выполнить с данными. Это может быть эффективным и полезным для других задач, но не для ваших задач.

Итак, что мы делаем, когда у нас есть данные, которые не представляют хорошую модель для наших средств?

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

Нужно ли переводить оба направления? Оба направления будут переведены так же, как в:

(Tbl A, Tbl B) -> Obj X (читать)

Obj X -> (Tbl A, Tbl B) (напишите)

или операции вставки / обновления / удаления представляют другой тип объекта, так что вы читаете данные как Obj X, но данные вставляются / обновляются из Obj Y? Какой из этих двух способов вы хотите использовать, или если обновление / вставка / удаление невозможно, являются важными факторами, на которых вы хотите разместить перевод.


Куда вы переводите?

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

Опять сейчас; Ваша проблема в том, куда вы кладете эту сложность? Ну, у вас есть выбор.

Вы можете сделать это в базе данных, используя хранимые процедуры. Это имеет недостаток, заключающийся в том, что они часто не очень хорошо играют с ORM, но это не всегда так. Хранимые процедуры дают некоторые преимущества, в том числе производительность. Однако хранимые процедуры могут потребовать большого количества обслуживания, но вы должны проанализировать свой конкретный сценарий и сказать, будет ли обслуживание больше или меньше других вариантов. Я лично очень хорошо разбираюсь в хранимых процедурах, и этот факт наличия таланта снижает накладные расходы; никогда не стоит недооценивать ценность принятия решений на основе того, что вы знаете. Иногда неоптимальное решение может быть более оптимальным, чем правильное решение, потому что вы или ваша команда знаете, как его создать и поддерживать лучше, чем оптимальное решение.

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

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

Существуют такие инструменты, как auto-mapper , которые могут сделать использование ORM правдоподобным, когда они могут выполнять перевод между моделью базы данных из orm в пригодные для использования модели, но некоторые из этих инструментов могут быть сложными для поддержания / понимания поведения, более похожего на магию; хотя они создают минимум служебного кода, что приводит к меньшим накладным расходам на обслуживание, если их хорошо понимать.

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


Теперь давайте начнем говорить сумасшедшие .

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

Там модель актера.Это интересный подход, потому что он говорит, что все, что вы делаете, - это отправляете сообщения другому коду, который эффективно делегирует всю работу этому другому коду, что очень эффективно для инкапсуляции сложности от всего вашего кода. Это может сработать, поскольку вы отправляете актеру сообщение «Мне нужен объект X, отправленный в Y», и у вас есть приемник, ожидающий ответа в местоположении Y, который затем обрабатывает объект X. Вы можете даже отправить сообщение с указанием «Мне нужен Obj X и вычисление Y, Z сделано для него», и тогда вам даже не нужно ждать; перевод происходит на другой стороне этого сообщения, и вы можете просто двигаться дальше, если вам не нужно читать его результат. Это может быть незначительное злоупотребление актерской моделью для ваших целей, но все зависит;

Другая граница инкапсуляции - это границы процесса. Они могут быть использованы для разделения сложности очень эффективно. Вы можете создать код перевода в виде веб-службы, где для связи используется простой HTTP, с использованием SOAP, REST или если вы действительно хотите использовать собственный протокол (не рекомендуется). STOMP совсем не плохой новый протокол. Или используйте обычный демон-сервис с системным каналом публичной памяти для очень быстрой связи с любым другим протоколом, который вы выберете. Это на самом деле имеет довольно хорошие преимущества:

  • У вас может быть запущено несколько процессов, которые выполняют перевод для поддержки более старых и более новых версий одновременно, что позволяет обновлять службу перевода для публикации объектной модели V2, а затем отдельно позднее обновлять код потребления для работы с новым объектом. модель.
  • Вы можете делать интересные вещи, такие как закрепление процесса на ядре для повышения производительности, вы также получаете определенную степень безопасности при таком подходе, делая единственный процесс, работающий с привилегиями безопасности, касающийся этих данных.
  • Вы получите очень сильную границу, когда будете говорить о границах процесса, которые останутся неизменными, гарантируя минимальную утечку вашей абстракции в течение длительного времени, потому что написание кода в пространстве перевода не будет вызываться вне пространства перевода, так как они не будет разделять объем процесса, обеспечивая фиксированный набор сценариев использования по контракту.
  • Возможность для асинхронных / неблокирующих обновлений проще.

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


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

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

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

Хорошо, да, это действительно говорит сумасшедший, но кто знает; Вы можете просто быть сумасшедшим (серьезно, не берите монады с рейтингом сумасшествия ниже 88%, существует реальный риск получения телесных повреждений).


4
Вау, какой необычайно полный ответ. Я бы сказал это несколько раз, если бы мне позволил только SE.
Марьян Венема

11
Когда выйдет версия фильма?
Яннис

3
@JimmyHoffa Браво, сэр !!! Я собираюсь добавить этот ответ в закладки и показать своей дочери, когда она станет старше.
Томбатрон

4

Мое предложение:

Создайте представления базы данных, которые:

  1. Дать значимые имена столбцам
  2. Выполните «скрещивание с другими таблицами с другими условиями», чтобы скрыть эту сложность.
  3. Преобразуйте числа или даты, хранящиеся в виде строк, в числа и даты соответственно.
  4. Создайте уникальность там, где ее нет, по некоторым критериям.

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

Затем настройте ORM на этот фасад вместо реальных таблиц.

Это не упрощает вставки, хотя.


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

3

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

  • Помните твердые принципы .
  • Напишите код, который может быть легко протестирован модулем (который часто сопровождается принципами SOLID).
  • Держите свою бизнес-логику отдельно от вашей логики отображения.
  • Прочитайте документацию Apache Wicket и примеры - эта среда, вероятно, поможет вам сэкономить больше стандартного кода, чем вы думаете, поэтому научитесь эффективно его использовать.
  • Храните логику, которая должна работать с базой данных, на отдельном уровне, который обеспечивает чистый интерфейс, с которым может работать ваша бизнес-логика. Таким образом, если вы (или будущий сопровождающий) когда-либо получите возможность улучшить схему, они могут сделать это без слишком большого количества изменений в бизнес-логике.

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

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


1

Отвечая на ваш первоначальный вопрос о лучшем объектно-ориентированном коде, я бы предложил использовать объекты, говорящие на SQL . ORM по своей сути идет вразрез с объектно-ориентированными принципами, поскольку действует на объект, а объект в ООП является самодостаточной сущностью, у которой есть все ресурсы для достижения своей цели. Я уверен, что такой подход может сделать ваш код проще.

Говоря о проблемном пространстве, т. Е. О вашем домене, я постараюсь определить совокупные корни . Это границы согласованности вашего домена. Границы, которые обязательно должны поддерживать постоянство. Агрегаты общаются через доменные события. Если у вас достаточно большая система, возможно, вам следует начать разбивать ее на подсистемы. (назовите ее SOA, Microservice, Автономные системы и т. Д.)

Я бы также подумал об использовании CQRS - это может значительно упростить как вашу сторону записи, так и чтения. Убедитесь в том , чтобы прочитать UDI Даан в статью об этой теме.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.