Як створити кращий код OO в додатку, керованому реляційною базою даних, де база даних погано розроблена


19

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

Я використовую myBatis як ORM, що може бути не найкращим вибором у моєму випадку, оскільки база даних погано розроблена, а mybatis - більш інструмент, орієнтований на базу даних.

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

отримати дані (p1, ..., р);

отримати дані B (p1, ..., pi);

отримати дані C (p1, ..., pi);

отримати дані D (p1, ..., pi); ...

І це незабаром вибухає, коли у нас різні таблиці з різними стовпцями.

Це також додає складності тому, що я використовую "хвіртку", що фактично є відображенням об'єктів на html-елементи на сторінці. Таким чином, мій код Java стає адаптером між базою даних та передньою частиною, завдяки чому я створюю багато електропроводки, кодовий код із певною логікою, що в ньому втручається.

Чи правильним рішенням буде обгортання картографів ORM додатковим шаром, який представляє більш однорідний інтерфейс до db, чи є кращий спосіб впоратися з цим кодом спагетті, про який я пишу?

EDIT: Більше інформації про базу даних

База даних зберігає в основному інформацію про телефонні дзвінки. Погана конструкція складається з:

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

Ніяких унікальних, пускових, чекових або сторонніх ключів.

Поля із загальною назвою, які відповідають різним поняттям для різних записів.

Записи, які можна класифікувати лише шляхом схрещування з іншими таблицями з різними умовами.

Стовпці, які мають бути числами або датами, зберігаються як рядки.

Підводячи підсумок, брудний / лінивий дизайн навколо.


7
Чи можна виправити дизайн бази даних?
RMalke

1
Поясніть, будь ласка, як погано розроблена база даних.
Тулен Кордова

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

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

4
Більшість проблем, які ви перераховуєте. . . ні. Використання сурогатних ключів, а не природних ключів, є насправді досить стандартною рекомендацією; зовсім не "поганий дизайн". Відсутність обмежень та використання невідповідних типів стовпців є кращим прикладом, що стосується "поганого дизайну", але він фактично не повинен впливати на код вашої програми (якщо ви не плануєте зловживати цими проблемами?).
ruakh

Відповіді:


53

Орієнтація на об'єкт цінна саме тим, що виникають такі типи сценаріїв, і він дає вам інструменти для розумного проектування абстракцій, які дозволяють скласти складність.

Справжнє запитання тут полягає в тому, де ти складеш цю складність?

Тож дозвольте мені відійти на мить назад і поговорити про те, про яку «складність» я говорю тут. Ваша проблема (наскільки я це розумію; виправте мене, якщо я помиляюся) - це модель наполегливості, яка не є ефективно корисною моделлю для завдань, які потрібно виконати з даними. Це може бути ефективним і корисним для інших завдань, але не для ваших завдань.

То що робити, коли ми маємо дані, які не представляють гарної моделі для наших засобів?

Перекласти. Це єдине, що ти можеш зробити. Цей переклад - це «складність», про яку я згадуюсь вище. Тож тепер, коли ми приймаємо, що будемо перекладати модель, нам потрібно визначитися з кількома факторами.

Чи потрібно нам перекладати обидва напрямки? Чи будуть обидва напрямки перекладені однаково, як у:

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

Obj X -> (Tbl A, Tbll B) (записувати)

чи дії з вставки / оновлення / видалення являють собою інший тип об'єкта, таким чином, що ви читаєте дані як Obj X, але дані вставляються / оновлюються з Obj Y? Який із цих двох способів ви хочете пройти, або якщо неможливо оновити / вставити / видалити, є важливими факторами, де ви хочете перекласти.


Куди перекладаєте?

Назад до першого твердження, яке я зробив у цій відповіді; OO дозволяє вам інкапсулювати складність, і на що я тут згадуюсь, це те, що ви не тільки повинні, але й повинні інкапсулювати цю складність, якщо хочете переконатися, що вона не просочується і не проникає у весь ваш код. У той же час важливо визнати, що ви не можете мати ідеальну абстракцію, тому турбуйтеся про це менше, ніж про дуже ефективну та корисну.

Знову зараз; Ваша проблема полягає в тому, де ви ставите цю складність? Ну у вас є вибір.

Це можна зробити в базі даних, використовуючи збережені процедури. Це має недолік, що часто не дуже добре грає з ORM, але це не завжди так. Збережені процедури дозволяють отримати певні переваги, зокрема часто. Процедури, що зберігаються, але можуть вимагати великого обслуговування, але вам належить проаналізувати конкретний сценарій і сказати, чи буде обслуговування більш-менш інших варіантів. Я особисто дуже вмілий зі збереженими процедурами, і як такий факт наявного таланту зменшує накладні витрати; ніколи не варто недооцінювати значення прийняття рішень , заснованих на тому, що ви дійсно знаєте. Іноді субоптимальне рішення може бути більш оптимальним, ніж правильне рішення, оскільки ви або ваша команда знаєте, як створити та підтримувати його краще, ніж оптимальне рішення.

Ще один варіант в базі даних - це перегляди. Залежно від сервера вашої бази даних, вони можуть бути вкрай оптимальними або недооптимальними або взагалі не ефективними, один із недоліків може бути часом запитів залежно від того, які параметри індексації є у ​​вашій базі даних. Перегляди стають ще кращим вибором, якщо вам ніколи не потрібно робити будь-які зміни (вставити / оновити / видалити).

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

Існують такі інструменти, як автоматичний картограф , які можуть зробити використання ORM правдоподібним, коли вони можуть зробити переклад між базовою моделлю з orm на зручні моделі, але деякі з цих інструментів можуть бути складними, щоб підтримувати / розуміти, що вони більше схожі на магію; хоча вони створюють мінімум накладних кодів, що призводить до менших накладних витрат на технічне обслуговування, коли вони добре розуміються.

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


Тепер почнемо говорити божевільно .

Це Objectне єдине, що існує у всьому, абстракція. Протягом багатьох років, що вивчали інформатику, і ще до цього часу з вивчення математики, спостерігалося глибоке абстрагування. Якщо ми почнемо займатися творчістю, давайте поговоримо про відомі доступні абстракції, які вивчалися.

Там модель актора.Це цікавий підхід, оскільки він говорить, що все, що ви робите, - це надсилання повідомлень на інший код, який фактично делегує всю роботу тому іншому коду, що є дуже ефективним у захопленні складності від усього вашого коду. Це може спрацювати в тому випадку, якщо ви надсилаєте повідомлення акторові, що каже: "Мені потрібен Obj X, надісланий Y", і у вас є гніздо, яке чекає відповіді в місці Y, яке потім обробляє Obj X. Ви навіть можете надіслати повідомлення, яке вказує "Мені потрібні Obj X і обчислення Y, Z зроблено для цього", і тоді вам навіть не потрібно чекати; переклад відбувається на іншій стороні цього пропуску повідомлення, і ви можете просто рухатися далі, якщо вам не потрібно прочитати його результат. Це може бути незначним зловживанням акторською моделлю для ваших цілей, але все залежить;

Ще одна межа інкапсуляції - це межі процесу. Їх можна дуже ефективно використовувати для розмежування складності. Ви можете створити код перекладу як веб-службу, де спілкування є простим HTTP, використовуючи SOAP, REST або якщо ви дійсно хочете власний протокол (не пропонується). STOMP - це зовсім не поганий новий протокол. Або скористайтеся звичайною послугою демона із системою пам’яті, що оприлюднюється в локальній системі, для швидкого спілкування знову, використовуючи той обраний протокол. Це насправді має деякі непогані переваги:

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

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


Існує велика різноманітність способів інкапсуляції складності, які дозволять розмістити цю складність у дедалі більш дивних та цікавих місцях вашої системи. Використовуючи форми функцій вищого порядку (часто їх підробляють, використовуючи шаблон стратегії або різні інші непарні форми об'єктних шаблонів), ви можете зробити дуже цікаві речі.

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

Це створює межу, коли весь переклад існує не лише з іншого боку кордону, далеко від усього вашого коду; він використовується лише на тій стороні, що дозволяє решті коду навіть нічого про нього не знати, окрім місця входу для цієї межі.

Гаразд, так, це справді говорить божевільно, але хто знає; ви можете просто бути таким божевільним (якщо серйозно, не вживайте монадів з божевільністю нижче 88%, існує реальний ризик тілесних ушкоджень).


4
Нічого собі, яка надзвичайно вичерпна відповідь. Я б схвалив це не раз, якби тільки SE дозволив мені.
Marjan Venema

11
Коли виходить версія фільму?
янніс

3
@JimmyHoffa Bravo сер !!! Я збираюся взяти закладку на цю відповідь і показати доньці, коли вона дорослішає.
Томбатрон

4

Моя пропозиція:

Створіть види баз даних, які:

  1. Дайте змістовні назви стовпцям
  2. Зробіть "схрещування з іншими таблицями з різними умовами", щоб ви могли заховати цю складність.
  3. Перетворити числа або дати, що зберігаються у вигляді рядків, у числа та дати відповідно.
  4. Створіть унікальність там, де її немає, за деякими критеріями.

Ідея полягає у створенні фасаду, який імітує кращий дизайн поверх поганого.

Потім зробіть ORM відносно цього фасаду замість реальних таблиць.

Це не спрощує вставки.


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

3

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

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

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

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


1

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

Якщо говорити про проблемний простір, тобто про ваш домен, я б спробував визначити сукупні корені . Це межі узгодженості вашого домену. Межі, які абсолютно повинні зберігати свою послідовність у будь-який час. Агрегати спілкуються через події в домені. Якщо у вас система досить велика, ймовірно, ви повинні почати розділяти її на підсистеми (називайте її SOA, мікросервіс, автономні системи тощо)

Я також розглядаю можливість використання CQRS - це може значно спростити як вашу сторону запису, так і читання. Переконайтеся в тому , щоб прочитати UDI Даан в статтю про цю тему.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.