1

I work with Spring applications. Recently I have found this article about the Anemic Domain Model.

They recommend putting logic in Entity classes. It solves a problem that Martin Fowler described in his article. We can move logic from our service layer to the domain layer. It sounds good.

But can we do the same with our DTOs? There are plenty of methods with a logic that would be convenient to put in those DTOs. Or they should remain as bags of getters and setters?

For example, can we move this method to ExpenseSearchDTO?

private void processSearchChartDTO(ExpenseSearchDTO expenseSearchDTO) {
        if (expenseSearchDTO.getDateTo() == null) {
            expenseSearchDTO.setDateTo(LocalDateTime.now());
        }
        if (expenseSearchDTO.getDateFrom() == null) {
            expenseSearchDTO.setDateFrom(LocalDateTime.of(0, 1, 1, 0, 0));
        }
}
2
  • 1
    Those steps look like they should be done in the constructor for ExpenseSearchDTO – BobDalgleish Mar 12 '20 at 13:37
  • @BobDalgleish it makes sense. But I receive ExpenseSearchDTO from external service and it's already populated. – Valerii Sloboda Mar 13 '20 at 7:53
1

The example you mention seems like default values for some internal members. It should be in the constructor of the object.

To your larger point about DTOs: The point of having "rich" objects is that the behavior should live with the object that contains the data for that behavior. So yes, behavior should go to their corresponding "DTO"s. If you do that consistently, pretty soon there will be no DTOs.

There is very little reason to have DTOs in the first place. Some people say it is necessary to transport data between layers. But that is a design decision. You can design software that doesn't need to share pure data between the layers, or have no layers in the first place.

All in all, DTO/Service based designs are rarely if ever compatible with even the basic concepts of object-orientation. Not that that is a problem in itself, just saying.

4

A Data Transfer Object’s sole responsibility is to act as boundary between layers.

If Layer A has data that layer B needs and you don’t want to do direct calls to reduce coupling, you create a DTO to transfer the data.

As soon as you put logic into a DTO, not only is it no longer a DTO by definition, but you create the coupling that you wanted to avoid in the first place.

1
  • It makes sense. But what should I do with methods like above? Leve it in the service layer? – Valerii Sloboda Mar 12 '20 at 11:08
2

A common misconception about Data Transfer Objects is that they transport data between layers of an application, and people infer this to mean between layers of an application that exist in the same running process.

DTOs were conceived as a means to transport data between processes. When data is transmitted from one process to another it gets serialized into another format. Text based formats like XML or JSON are most popular (think "web services" here), but other formats exist as well, and can be specific to the two processes exchanging communication. DTOs are basically handy bags of data that represent a whole bunch of parameters in a single variable, instead of tracking all the parameters in separate variables.

The Rich Domain Model ends where the running process of the application ends. It begins again in another process, but that is a separate Rich Domain Model. The DTO is an application specific rendition of that transported data. You would need to write code that maps DTOs to your Domain objects, which makes this transmitted data compatible with your own Rich Domain Model.

When you're working with a remote interface, such as Remote Facade (388), each call to it is expensive. As a result you need to reduce the number of calls, and that means that you need to transfer more data with each call. One way to do this is to use lots of parameters. However, this is often awkward to program - indeed, it's often impossible with languages such as Java that return only a single value.

The solution is to create a Data Transfer Object that can hold all the data for the call. It needs to be serializable to go across the connection. Usually an assembler is used on the server side to transfer data between the DTO and any domain objects.

Source: Data Transfer Object by Martin Fowler

1

The boundary that creates the need for DTOs isn’t simply layers and it isn’t simply processes. The boundary is any line that you can’t move methods across. Sometimes this is because of the limits of your own control over the code.

This might be a layer or a process, but it can simply be a framework or even code from a different team within your own business.

DTOs happen when you can’t move methods to avoid moving data. For whatever reason you must now break encapsulation and admit what values these fields have and their data types. You can’t hide that information behind domain abstractions the way OOP people like to do.

But just because this happens doesn’t mean you or ‘that other team’ must spread knowledge of that DTO all over your respective domains. You can isolate knowledge of those details by creating a class that eats the DTO and hides it’s details for as long as it can.

This class can have all the methods needed to do everything there is to do with those details without wrapping them in encapsulation breaking getters. This will work right up until you run into a need to run those details through a method that you can’t move into the code you control and must make a new DTO to send off for this other code to consume.

If your own code is on both sides of a layer that you’re sending DTOs across understand that this is a self imposed restriction. You may be doing this because isolating knowledge of an implementation is more important than isolating data in this case. That’s fine. But at least understand that you’re doing it out of choice.

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.