Composition of Data Transfer Objects

compositiondata structuresdto

Let's say I want to create a shop/order system.
I'll have an Order DTO to which I'll map the data from a request and results from the database.
The object will consist of an order number, a customer number and a list of articles.

The order number is pretty simple, I'll just use an int to store the number. But what about the customer? I'll also have a Customer DTO so I could use it inside of the Order DTO instead of saving the customer number as int.

In my case the customer number consists of a store_no (the store where the customer is registered) and a customer_no (store internal customer number).

To me it seems to be cleaner to have a structure which looks like this:

Order {
 int order_no;
 Customer cust;
}

Customer {
 int store_no;
 int cust_no;
}

instead of this:

Order {
 int order_no;
 int store_no;
 int cust_no;
}

Customer {
 int store_no;
 int cust_no;
}

My problem:
The Customer DTO will have a lot more information than just the customer number. Like a first name, last name, address, user name, some flags and so on. Things I don't care about when I just want to save an order with order_no 123 for the customer with store_no 12 cust_no 543.

So in this case using the Customer DTO inside of the Order DTO seems to be 'too much'.

Same goes for the articles.

Are there any best practices how to create such DTOs? Encapsulate DTOs or repeat the same information in different DTOs?

Best Answer

My problem: The Customer DTO will have a lot more information than just the customer number. Like a first name, last name, address, user name, some flags and so on. Things I don't care about when I just want to save an order with order_no 123 for the customer with store_no 12 cust_no 543.

A data transfer object's purpose is to represent the data to be transferred to another process, e.g. from your backend to a web frontend. If the information you are transferring has a nested structure, also using a nested type for the DTO does make sense. However, the DTO must not include unnecessary data you aren't actually using – that's just misleading.

A DTO is absolutely not the same as your domain model. Your model describes entities like a Customer or an Order with all properties that are relevant for your problem domain. This includes properly modelling the relationship between an Order and a Customer. It is not unusual to end up with a complex object graph describing all of these relationships (and equivalently: many foreign keys in a relational database).

It sounds to me like your Customer “DTO” is actually an accurate domain model, not just a DTO. It would therefore be wrong for an Order to include a full Customer instance, if all that is transferred is actually just an ID. Assuming you're transferring this data as JSON, I'd just look at the JSON and derive my DTOs from that:

{
  "order_no": 1234,
  "store_no": 1234,
  "cust_no": 1234
}

→ best represented as a single, flat DTO.

{
  "order_no": 1234,
  "customer": { "cust_no": 1234, "store_no": 1234 }
}

→ probably a separate Order and Customer type.

Another possible approach is having different DTOs for different levels of detail, e.g. a customer could be represented as a full customer with complete profile information, or just as a brief customer summary with only the most essential information, or even just the ID. This adds a lot of complexity since the same entity can be represented by multiple DTO types, but keeps you from transferring unnecessary data. That requires a design decision.

Related Topic