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.
A DTO (Data Transfer Object) is an object that defines how the data will be sent over the network or application. On the other hand Response Object which usually holds response data from DTO's or WebApi's or Data Access or any other responses to client.
You do not need another DTO for create operation. You just set the default value(id=0) for creating a new object. This will help you for figuring out if the object is yet to be created in database in case you have to.
DTO, which stands for Data Transfer Object, is a design pattern conceived to reduce the number of calls when working with remote interfaces. As Martin Fowler defines in his blog, the main reason for using a Data Transfer Object is to batch up what would be multiple remote calls into a single one.
A DTO is helpful whenever you need to group values in ad hoc structures for passing data around. From a pure design perspective, DTOs are a solution really close to perfection. DTOs help to further decouple presentation from the service layer and the domain model.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With