数据库 Table 是否应该使用 API 中定义的相同结构?

Should a Database Table Use the Identical Struct as Defined in the API?

假设我有一个 grpc 服务,并且有一个 API 可以像这样创建新用户:

 service UserService{
  rpc CreateUser(CreateUserRequest) returns (CreateUserResponse);
}

message User {
  string userId = 1;
  string firstName = 2;
  string lastName = 3;
  string password = 4;
}
message CreateUserRequest {
  user User = 1;
}

message CreateUserResponse {
  user User = 1;
}

该服务将用户数据保存到 PostgresDb 中的 users_table 中,如下所示:

user1 := NewUser() // instantiating the User object as defined in the proto file.
// user2 := NewMyUser() // instantiating the MyUser object as defined separately in the service.
result := s.db.Table(UsersTable).Create(user1) 

此外,我正在使用原型缓冲区根据上面的 api 原型文件生成服务器和客户端代码。 我的问题是:在实例化用户对象时,用户应该是proto定义中定义的生成的用户结构吗?或者我应该像这样在专用于 postgresDb 的服务中定义另一个用户结构作为模型吗?

struct MyUser {
      userId string
      firstName string 
      lastName  string 
      password string 
      createdAtMs int64 // an extra field not available in the api
}

后续问题: 每种方法的优缺点是什么?它的最佳设计原则是什么?

这两种方式在实践中都会发生。这可能主要取决于您的服务有多少逻辑;逻辑很少的服务(例如,一个围绕数据库的小包装器)更有可能将原型直接存储在数据库中。具有相似但略有不同的原型是很常见的,其中数据在通过系统时的每​​一步都被复制。

如果您直接将原型存储在数据库中,然后意识到您犯了一个错误,您可以通过创建一个与旧原型兼容的新原型来“分叉”原型消息(一个新名称,但所有具有相同 ID 的相同字段)。将代码迁移到新消息(将旧消息留在服务中)会让您感到痛苦,但使用这种方法,您不必在切换到新消息类型之前重新编码数据库中的所有数据。

如果您选择在数据库中使用相同的消息,您应该确保在存储它之前去除未知字段。否则,当您将来添加新字段时,您可能会发现 broken/malicious 客户端已将数据存储在该字段中。