1

If I have for example a User POJO like the following

@AllArgsConstructor
public class User {

@Id
private final String id;

private String username;

private String password;

private Date createdDate;

private Boolean enabled;

//getters and setters 

}

And I want to post a User and have an API like the following

@PostMapping("/user")
public User postSignup(@Valid @RequestBody User user) {

    return usersService.createUser(user);
}

Does it make sense to create a DTO just to transfer the object and make it so the additional fields createdDate & enabled can't be set by someone modifying the JSON manually on the client side. Theoretically there could be a bunch of other fields and this applies to other POST requests. So in general should I also create a DTO for my entity and use that DTO for the POST and then convert to the entity to be saved to the DB?

Thanks in advance

5 Answers 5

3

As always, it depends.

It depends on who is using the API.

If the API is used internally in the application and the client and the server constantly change together and are developed by the same team, a DTO may not be necessary.

If it's a 3-party API, you may not want to expose the entity model but create a API layer with DTOs.

2

There are at least 3 common (non-exclusive) cases where I consider introducing DTOs for transit over the web:

  • When the API needs to be versioned - I will need to support multiple versions at the same time.
  • When the transit messages are significantly different to the domain entities - philosophies such as CQRS and/or DDD may push you in this direction.
  • To establish clear object boundaries, i.e. the domain entity may have many child objects which can be lazy loaded, however I want clear documentation about what the API returns. Therefore I create custom DTO's that only contain fields/objects that are (fully) populated.

Currently my preference is to use OpenAPI to specify the API and then code-gen both the DTO's and the method signatures. I find this approach helpful both to keep the API docs in sync with the code and to force me to think about the public API independently of my implementation.

make it so the additional fields createdDate & enabled can't be set by someone modifying the JSON manually

I ask myself the question are these fields something I want to expose in my API (which may be two separate questions for read and write operations), if not, I don't expose them.

In practice this means I have, separate DTO's for read and write, however I usually re-use the same DTO for insert and update - I throw the id in the URL so I don't have to pass it in the body of the message, that is consistent with how most sites implement PUT anyway.

Does it make sense

Only you can answer that, if its an internal app / you are implementing both sides of the API yourself, you will have to decide if it's worth it.

Most of what I have said only really provides value when you are trying to convey clear intent/meaning to a larger group of developers.

0

Yes, I would say that you should create a DTO. A lot of folks might say that it depends or that you probably don't need it, but from my perspective any time you accept data from an external data source like a web-baed API, you should consider putting the data into a format that allows you to move the data around in your system, in a structured manner. A POJO DTO is a good way to do that.

0

As you tagged your question with and , first and foremost I don't think you have the right understanding of what REST really is and what purpose it tries to solve.

A REST architecture is basically just an architecture style that consists of just a few constraints that when followed correctly support developers in decoupling clients from servers. The benefits to gain from that are simply that the server side can evolve without having to fear breaking clients while clients just get robust to changes. This is achieved by introducing a series of indirection mechanisms, i.e. like link relations and what not.

If you look at other Web services you often will find that they support only one syntax, and requires a kind of interface definition language (IDL) to advertise their API to clients. SOAP i.e. is based on an XML syntax and often the Web Service Description Language (WSDL) to define how the structure of requests need to look like and what API methods are available to call. REST has a very different approach here. Instead of having a particular IDL to define the interface of the API, its API consists of 3 components: a transport layer, a naming service and a set of supported media types.

The transport layer in short is often HTTP and is de facto just a remote document management system that allows you to store, update or delete documents on a remote server and hence the HTTP operations are catered exactly for that purpose. If you try to create, delete or update multiple documents at once with a single request you might have some problems as it is designed with single document manipulation in mind. As such, we usually operate on side effects of that document management. See Jim Webbers excellent talk on REST and DDD.

URL/URI/IRIs are usually used as naming service to address resources in such an architecture uniquely. The exact form of such URIs in a REST environment are not important and as such is no real concern of it. URIs per se don't express meaning, they don't give a clue on what kind or type of data a resource contains and in which relation that resource is to the current context. It also doesn't express a parent-child relationship naturally. Most simply said, a URI is just a pointer to a resource. Even if you have a URI like /a/b/c and an other URI /a/b that doesn't express that the first URI is a child of the second one or the second one a parent of the first one or that there is any relation between these two URIs at all. This relation is created through link-relation names though. This is a concept that sets the targeted resource in context to the current resource processed. You might have seen link relations like self, first, next, prev or last. This indirection mechanism allows clients to basically lookup the URI if interested i.e. in the next resource in the chain or the first one or the like. Ideally such link-relation names are registered with IANA. At times the definition for specific link relations might also be defined in special media types. If neither IANA nor the media type provide an adequate link relation name that expresses the proper intent you can define custom ones that follow the Web linking extension mechanism. Here you basically create link-relation names within your own namespace, i.e. https://acme.com/rel/order might express such relation name. Don't confuse it with a URI though. While it may return a human-readable description of its purpose, it is not mandatory though.

The third interface component in a REST architecture are media types. Media types are nothing more than a human-readable description of the syntax and structure of a document exchanged in that format. Media type definitions contain the elements that may appear in the document, as well as the context these elements may appear in and define the semantic of such elements. I.e. if you look at the HTML definition you may find the definition for ordered list entries (<OL>...</OL>) that may appear insight of lists (<LI><OL>first entry</OL><OL>second entry</OL></LI>). In contrast to common RPC solutions which usually fixate on one particular syntax, peers in a REST architecture couple to one or multiple media types. The media type therefore becomes the contract both clients and servers need to support in order to exchange messages. This is supported i.e. by the transport layer (HTTP) through content negotiation. This ensures that peers in such environments remain interoperable even though change may be introduced on the fly. This of course makes a mapping between the internal data structure of objects to and from the negotiated media type necessary. The media type therefore needs to be expressive enough to communicate the meaning of certain data to the client. Note however, as Fielding mentioned in his famous rant REST environments should never have typed resources that are significant to clients as the representations should follow a more or less common media type definition that allows the mapping from and to that representation to business objects. This way multiple different representation formats could be used to express the same resource state and depending on the capabilities and negotiated content type servers will provide the appropriate content to the client.

One of the main principles REST is build on top is that servers should teach clients on what they can do next. I.e. a server may internally use a state machine to guide clients though an order, payment and checkout process. At each step of the process the client is given further options through link relations which the client can chose one to progress further through its task of buying something. If the server needs further input from clients it may offer the client a resource that expresses some kind of input form, similar to HTML forms i.e. In that form structure the client might get taught the URI on where to send the request payload to, the HTTP operation to use as well as the media type the request should be marshalled to. In HTML this is usually implicitly given as application/x-www-form-urlencoded or might be changed to multipart/form-data i.e. Besides that the client also teaches the client about the properties the resource supports or the server expects to be defined. It may also teach the client on certain format the properties have and provide some help on generating UIs and what not (i.e. a date or color picker or the like).

This all allows peers in REST architectures to operate without the need of external API documentation like Swagger or OpenAPI and given support for the negotiated media types works out of the box for various different services without the need to change the client.

As this answer so far contained lots of theory but didn't address the issues of the OP so far, let's dive into the actual question:

So in general should I also create a DTO for my entity and use that DTO for the POST and then convert to the entity to be saved to the DB?

As your DB entities might not, with some given likelihood, be able to express all the possible options to clients and the data should be mapped to a representation format that follows the negotiated content type you need some mapping of the data to the actual response format. However, how this is done on the server side is an implementation detail and therefore completely up to you. A simple DTO as mentioned by you with some certainty might not be enough IMO, at least if you want to benefit from a REST architecture.

If you are though not interested in decoupling clients and services and treat REST just as buzzword for something that sends JSON over the Web to some HTTP Web service, than it is just personal preference IMO. DTOs may allow you to add and/or remove certain internal data to the response before actually sending it. Your framework of choice might also allow you to transform the data to certain response formats like i.e. application/hal+json or the like.

If your API though just exposes DB entries 1:1 to remote clients then I'd question the need for your "REST API" in first place as exposing the DB and/or views on certain tables/collections directly to the client might be more performant and spare you from the hazzle of managing a further API. Most people often forget that such DBs usually provide a well designed security concept and thus allow particular users to only read data while others can create aggregations and yet others are allowed to add particular information.

0

Typically yes, you want to separate the internal representation from the external representation. Right now the objects might look the same, but soon comes a feature where a field a convenience getter or a temporary attribute is added to the model and no one remembers that it also will be visible via the API. That will either confuse consumers of the API or the might start using it. Then you rename it or feel it is unnecessary and suddenly the API breaks. Similarly, if you need to add some field to the API level, suddenly your model needs to support that additional field whenever you use the object - no matter if it is a helpful attribute in your internal representation.

I would go with this as the default route - the additional transformation is in most cases negligible and the effort is a one time effort, with a tiny additional effort when you add fields to both the model and the outside representation. However, it protects you against unpredictable effort costs and user impact when introducing bugs or having to maintain a model class that also works as a outside representation. Note that this makes it also easier to find, e.g. annotations impacting the outside representation (if you use an autogenerated documentation of your API for instance as you can put all the API dto files into clearly structured packages).

Other benefits include: clear dependencies (no need to depend with an internal module/package on a module/package that holds the API), clear package structure (should be easy to locate any model file and any api dto representation).

Like any guideline, there could be cases where you want to or need to break it:

  1. Sometimes you really don't do anything with an object other than load it from a database and serve it. Personally though, then I would rather skip the model layer and directly produce a dto file in the service layer.
  2. If you build a minimum effort prototype, you might not need to maintain it or if so, just go with the fastest approach now and clean it up later
  3. If you really really need to aim for every inch of performance saving an object conversion can save you a tiny bit of time and some pressure on the garbage collector for high throughput endpoints, but I would not prematurely optimize for this. Typically there are other bottlenecks that come way earlier.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.