49

I am working on RCP application, I'm new to this application.

Spring beans are used to write business logic to save/fetch entities.

But, instead of sending entities directly to client we are converting to DTOs and populating client. While saving, we again are converting DTO to entity and saving.

What's the benefit of these conversions? Can someone explain?

Dherik
  • 2,406
  • 20
  • 33
Naveen Kocherla
  • 613
  • 1
  • 6
  • 5
  • 1
    `What's the benefit of these conversions?` decoupling the persistence data model from the data model (representation) offered to the consumers. The benefits of decoupling has been widely discussed in S.E. However, the aim beneath DTOs is to gather in a single response as many info as deemed necessary for clients to save calls to the server. What makes the communication client-server smoother. – Laiv Jun 28 '18 at 13:02
  • 2
    Possible duplicate of [Is it good practice to use entity objects as data transfer objects?](https://softwareengineering.stackexchange.com/questions/350067/is-it-good-practice-to-use-entity-objects-as-data-transfer-objects) – Dherik Jan 15 '19 at 11:05
  • Your example is nice. When you are the client (views...) is painful to change, but the biggest problem is when the system already have integrations of 3th party, that is impossible to change (contract, fees...). If your system will have third-party integration, ever use DTO. – Lucas Gonçalves Jan 30 '19 at 17:09

1 Answers1

110

Whenever a developer asks "what's the point of doing this?", what they really mean is "I see no use case where doing this provides a benefit". To that end, let me show you a few examples.


All examples will be based on this simple data model:

A Person entity has five properties: Id, FirstName, LastName, Age, CityId

And you can assume that the application uses this data in many varied ways (reports, forms, popups, ...).

The entire application already exists. Everything I mention is a change to the existing codebase. This is important to remember.


Example 1 - Changing the underlying data structure - Without DTO

The requirements have changed. The age of the person needs to be dynamically retrieved from the government's database (let's assume based on their first and last name).

Since you don't need to store the Age value locally anymore, it therefore needs to be removed from the Person entity. It's important here to realize that the entity represents the database data, and nothing more. If it's not in the database, it's not in the entity.
When you retrieve the age from the government's web service, that will be stored in a different object (or int).

But your frontend still displays an age. All the views have been set up to use the Person.Age property, which now no longer exists. A problem presents itself: All views that refer to the Age of a person need to be fixed.


Example 2 - Changing the underlying data structure - With DTO

In the old system, there is also PersonDTO entity with the same five properties: Id, FirstName, LastName, Age, CityId. After retrieving a Person, the service layer converts it to a PersonDTO and then returns it.

But now, the requirements have changed. The age of the person needs to be dynamically retrieved from the government's database (let's assume based on their first and last name).

Since you don't need to store the Age value locally anymore, it therefore needs to be removed from the Person entity. It's important here to realize that the entity represents the database data, and nothing more. If it's not in the database, it's not in the entity.

However, since you have an intermediary PersonDTO, it's important to see that this class can keep the Age property. The service layer will fetch the Person, convert it to a PersonDTO, it will then also fetch the person's age from the government's web service, will store that value in PersonDTO.Age, and passes that object.

The important part here is that anyone who uses the service layer doesn't see a difference between the old and the new system. This includes your frontend. In the old system, it received a full PersonDTO object. And in the new system, it still receives a full PersonDTO object. The views do not need to be updated.

This is what we mean when we use the phrase separation of concerns: There are two different concerns (storing the data in the database, presenting data to the frontend) and they need a different data type each. Even if those two data types happen to contain the same data right now, that might change in the future.
In the given example, Age is a difference between the two data types: Person (the database entity) doesn't need an Age, but PersonDTO (the frontend data type) does need it.
By separating the concerns (= creating separate data types) from the beginning, the codebase is much more resilient to changes made to the data model.

You might argue that having a DTO object, when a new column is added to the database, means you have to do double work, adding the property in both the entity and the DTO. That is technically correct. It requires a bit of extra effort to maintain two classes instead of one.

However, you need to compare the effort required. When one or more new columns are added, copy/pasting a few properties doesn't take all that long. When the data model changes structurally, having to change the frontend, possibly in ways that only cause bugs at runtime (and not at compile time), takes considerably more effort, and it requires the developer(s) to go hunting for bugs.


I could give you more examples but the principle will always be the same.

To summarize

  • Separate responsibilities (concerns) need to work separately from each other. They should not share any resources such as data classes (e.g. Person)
  • Just because an entity and its DTO have the same properties, does not mean that you need to merge them into the same entity. Don't cut corners.
    • As a more blatant example, let's say our database contains countries, songs and people. All of these entities have a Name. But just because they all have a Name property, doesn't mean that we should make them inherit from a shared EntityWithName base class. The different Name properties have no meaningful relation.
    • Should one of the properties ever change (e.g. a song's Name gets renamed to Title, or a person gets a FirstName and LastName), they you'll have to spend more effort undoing the inheritance which you didn't even need in the first place.
    • Although not as blatant, your argument that you don't need a DTO when you have an entity is the same. You're looking at the now, but you're not preparing for any future changes. IF the entity and DTO are exactly the same, and IF you can guarantee that there will never be any changes to the data model; then you are correct that you can omit the DTO. But the thing is that you can never guarantee that the data model will never change.
  • Good practice doesn't always pay off immediately. It might start paying off in the future, when you need to revisit an old application.
  • The main killer of existing codebases is letting the code quality drop, continually making it more difficult to maintain the codebase, until it devolves into a useless mess of spaghetti code that's unmaintainable.
  • Good practice, such as implementing a separation of concerns from the get to, aim to avoid that slippery slope of bad maintenance, in order to keep the codebase maintainable for as long as possible.

As a rule of thumb to consider separating concerns, think of it this way:

Suppose that every concerns (the UI, the database, the logic) is handled by a different person in a different location. They can only communicate by email.

In a well-separated codebase, a change to a particular concern will only need to be handled by one person:

  • Changing the user interface only involves the UI dev.
  • Changing the data storage method only involves the database dev.
  • Changing the business logic only involves the business dev.

If all of these developers were using the same Person entity, and a minor change was made to the entity, everyone would need to be involved in the process.

But by using separate data classes for every layer, that issue isn't as prevalent:

  • As long as the database dev can return a valid PersonDTO object, the business and UI dev do not care that he changed how the data is stored/retrieved.
  • As long as the business dev stores the data in the database, and provides the needed data to the frontend, the database and UI devs don't care if he decided to rework his business rules.
  • As long as the UI can be designed based around the `PersonViewModel, then the UI dev can built the UI however they want. The database and business devs don't care how it is done, since it doesn't affect them.

The key phrase here is since it doesn't affect them. Implementing a good separation of concerns seeks to minimize affecting (and therefore having to involve) other parties.

Of course, some major changes can't avoid including more than one person, e.g. when an entirely new entity is added to the database. But don't underestimate the amount of minor changes that you have to make during an application's lifetime. Major changes are a numerical minority.

Flater
  • 44,596
  • 8
  • 88
  • 122
  • 2
    Comprehensive answer, Thanks. I have questions; Appreciate if you answer: **1**- Correct me, if i'm wrong. **Business Object** or **View Object** is for transferring data between **Presentation Layer** and **Business Layer** and **Entity Object** is for transferring data between **Business Layer** and **Data Access Layer**. and DTO can be used as a dumb BO. **2**- Assume that two views need different info of a company then we need two different companyDTOs? – Arash Nov 06 '19 at 10:24
  • 4
    @Arash (1) "DTO" is really a catch-all definition for any data class that is used for exchanging between two layers. A business object and a view object are both DTOs. (2) That very much depends on a lot of things. Creating a new dto for every collection of fields you requires is a cumbersome task. There's nothing inherently wrong with simply returning a full company DTO (where reasonable) and then letting the view pick the fields it's interested in. It's a matter of finding the balance between implementing adequate separation of concerns and avoiding overengineering and pointless repetition. – Flater Nov 06 '19 at 11:18
  • Now that's make sense to me. Thanks a lot. Flater. – Arash Nov 06 '19 at 16:13
  • One more question. You said "business object **and** a view object". I thought that both are equal. When I searched, I realized that **Business Object** has general meaning to compare with **View Object**. But **Business Object** should be derived from **use case** and **Entity Object** should be derived from **Data Model** therefore they are different, am i right? could you explain it a bit, please? – Arash Nov 06 '19 at 17:02
  • @Arash: The difference between what you call a "business object" and a "view object" is **context**. To us humans, that distinction matters to understand things properly. But the compiler (and by extension the language itself) doesn't see a technical difference between them. When I say they're the same, I mean that from a technical perspective. Both are just a class with properties intended to hold data and be passed around. In that regard, there is no difference between them. – Flater Nov 27 '19 at 11:24
  • DTOs are a simple protocol in the form of a data structure all parties agree on. You can also see them as arguments objects. Languages that support anonymous records (whether it calls them "dicts" or "objects" or whatever) tend just pass those around rather than creating whole new DTO classes and get largely the same benefits. You do get type safety with DTOs but structural subtyping would work just as well (which I don't imagine java will ever get) – Chuck Adams Jan 13 '20 at 17:18
  • @ChuckAdams: For the purpose of my answer specifically, there's no reason to distinguish between a DTO class and a collection containing the same data. The question and answer focus on the the necessity of the _layer/abstraction_ rather than its specific structure. – Flater Jan 16 '20 at 10:35
  • @Flater I understand, I was trying to "demystify" DTOs by presenting them as a verbose-but type-safe convention for named arguments. Nothing to be persisted or treated as the forever gospel of one's business object schema. In that sense, it's just a "glue layer", not a Great Abstraction Of Enterprisiness ;) – Chuck Adams Jan 16 '20 at 22:45
  • what if we dont create the DTO, and the Service class adds the age to the entity (like it would for the DTO) ? – Gilad Baruchian Apr 26 '20 at 07:51
  • 1
    @GiladBaruchian: From the answer: _"It's important here to realize that the entity represents the database data, and nothing more. If it's not in the database, it's not in the entity."_ What you're suggesting is to use a DTO but call it an entity. That's just going to lead to confusion and muddying the separation between your persistence and domain/business layer. – Flater Apr 26 '20 at 12:41
  • 1
    @Flater i am working on a big application and we have very big and complex object, just because of this concept we are doing so much work and have many bugs because of mapping, and 99% of the time the DTO and entity are exactly the same – Gilad Baruchian Apr 27 '20 at 13:43
  • @GiladBaruchian That sounds like it could be fixed with more testing and making use of automatic mapping (e.g. Automapper), instead of being an argument against separation of concerns. Good practice sacrifices effort now in order to avoid a lot more effort down the line. Don't fall into the trap of avoiding the initial extra effort because in the long term it leads to even more effort, which is the opposite of what you'd want. – Flater Apr 29 '20 at 08:16
  • Is it a good idea to return dto in service or do the conversion in controller. If we return that in service layer(in 3 layered architecture), will we not have problem when we need to version APIs(REST)? – pinkpanther Aug 31 '21 at 14:43
  • @pinkpanther: Clean coding is a spectrum. On the clean end of that spectrum, _every_ layer should have their own DTOs. However, in reality, that is usually considered overkill, and codebases tend to specify one level of DTO, usually on the business level. Note, however, that e.g. wrapping the business DTO in a viewmodel of sorts is a separate consideration here; a viewmodel does not supplant the need for a business DTO. – Flater Aug 31 '21 at 14:48