Дилемма состояния работы

Существует повторяющаяся проблема относительно полей состояния и аналогичного предопределенного набора значений.

Возьмем пример системы упорядочения с сущностью порядка, которая имеет статус, который может быть "Новый", "Прогресс", "Платный" и т.д.

Эта проблема:

Статус заказа должен быть

  • хранится (в базе данных)
  • обрабатывается (в бэкэнд)
  • (для интерфейса API веб-сервиса)

Как выполнять эти три действия при сохранении:

  • Сохраните значение статуса.
  • эффективное хранилище.

Вот некоторые примеры реализации с их плюсами и минусами:

1- Таблица состояния

  • База данных будет содержать таблицу состояния с идентификатором, именем
  • Таблица заказа ссылается на идентификатор состояния.

    CREATE TABLE 'status' (
      'id' INT NOT NULL,
      'name' VARCHAR(45) NOT NULL,
      PRIMARY KEY ('id'));
    
    CREATE TABLE IF NOT EXISTS 'order' (
      'id' INT NOT NULL AUTOINCREMENT,
      'status_id' INT NOT NULL,
      PRIMARY KEY ('id'),
      INDEX 'order_status_idx' ('status' ASC),
      CONSTRAINT 'order_status_id'
        FOREIGN KEY ('status_id')
        REFERENCES 'status' ('id')
        ON DELETE NO ACTION
        ON UPDATE NO ACTION);
    
  • Бэкэнд-код имеет перечисление, которое дает эти предопределенные целые значения в коде

    enum Status {
        PAID = 7;
    };
    
    // While processing as action ...
    order.status = Status::PAID;
    
  • API веб-службы вернет номер статуса

    order: { id: 1, status_id: 7 }
    
  • Код интерфейса имеет аналогичное перечисление, которое дает эти предопределенные целые значения в коде. (например, бэкэнд-код)

  • Плюсы:

    • База данных четко определена и нормализована
  • Минусы:
    • Отображение между номером статуса и значением выполняется в трех местах, что дает пространство для человеческих ошибок и непоследовательность при определении значения определенного номера статуса.
    • status_id: 7 данные API не являются описательными, потому что status_id: 7 не дает конкретного значения, потому что он не включает значение status_id: 7

2- Статус ENUM

  • В базе данных таблица заказов будет содержать столбцы состояния с типом ENUM, содержащие предопределенные статусы.

    CREATE TABLE IF NOT EXISTS 'order' (
      'id' INT NOT NULL AUTOINCREMENT,
      'status' ENUM('PAID') NULL,
      PRIMARY KEY ('id'));
    
  • Бэкэнд-код имеет постоянные значения как артефакты кода для предопределенного статуса

    enum Status {
        PAID = 'PAID'
    };
    

    ИЛИ ЖЕ

    class Status {
    public:
        static const string PAID = PAID;
    };
    

    Использовать как последующее

    // While processing as action ...
    order.status = Status::PAID;
    
  • API веб-службы вернет константу статуса

    order: { id: 1, status: 'PAID' }
    
  • Код frontend будет иметь аналогичную конструкцию для предопределенных констант состояния. (например, бэкэнд-код)

  • Плюсы:

    • База данных четко определена и нормализована
    • Возвращенные данные из API являются описательными и предоставляют требуемое значение.
    • Используемые константы статуса уже содержат их значение, что уменьшает вероятность ошибок.
  • Минусы:
    • Использование типа ENUM для столбца в базе данных имеет свои ограничения. Добавление новой константы статуса к этому перечислению позже с помощью команды ALTER дорого стоит специально для огромных таблиц, таких как таблица order.

3- Мое предлагаемое решение:

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

    CREATE TABLE 'status' (
      'key' VARCHAR(45) NOT NULL,
      PRIMARY KEY ('key'));
    
  • Таблица заказов будет содержать поле с именем status с строкой типа, которое ссылается на key поле таблицы status.

    CREATE TABLE IF NOT EXISTS 'order' (
      'id' INT NOT NULL AUTOINCREMENT,
      'status' VARCHAR(45) NOT NULL,
      PRIMARY KEY ('id'),
      INDEX 'order_status_idx' ('status' ASC),
      CONSTRAINT 'order_status'
        FOREIGN KEY ('status')
        REFERENCES 'status' ('key')
        ON DELETE NO ACTION
        ON UPDATE NO ACTION);
    
  • Бэкэнд-код имеет постоянные значения как артефакты кода для предопределенного статуса

    enum Status {
        PAID = 'PAID'
    };
    

    ИЛИ ЖЕ

    class Status {
    public:
        static const string PAID = PAID;
    };
    

    Использовать как последующее

    // While processing as action ...
    order.status = Status::PAID;
    
  • API веб-службы вернет константу статуса

    order: { id: 1, status: 'PAID' }
    
  • Код frontend будет иметь аналогичную конструкцию для предопределенных констант состояния. (например, бэкэнд-код)

  • Плюсы:

    • База данных четко определена и нормализована
    • Возвращенные данные из API являются описательными и предоставляют требуемое значение.
    • Используемые константы статуса уже содержат их значение, что уменьшает вероятность ошибок.
    • Добавление новой константы состояния выполняется с помощью команды INSERT в таблице состояния.
  • Минусы:
    • ???

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

Укажите причины, по которым предлагаемое решение плохое, и почему лучше ваше решение

Спасибо.

Ответ 1

Этот мой подход к этой проблеме:

  1. Я добавляю status столбца с string типа в таблице orders.
  2. Определите константу всех ваших статусов в своем классе, чтобы вы могли легко ссылаться на них.
  3. Выполните правило проверки правильности создания порядка, в котором значение статуса находится в разрешенных ранее разрешениях.

Это упрощает добавление нового статуса, просто редактируя базу кода, а полученное значение для статуса по-прежнему является строкой (описательной).

Надеюсь, это ответит на ваш вопрос.

Ответ 2

Я предлагаю это:

  1. Сохранить в БД как статус (unsigned tinyint, char (5)).
  2. Идентификатор должен быть степенью 2: 1,2,4,8,...
  3. На внутреннем коде имя const должно быть гуманизированным, а значение - int: const PAID = 2
  4. В бэкэнде вы не должны использовать conts напрямую, а использовать объект класса статуса, который будет содержать некоторые методы, такие как value и name.
  5. Этот тест класса проверит, что все его значения находятся в БД и все значения БД покрыты классом.

пространство для человеческих ошибок

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

Статусы обычно не так сложны и имеют не так много значений, чтобы с ними связываться.

Enum это зло. http://komlenic.com/244/8-reasons-why-mysqls-enum-data-type-is-evil/

По поводу вашего предложения:

База данных хорошо определена и нормализована

Нет. Это денормализовано.

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

Вы всегда можете использовать обертку, которая входит в таблицу статуса, чтобы получить человеческое имя.

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

Название Const для людей, а значения для Benders.

Добавить новую константу состояния просто с помощью команды INSERT в таблице состояния.

То же самое в первом и моем решении.