处理状态困境

时间:2018-03-14 12:12:20

标签: php node.js database api web-services

关于状态字段和类似的预定义值集存在一个反复出现的问题。

让我们举一个订单系统的例子,订单实体的状态可以是新的,正在进行的,付费的等等。

问题:

订单的状态需要

  • 存储(在数据库中)
  • 已处理(在后端)
  • 传达(到Web服务API的前端)

如何保持这三项活动:

  • 保留状态的含义。
  • 高效存储。

以下是一些具有优缺点的示例实现:

1-状态表

  • 数据库将包含一个ID为name
  • 的状态表
  • 订单表引用状态的ID。

    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;
    
  • Web服务API将返回状态编号

    order: { id: 1, 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'
    };
    

    OR

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

    用作以下内容

    // While processing as action ...
    order.status = Status::PAID;
    
  • Web服务API将返回状态常量

    order: { id: 1, status: 'PAID' }
    
  • 前端代码将具有预定义状态常量的类似构造。 (如后端代码)

  • 优点:

    • 数据库定义良好且规范化
    • 来自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'
    };
    

    OR

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

    用作以下内容

    // While processing as action ...
    order.status = Status::PAID;
    
  • Web服务API将返回状态常量

    order: { id: 1, status: 'PAID' }
    
  • 前端代码将具有预定义状态常量的类似构造。 (如后端代码)

  • 优点:

    • 数据库定义良好且规范化
    • 来自API的返回数据具有描述性,并提供所需的含义。
    • 使用的状态常量已包含其含义,可减少出错的几率。
    • 使用状态表中的INSERT命令添加新状态常量很简单。
  • 缺点:
    • ???

我想知道这是否是一个可行的解决方案,或者对于这个反复出现的问题有更好的解决方案。

请说明为什么建议的解决方案不好以及为什么更好的解决方案更好

谢谢。

2 个答案:

答案 0 :(得分:0)

这是我解决这个问题的方法:

  1. 我在status表格中添加了string列{。}}。
  2. 定义班级中所有状态的常量,以便您轻松引用它们。
  3. 制定有关订单的验证规则,即状态值位于您之前定义的唯一允许值中。
  4. 这样只需编辑代码库就可以非常轻松地添加新状态,并且检索到的状态值仍然是一个字符串(描述性)。

    我希望这能回答你的问题。

答案 1 :(得分:0)

我建议这样做:

  1. 以状态(未签名的tinyint,char(5))存储在数据库中。
  2. Id必须是2、1、2、4、8,...的幂
  3. 在后端代码中,const名称必须是人性化的,但值-int:const PAID = 2
  4. 在后端,您不应直接使用const,而应使用状态类对象,该对象将包含诸如valuename之类的一些方法。
  5. 该类的测试将检查其所有值是否都在数据库中,并且所有DB的值都被该类覆盖。
  

人为错误的空间

为避免人为错误而发明的测试。

状态通常不是那么复杂,也没有太多的值可弄乱它们。

枚举是邪恶的。 http://komlenic.com/244/8-reasons-why-mysqls-enum-data-type-is-evil/

关于您的建议:

  

数据库定义良好并规范化

不。它是非规范化的。

  

API返回的数据具有描述性,并具有所需的含义。

您始终可以使用包装器,该包装器会进入状态表以获取人名。

  

所使用的状态常量已经包含了其含义,从而减少了出错的机会。

const名称是人类的名字,值是Benders的东西。

  

使用状态表中的INSERT命令添加新的状态常数很简单。

与第一和我的解决方案相同。