REST和数据库的对象命名约定

huangapple go评论60阅读模式
英文:

Object naming conventions for both REST and database

问题

我在使用Java构建带有REST API的CRUD应用程序时,经常陷入反复造轮子的困境,我正在寻找某种可应用的标准。

让我们通过示例来说明。假设我有一个“用户”表和这些REST终端点:

GET  /users/{user_id} // 返回用户
POST /users           // 创建用户
PUT  /users/{user_id} // 更新用户

在每个方法中,我都需要在数据库中选择、插入或更新用户。

对于每个操作(REST有3个,数据库有3个),我需要一组不同的属性,用于在REST上接收或发送到数据库的“用户”POJO。

例如,在REST端,创建用户时,我不需要user_id(因为它由数据库创建),但我需要密码。获取用户时,我需要user_id,但我永远不希望将密码返回给客户端。更新用户时,我可能希望省略一些字段,因为它们是只读的(比如用户名或创建日期)。

在数据库端,我可能需要不在REST API中传递的不同字段,比如“is_approved”或“some_secret”。我经常需要创建派生字段,比如“password_hash”。

所以,正如我所说,同一事物的六种不同表示。在Java中,我通过创建六个不同的POJO类来实现。虽然并不总是需要那么多独特的类,但有时确实需要。

在REST API中,我不希望为所有终端点使用相同的类,只是忽略一些字段,因为这个类会传递给我们的API文档工具,并且它的属性会被发布出去。

是否有关于这六个类的标准命名约定?

在过去,对于REST,我使用了CreateUser、GetUser和UpdateUser。我不喜欢这些名称,因为它们是动词,应该是名词。

UserForCreation和UserForUpdate听起来很别扭。NewUser和ModifiedUser可能不错,但我不知道如何称呼一个用于GET的用户。

我还需要另一套用于数据库端的名称。

肯定有一种标准或约定适用于这种情况。有人知道是什么吗?

英文:

I keep reinventing the wheel when it comes to building a CRUD app with a REST API in Java, and I'm looking for some kind of standard to apply.

Let's do this by example. Let's say I have a table of "users" and these REST endpoints:

GET  /users/{user_id} // returns a user
POST /users           // creates a user
PUT  /users/{user_id} // updates a user

Within each method, I have to select, insert, or update the user in a database.

For each of the six operations (3 for REST, 3 for the database), I need a different set of properties for the "user" POJO that comes in over REST or gets sent to the database.

For example, on the REST side, when creating a user, I don't want a user_id (because that gets created by the database), but I do want a password. When getting a user, I do want user_id, but I never want to return a password back to the client. When updating a user, I may want omit some fields because they are read-only (like username or creation date).

On the database side, I may need different fields that don't get passed back in the REST API, like "is_approved" or "some_secret". And I often need to create derived fields, like "password_hash".

So, as I say, six different representations of the same thing. In Java, I do it by making six different POJO classes. It doesn't always take that many unique classes, but sometimes it does.

In the REST API, I do not want to use the same class for all endpoints and just ignore some fields, because this class gets passed to our API documentation tool and its properties get published.

Are there any standard naming conventions for these six classes?

For REST, in the past, I've used CreateUser, GetUser, and UpdateUser. I don't like these names because they're verbs and they should be nouns.

UserForCreation and UserForUpdate are awkward. NewUser and ModifiedUser might be good, but I don't know what to call a user for a GET.

And I need another whole set of names for the database side.

Surely there is a standard or a convention for this kind of thing. Anyone know what it is?

答案1

得分: 4

这种方法是借鉴/受到六边形架构/清洁架构/端口和适配器的启发。由于我们已经对DTO和业务对象进行了清晰的分离,因此我们离上述架构已经很接近了。在清洁架构中,Bob 大叔谈到了 "用例"。每个用例都有一些输入和输出。我们可以将输入想象成用例接收的 请求,将输出想象成对给定请求的 响应。因此,对于业务实体 User 和用例来说,比如 creategetupdate 等,我建议以下命名模式:

<用例><业务实体>[Request|Response]

对于给定的示例,这意味着我们创建以下类:

  • CreateUserRequestCreateUserResponse
  • GetUserRequestGetUserResponse
  • UpdateUserRequestUpdateUserResponse

更重要的是:对于像 CreateUpdate 这样的复杂操作,我们可以提取共同的字段并将它们放在一个超类中(如果 Java 支持多继承,我们可以使用混合类...),这样实际的用例请求中只包含了我们真正需要定义的数据。在许多情况下,响应是相同的,因此有一个通用的 UserResponse 类而不是许多不同的响应可能是有意义的。这还带来了一项额外的好处,即一致的API响应,例如,如果想要返回一个用户列表,可以返回一个 List<UserResponse>(以及可能一些分页信息)。

英文:

This approach is borrowed/inspired by HexagonalArchitecture/Clean Architecture/Ports and Adapters. Since we already have a clean separation of DTOs and business objects, we are quite close to the aforementioned architectures. In Clean Architecture, Uncle Bob speaks about "use cases". Each use-case has some input and some output. We can imagine the input as a request given to the use-case and the output as a response to the given request. Thus, for a business entity User and use-cases to create, get, update, ... one of such entities, I would suggest the following naming pattern:

&lt;use-case&gt;&lt;Business-entity&gt;[Request|Response]

For the given example, this would mean we create classes

  • CreateUserRequest, CreateUserResponse
  • GetUserRequest, GetUserResponse
  • UpdateUserRequest, UpdateUserResponse

What's more: for complex operations like Create and Update, we can extract the common fields and place them in a superclass (if only Java had multi-inheritance, we could use mixins...), leaving the actual use-case requests with only the data to define we really need. In many cases, the respsonse is identical, thus it can make sense to have a common UserResponse-class instead of many different responses. This gives the added benefit of consistent API responses, e.g. if one wants to return a list of users, one can return a List&lt;UserResponse&gt; (plus, maybe, some pagination information).

答案2

得分: 2

我认为你的想法基本上是正确的,为每个请求和响应设置具体的对象;然而,根据API的设计,你实际上只需要一个 User 类就足够了。你可以使用构建器来抽象出创建逻辑(比如生成用户ID),然后将请求对象传递给一个位于REST端和数据库之间的 DAO。DAO将简单地获取请求,检索用户数据,构建并返回 User 对象。

一旦用户对象创建完成,它可以用于创建响应对象,这些对象将被序列化并放入响应数据中。在这里,可能是个不错的主意写两个用户类(比如 UserInternalUser),以便更明确地说明哪些数据将会被暴露给客户端,特别是如果客户端是在与API相同的库中编写的。你也可以不实现额外的 InternalUser,并在响应对象的构建器、工厂或构造函数中处理字段过滤;然而,如果客户端是在相同的库中编写的,你可能会有泄露敏感或必要信息的风险。

你可能想要查看一下 Swagger 文件。该标准提供了一个很好的REST API规范,非常简单并且提供了一种很好的方式来为API的结构提供模板,即使它们通常看起来像是一堆JSON或XML。

简而言之
(根据 @Turing85 的建议)

  • CreateUser[Request|Response]
  • GetUser[Request|Response]
  • UpdateUser[Request|Response]
  • User
  • InternalUser
英文:

I think you generally have the right idea having specific objects for each request and response; however, depending on the design of the API, you only really need the one User class. You can use builders to abstract out the creation logic (such as generating user ids), and pass the request objects into a DAO that sits between the REST side and the database. The DAO will simply take the request, retrieve the user data, build and return the User object.

Once the user object is created it can be used to create response objects, which will be serialized and put into the response data. It might be a good idea here to write two user classes (something like User and InternalUser) to make it more explicit about what data will be exposed to the client, especially if the client is being written in the same library as the API. You could not implement the extra InternalUser and handle the field filtering in the response object builders, factories, or constructors; however, if the client is being written in the same library, you risk leaking sensitive or necessary information.

You might want to look at Swagger files. The standard provides a nice REST Api specification that are pretty simple and provide a good way to template out the APIs structure, even if they often end up lookung like walls of json or xml.

tl;dr
(as per suggestion of @Turing85

  • CreateUser[Request|Response]
  • GetUser[Request|Response]
  • UpdateUser[Request|Response]
  • User
  • InternalUser

huangapple
  • 本文由 发表于 2020年8月19日 06:34:57
  • 转载请务必保留本文链接:https://go.coder-hub.com/63477545.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定