2

I'm starting to work with dto's (data transfer objects) and I have some doubts about the best way to build the system architecture of the API.

Imagine a domain entity 'A', with relations to 'B', 'C' and 'D'. We have a service 'S' that return a json list with all "A's". It's correct to create an 'ADTO' in that service, fill with "BDTO's", "CDTO's" and "DDTO's"? If then we have another service "S2", and we need to return an specific set of "B's", then we need to create another tree of "B2DTO's" with "C2DTOS's", "D2DTO's"... ? Is this the correct way to do it?

I see that this way, we'll have a huge and complex tree of DTO's, with an specific DTO's for each use case.

EDIT:

I forgot the assemblers part. Is necessary to implement a different assembler for every DTO? for example, for an entity A, we have two DTO's. Can I use the same assembler or is better to have A1Assembler and A2Assembler?

Ijuhash
  • 137
  • 13
  • Looks like your service boundaries are not clearly defined. Your S should either only hold A and return ADTO without any additions, or it should have the knowledge and state for all A, B, C and D and return any DTO that these objects can form as read models. – Alexey Zimarev Dec 19 '16 at 09:23
  • The problem is that ADTO is filled with BDTO, CDTO, etc... but service S will only return an ADTO, how to work with that? Can a DTO hold another DTOs? – Dani Tome Dec 19 '16 at 09:25

2 Answers2

1

I think you mistake what your DTO's are. You'll have 2 kind of DTO's Roughly speaking 1) they can be your domain entities, then you can return ADTO, BDTO and CDTO. But those DTO's can be fairly consistent (why would B2DTO be any different from BDTO)

If you look at what your json would look at

{
 Id: 1
 name: "foobar",
 $type: "A",
 B: [ {
      name: "b-bar",
      $type: "B"}]
 CIds: [ 2,23, 42]
}

Here you see 2 kind of objects, some (B's) are returned in full in your DTO as subobjects. Others (like C) are turned by Id and can be queried separately. If it's S2 which implements the C query or not you don't care about.

2) When you get to an architecture like CQRS then you do get different DTO's. (projections or commands) but then you would also see this in the naming of the DTO's. Forexample are AListOnOverviewPageDTO, AUserEditDetailDTO, etc.

Now it makes very much sense to have different DTO's since they are projections representing very different usecases. (and not a full object as is common in DDD)

Update The reason you want different DTO's are twofold. First of all it allows you to optimize each call separately. Maybe the list needs to be faster so (using CQRS) you can put the right indexes on your data so your listDTO will be returned faster. It allows you to reason about usecases more easily. Since each DTO represents 1 usecase/screen (Otherwise you get cases ok in the userlistDTO i need to populate only these 3 fields in this case ..etc.). Furthermore you need to make sure you API is honest. NEVER EVER return a "age" field with empty data but have some other call return the same user but with another call return the same user with a real age. It makes your backend appear broken. However if i had a call to /users/list and another call to /users/1/detail it would be natural if the detail calls returned more fields about a specific user

Batavia
  • 2,424
  • 12
  • 16
  • Imagine a domain entity "User", and then an Api call to get all users (here you have a userListDTO), and later, another call to get all users with an aditional information. Is neccessary to create an userExtraListDTO?, and another call to get only the names of the users with here age. It's correct to implement 3 DTO's here for the 3 different responses or better to have one and fill it with the necessary? (leaving the other fields empty). If the answer is an implementation for case (3 in this example), the same question with assemblers: 3 different assemblers or one more generic? – Ijuhash Dec 20 '16 at 07:19
  • Yes i would often create 3 DTO's for that. It has 2 advantages 1) security. I can controll access to each call/DTO separately. It also makes your backend easier to reason about security. 2) I am honest about the information i return. Returning an empty age when another call will return a correct (non-empty) age makes for very unfriendly API's. All these 3 DTO's can however be in the same assembly, that's not a problem – Batavia Dec 20 '16 at 10:49
  • Thank for the answer. I imagined that use the same assembly would't be a problem. – Ijuhash Dec 21 '16 at 06:58
1

Your DTOs should represent a set of data that you want your client to have. Usually, you should never 'copy' your entities into DTOs because you may have fields that you don't want to share with the world. Let's supposed that you are creating automatically a 'tracking' column with the ID of who entered that data, or say that you have a Customer entity with password fields. You don't want that to be part of your DTOs. That's why you must be EXTRA CAREFUL when using AutoMapper etc.

When you design DTOs think about what your client needs from that endpoint specifically. Sometimes DTOs may look the same and that's ok. Also, your DTOs can be as simple or as complex as needed. One crazy example, lets say that a page where you show an artist, his songs, the voting rate for those songs and some extra data.

If your use case justifies it, you may very well put all of that into a DTO. DTO all they do is carry data.

YES, your services should return DTOS (POCO).

Also, DTO is just naming convention. Don't get caught up in the "dto" suffix. A 'command' coming from a client is a DTO, but in that case you would call it AddNewCustomerCommand for example.

Makes sense?

Pepito Fernandez
  • 2,116
  • 5
  • 27
  • 43
  • Thank you. I Know that a DTO is a carrie bag to store only plain information, and that the service's must return the DTO, not the domain entity. When you talk about command, are you talking about the command pattern? As far as I know, the command pattern is one thing and a DTO is another. – Ijuhash Dec 20 '16 at 07:22