0

I'm wondering what is a clean solution for representing an entity in many different format. Let's suppose I have the following entity:

Person {
    Long id;
    String title;
    String firstName;
    String lastName;
    Date birthDate;
    List contacts;
    Address address;
    ...
    Date joinDate;
    Date lastLoginDate;
    Status status;
}

And I have a web application where I need to display different data from the Person entity depending on where it is needed. For example:

  • Public GUI header: firstName, city(from address), email(from contacts)
  • Public GUI profile: title, firsName, lastName, birthDate, contacts, address
  • Admin persons list: id, fullName, birthDate, joinDate, lastLoginDate, status
  • Admin person edit: all the fields
  • REST API: id, title, firstName, lastName, age(as int and not birthDate), address
  • Report: id, fullName, fullAddress, birthDate, login attempts (not part of the main entity, get from other service), audit logs (not part of the main entity, get from other service)

In general we know the person's id and can get the entity like personRepository.getById()

I can just send the Person entity to the various representation layers and get what it needs, but in this case I have to repeat formatting like fullName concatenation, I can accidentally expose data in case of the REST API, in case of list pages it can cause performance degradation, and so on.

Or, I can create unique representation for each case so in this example:

  • PersonPublicHeaderViewData
  • PersonPublicProfileViewData
  • PersonAdminListViewData
  • PersonAdminEditViewData
  • PersonRestViewData
  • PersonReportViewData

In this case I also need some factories that can build the views from the original entity:

  • PersonPublicViewFactory: creates header and profile views
  • PersonAdminViewFactory: creates admin list and edit views
  • PersonRestViewFactory: creates the REST view
  • PersonReportViewFactory: create the report view with the additional external data

The second solution seems to be cleaner but requires a lot of extra classes.

What do you think? Any other idea?

  • Have you looked into libraries like Automapper? (Your question seems to imply you didn't but I wanted to make sure) – Flater Jun 14 at 9:59
  • No I didn't. I'm more interested in architecture point of view what is a good solution. – Vmxes Jun 14 at 10:23
  • I suggest not jumping to architectural decisions if you haven't look at lower level libraries yet. The solution is likely simpler than you're making it out to be. At the very least I'd remove the assertion that you need factories in your answer as that is not necessarily the case. – Flater Jun 14 at 10:25
  • @Laiv: No, I'm countering the question's assertion that the second option requires making factories. Factories are one option but definitely not the only one. Secondly, there's a big difference between building the architecture based on libraries, and simply not custom designing a solution for a problem which is already trivially solved with a widely used library. OP's problem isn't a rarely encountered issue that requires custom attention, it's the exact target audience of libraries like Automapper. – Flater Jun 14 at 10:29
  • @Vmxes dogmatically speaking, from the clean architecture standpoint, these "ViewData" objects are fine. In most of the cases, having many but different plain objects is not a design flaw. You might want to change this in favour of a more maintainable solution. One that takes less code. Have you considered not typing these objects? If they are meant to be serialised and nothing else, then a mere Map-like data structure could make the job. In other words, to make the ViewData dynamic – Laiv Jun 14 at 10:30
1

After the comment exchange, you mentioned that the core of your question is on the necessity of custom DTOs as opposed to sending the basic entity to your consumers.

I think the most important for me that using custom DTOs is the way to go instead of giving the main entity to everyone.

That is mostly a consideration you have to make yourself. You have to weigh the pros and cons

Pro creating custom DTOs

  • It enables you to hide data from consumers that don't care about it.
    • This counts double for privacy concerns!
  • It reduces bandwidth as you send less useless data to your consumer.
  • Shorter DTOs are nicer to handle, even if only from an Intellisense/documentation point of view.
  • It enables you to apply custom formatting for some of your consumers and not others.

Pro using the full entity

  • You don't need to develop additional DTOs
  • You don't need to develop different calls to retrieve the correct DTO and your consumer doesn't need to pick the right DTO for them
  • If the entity changes, it takes (marginally) less effort to update it, compared to also having to update the DTOs and their mappings.
  • No need to create DTO factories, BUT: you mentioned needing to also use factories when you do DTOs, but that not necessarily the case. In the comments we already talked about Automapper (and similar tools) and how it can help cutting down on the boilerplate factory code.

That's all there is to it. This is mostly an effort-vs-feature consideration. As far as I'm aware, the community tend to lean towards custom DTOs here because it simplifies data maintenance in the future and enables a cleaner separation of layers.

1

You can use the Data Transfer Object Design Pattern (DTO)

DTO (Data Transfer objects) is a data container for moving data between layers. ?>They are also termed as transfer objects. DTO is only used to pass data and does >not contain any business logic. They only have simple setters and getters.

For example, below is an Entity class or a business class. You can see that it has business logic in the setters.

    class CustomerBO
{
        private string _CustomerName;
        public string CustomerName
        {
            get { return _CustomerName; }
            set 
            {
                if (value.Length == 0)
                {
                    throw new Exception("Customer name is required");
                }
                _CustomerName = value; 
            }
        }
}

A data transfer object of the above Customer entity class would look something as shown below. It will only have setters and getters that means only data and no business logic.

    class CustomerDTO
{
        public string CustomerName { get; set; }
}

If you have the same set of properties, you can build a BaseDto and embed the properties that exist in all DTO . for example

    public class BaseDto
{
     public Long id{get; set;}

     public string firstName{get; set;}

     public string lastName{get; set;}
}

And

   public class PersonDto extends BaseDto
    {
         public Address Address{get; set;}

         public string title{get; set;}

         public Date birthDate{get; set;}

         public List contacts{get; set;}
    }

You can find out more here

0

If you are serious with object-orientation and maintainable code, you shouldn't use DTOs at all. Also, "entities" are not data bags either.

One proper solution would be to include the things you want into the object itself. It would look like this:

public final class Person {
   public Component displayPublicHeader() { ... }

   public Component displayPublicProfile() { ... }

   public Json toAPI() { ... }

   public Report generateReport(); { ... }

   ...
}

Such a design is much more maintainable than having DTOs and no clue what happens with the data in various parts of the system. Things that belong together should be together. These things belong together and likely change together. In addition the methods are business-relevant, they all represent functions from the requirements.

Also, the full name handling should not be in here. That should be in a class called FullName. Which is also not a DTO, but a real object that knows how to present itself in various formats if need be.

0

I have a technical solution to this problem that you may not use but will still be a good example : Jackson with JsonView.

By annotating the corresponding fields properly and annotating the entry point, you can selectively serialize only the field you want... without having to maintain multiple DTO.

Here is one of the many exemple on the web : https://www.baeldung.com/jackson-json-view-annotation

0

I strongly support Robert Brautigam’s answer. Had I the rep I would thumb it up and comment as opposed to giving an answer.

I would like to add to Robert’s answer by sharing another SE member, Ben Aaronson, wrote in regard to duplication in the SE question titled, “Illusory code duplication.” This is all in the context of clean code or SOLID principles. SE Member, Mael posed the question.

In a nutshell Ben states, duplication is not about code but rather knowledge. Knowledge equates to business rule or a data entity.

For example:

One class has knowledge of two more business rules or data entities. Or stated in another way one class is performing work on two or more different kinds of business rules. That’s violates SRP. The one class may change from two or more rules may change.

Multiple classes performing work on one business rule. That’s violates SRP. If a business rule changes, then the change take place in all classes as opposed to one.

One class handles one business rule. That’s SRP compliant. One Responsibility Rule. It doesn’t matter if the class may get a little big and appear to be a code smell. Why? Because its one class handling its responsibility, its knowledge is in one place, where there is only one reason for change. That equates to being clean.

You can still be SRP compliant should there be a need to use a strategy pattern or decorator patter and delegate work outside your class. You would pass to the strategy or decorator via Dependency Injection the required data in order for those patterns to do their work. (That means passing an object to the pattern object’s constructor.)

Adding to Robert’s point of view of object-oriented design.

Programmers will take up issues, “how in the world do you get object state and/or data without getters and setters”, such as DTOs. Its easy, the class does the work instead. You tell the class what to do as opposed asking it about its state.

I’ll use a winforms example because that’s my strong suit.

A listview control on a winform needs to display product data that’s in a table from sql server. Many programmers will violate the Law of Demeter and build entities with getters/setters and DTOs. Instead of doing that, the product class builds a listview item that the listview control uses and adds to it its items collection.

So instead of iterating through a for each loop reading getters and collecting object state, the object itself is performing an action by creating an ListView Item, (object or struct) for the listview (other control) may use.

Use an interface for each task that your class will perform. This will limit the client to tell your object to do something and protect your object from unnecessary access.

Example:

Coding to an interface: IProduct product = new Product();

The interface limits the client to only what it needs, protecting the object.

Coding to implementation: Product product = new Product();

The client has access to everything about the product.

Hope this helps.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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