1

I have a typical Spring boot Rest API to do crud operations on a user object.

Class User{
    long id
    String displayName
    String email
    String passwordHash
    String passwordSalt
    Instant passwordExpiryDate
    boolean locked
    Instant version (sql timestamp for optimistic locking)
}
  • POST users/: create user
  • GET users/{id}: get user details
  • PUT users/{id}: update user name and email
  • PUT users/{id}/password: update user password
  • PUT users/{id}/unlock: set lock status to false
  • DELETE users/{id}:

what bothers me is that my full model User class is currently used as input or output, even though only some properties matter. Some examples:

  • POST users/: you don't need to give id, password salt/hash or version, they are generated.
  • GET users/{id}: no need to return the id, it is already known by the requester.
  • PUT users/{id}: only need a new email, name and the version.
  • PUT users/{id}/password: only need a new password and the version
  • PUT users/{id}/unlock: only needs the version

To cope with this I started to create several "request" classes:

  • NewUserDetails
  • UserWithoutId
  • UserWithOnlyPassword
  • UserWithOnlyPersonalDetails
  • UserVersion

This ensures that my input and output only contains EXACTLY what is required, and no documentation has to be read to know which property is mandatory/used. The downside is that it is silly because they all represent the same entity, just different aspects of it. Also any validation (I use javax contraints annotations) is declared multiple times.

I was thinking about removing all the requestbodies and instead use requestparams (query parameters) to give only the necessary details, but this would result in me having put and post operations without requestbodies.

It seems there is no solution that is both clean for the clients of the api, and for the internal structure. What are best practices for this API design?

user1884155
  • 3,616
  • 4
  • 55
  • 108

2 Answers2

1

I strongly advise you to create models for your Web API rather than exposing your domain (or persistence) models, giving you a fine-grained control over the data produced and consumed by the API.

Domain (or persistence) models are complex, usually contain relationships and attributes that you don't want to (or must not) expose. Over the time, fields may be added to your domain models, removed, renamed, modified and so on. If you expose such models, you may find a hard time trying to orchestrate multiple versions of your API without breaking your clients.

By introducing mapping frameworks, you certainly can reduce the boilerplate code of mapping API models to/from domain models.

Validation should be performed over the domain models in the service layer. But nothing stops you from performing some level of validation over the Web API models in the controller layer.


Related: A similar topic is addressed in this answer.

cassiomolin
  • 124,154
  • 35
  • 280
  • 359
0

I believe creating different classes for each operation is wrong as the business logic runs around same entity. Use same object itself for all operations (Domain object model). You can create different validations based on this classes.

Arun
  • 3,701
  • 5
  • 32
  • 43
  • The validation is inside the class, using javax so that will not work. Like, for updatepassword the password is mandatory, but for unlock the password should actually be null. This is why I wanted to move away from using only a single class. – user1884155 Mar 23 '18 at 14:27
  • Most of the enterprise applications go with Domain Object model because they do not want to mess up things with different classes at the end. In your case just do not pass the optional fields and make validations accordingly. – Arun Mar 23 '18 at 14:32