Reusing DTO for various request/response types vs

2019-04-06 05:37发布

问题:

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.

回答1:

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.



回答2:

Using separate DTO enables you to optimise data transfer by the behaviour of the data access calls, it also allows you to restrict access to properties you may not want to expose. Although it is more code, you are defining exactly what is being exposed and are in full control of that.



回答3:

You definitely want to separate DTOs per endpoint into 2 gruops: request and response ones.

Now, speaking of inheritance, you could have base Request and base Response class which would include some general, non-specific things (for example, request&response wrapper message into which you plugin your data) but you don't want to mix inheritance between request and response side.

As others already pointed out, it is a bit more code at the begining but that way you'll never hit the wall. On a contrary, mixing them will pretty soon make you have huge refactorings where you'll lose more time&nerves than starting in a clear, separate way.