Абстракции клиента базы данных - PullRequest
1 голос
/ 11 февраля 2020

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

В моем В проекте я использую и BigQuery, и Firebase довольно интенсивно, но клиентские библиотеки настолько просты в использовании, что я не уверен, что я могу добавить в качестве слоя абстракции, например:

  await bigquery
    .dataset(datasetId)
    .table(tableId)
    .insert(rows);

Я мог бы обернуть это в функции, которая включала бы небольшую обработку ошибок.

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

Как работает одна абстрактная база данных в nodejs приложениях?

1 Ответ

2 голосов
/ 14 февраля 2020

Самая важная часть - , чтобы ваш домен не зависел от деталей реализации . База данных - это деталь реализации.

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

Как вы это делаете?

Определяя интерфейс (это ваша абстракция), который говорит: «Мне нужно сохранить / получить данные, какая бы база данных ни стояла ". Затем вы внедряете реализацию BigQuery этого интерфейса в prod… и внедрить реализацию в памяти в тестах или даже в режиме разработки просто.

Теперь, в JavaScript, явного интерфейса нет. Но идея абстракции еще существует. Это просто неявно. Интерфейс будет тем, что вы на самом деле используете (типизированный ввод).

Используя ваш конкретный пример

Допустим, у вас есть:

async function doSomething() {
  // Some other domain stuff…

  await bigquery
    .dataset(datasetId)
    .table(tableId)
    .insert(rows);
}

Это не сильно поможет :

function insertInDb(rows) {
  return bigquery
    .dataset(datasetId)
    .table(tableId)
    .insert(rows);
}


async function doSomething() {
  // Some other domain stuff…

  await insertInDb(rows);
}

Однако это поможет:

function insertInDb(rows) {
  return bigquery
    .dataset(datasetId)
    .table(tableId)
    .insert(rows);
}


async function doSomething(insertInDb) {
  // Some other domain stuff…

  await insertInDb(rows);
}

Разница невелика, но фактическая функция insertInDb вводится во время выполнения, что инвертирует зависимость.

Идем дальше: абстракция репозитория над базами данных

Теперь эту концепцию обычно называют репозиторием.

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

class ScoreRepositoryBigQuery {
  save(newScore) {
    // Some logic to convert `newScore` into BigQuery compatible `rows`…

    return this.bigquery
      .dataset(this.datasetId)
      .table(this.tableId)
      .insert(rows);
  }
}


async function answerQuestion(scoreRepository) {
  // Some other domain stuff…

  await scoreRepository.save(newScore);
}

Было бы легко создать новый ScoreRepository с использованием другого хранилища. механизм (например, MongoDB, сторонний сервис, реализация в памяти и т. д. c.).

Вам просто нужно реализовать неявный интерфейс (например, он должен иметь асинхронный c save() метод, который берет newScore и сохраняет его).

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

Так что this будет полезным уровнем абстракции.

...