3

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?

2 Answers 2

2

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.

0

I find it best if your DTO matches your back-end data structure.

In this case, if you are using a relational db, presumably your back-end can retrieve your customer and its associated store number via the customer id. Therefore you can structure your order DTO thus:

class Order
{
    int order_no;
    int customer_no;
    ...other order info
}

and Customer:

class Customer
{
    int customer_no;
    int store_no;
    ..... extra customer info
}

Which would match the table columns and their relationships.

If however, you are using a no-sql approach you should probably send the whole customer object with the order, including customer name, address etc. as the back-end will be treating the order as a single entity with attached data, and will need the customer details in order to fulfill the order.

ie:

class Order
{
    int order_no;
    ...other order details
    Customer customer
    {
        int customer_no;
        ....name address etc
        Store store
        {
            int store_no;
            ....other store details
        }
    }
}
3
  • But what happens in the (very common) situation where you need to display the customer name, but would need to store a new customer ID back to the DB, when a user edits an order? This I think it part of why the OP asked the question. In business apps, it's most common to display the "name" and capture a changed ID to store back to the database. Should a new customer DTO be created, with just fields ID and Name? And that DTO be a property of the Order DTO?
    – HardCode
    Commented Oct 23, 2019 at 19:18
  • The same usually applies to most complex business DTOs. There's the ... aggregate root? ... that contains many other DTOs, but only those child IDs need to be persisted to the database when added/removed, but summary details of those children need to be displayed on-screen. It seems that most blogs, articles, etc. seems to answer this dilemma for one direction only (i.e. an article only talking about persisting to the DB, or only talking about reading to display on-screen. Hardly ever a complete picture for both display and persistence when designing classes).
    – HardCode
    Commented Oct 23, 2019 at 19:21
  • yeah, fetch them seperately as needed
    – Ewan
    Commented Oct 23, 2019 at 20:12

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.