Reusing DTO for various request/response types vs explicitness of what is required / what should be returned

Bartosz picture Bartosz · Jun 4, 2017 · Viewed 11.4k times · Source

I started wondering on whether I am not falling into an antipattern here, so please advise on the best practices.

I am designing a REST API with a set of various endpoints and I wanted to wrap the request & response parameters into nice DTO.

For example, a few endpoints:

public async Task<JobStateResponse> GetJobState(JobStateRequest request);
public async Task<JobDownloadRespose> DownloadJob(JobDownloadRequest request);
public async Task<CreateJobResponse> CreateJob(CreateJobRequest request);

The problem is that these requests and responses are relatively similar DTO, for example:

public class JobStateResponse{
    public int TaskId {get;set;}
    public string ExternalId {get;set;}
    public State State {get;set;}
}
public class JobDownloadResponse {
    public int TaskId {get;set;}
    public string ExternalId {get;set;}
    public string JobContent {get;set;}
}

I thought about creating a base class for these and inheriting, but in some cases some of the properties can be redundant... Which means that the methods don't clearly indicate what parameters are needed for them to work OK.

I mean, exposing an API endpoint with a DTO parameter that has 7 properties but only really needs 2 sounds pretty bad...

On the other hand, maintaining separate DTOs for most of the endpoints seems like an overkill as well, and also a maintenance hell.

And also the last thing I want is a complex relationship of several base-base classes for the requests as this may be an even worse maintentance problem.

So, what is the proper approach for request<>response handling?

EDIT: Regarding the 'opinion based' flags - I am looking for best practice for handling this. I know it can be done in multiple ways, but I want to avoid a landmine / antipattern. Also, I gotta say I am pretty happy with the answers so far.

Answer

Rob Conklin picture Rob Conklin · Jun 4, 2017

Separate, simple DTOs will make your life infinitely easier. It will require more code, but it will be boring, simple code, that is easily tested, and allows your interfaces to change and evolve independently.

Make a DTO for each request endpoint and a separate DTO for each response. Otherwise, you will eventually be sad.

If you find elements that are common to multiple endpoints, extract them into their own object, and include them in both.

And yes, using inheritance here would be wrong. Stick to compositional patterns and you will be fine.