Data Transfer Objects

My observations on data transfer objects

  1. They should not be a one to one representation of your domain classes.
    Sometimes you want a subset of the information of a single instance, sometimes your DTO will collect data from various instances (starting with an aggregate root). You might create different types of DTO from the same domain classes based on what the user needs to do. Sometimes the user only wants OrderNumber + DateRaised + TotalValue (to show as a list of orders), sometimes they want the entire order details (including lines) for editing.

  2. The domain classes should have no knowledge of your DTO classes.
    So you shouldn't have any methods such as

    pubic PersonDto CreateDto();
    public UpdateFromDto(personDto);

    DTO's are not part of the business domain, so you should never do this!

  3. The DTO you send out might not be the same type as you get back.
    For example you don't want the user to edit the order number or the date raised. If there are only a couple of properties like this you might opt to use the same DTO class but just ignore those properties when updating the domain instances, or you might decide on Request/Response DTOs

  4. The DTOs should have no knowledge of the domain classes.
    This is because the DTO classes will be shared between the client and the server, and the client will have no domain class knowledge. If you have domain classes on the client then you probably don't need DTOs.

  5. There is only one way to update domain instances from a specific DTO.
    A DTO doesn't always update domain classes, but when you receive a specific kind of DTO and need it to update domain instances it will always update in the same fasion.
So, how do you create DTOs from domain instances and how do you update domain instances from DTOs? The first thing to be aware of is that this code belongs in a layer above the domain layer, in a services layer or the app layer itself. Then we need some way of saying

  "I want to convert this Order to an OrderSummaryDto"

or

  "I want to convert this Order to an OrderDto, which includes its order lines"

and then finally

  "I have received a DTO, I need to update the relevant domain instances"

We need to do this in a reusable way, because the same DTOs may be reused in various parts of the application layer. To achieve this I used the Dependency Injection Container from Microsoft named "Unity".

Naming conventions I used are
  • xxxDtoFactory - Creates a DTO of a specific type from a specific domain instance.
  • xxxDtoTranslater - Takes a DTO of a specific type and translates its values back into the domain instances (updating existing instances, creating new instances, or whatever is required.)
The example model has a class named "Template" which has only a "string Name" property. It is an aggregate root so it owns multiple TemplateProperty instances. TemplateProperty is an abstract class with two concrete descendants; TemplateStringProperty and TemplateBooleanProperty.

When I create a DTO I want a TemplateDto and added to its Properties I want TemplateStringPropertyDto and TemplateBooleanPropertyDto. When I translate TemplateDto I want to update the existing object.

To register a factory
container.RegisterType<IDtoFactory<Template, TemplateDto>, TemplateDtoFactory>(
    new ContainerControlledLifetimeManager()
    );


The structure is IDtoFactory<TDomainClass, TDataTransferObject> to identify the domain class and desired DTO class, followed by the type that implements the interface to perform this DTO creation. This code registers a factory that takes a Template domain class and creates a TemplateDto data transfer object.

To register a translater
container.RegisterType<IDtoTranslater<TemplateDto>, TemplateDtoTranslater>(
    new ContainerControlledLifetimeManager()
    );


The structure is more simple. A DTO can only be translated in one way, so the generic IDtoTranslater interface only requires a single type, TDataTransferObject. This code registers a translater that takes a data transfer object and maps it to the domain.

Once registered the services are used like so:
//Create a template
Template template1 = new Template();

//Create a DTO from the template
TemplateDto template1Dto = container.Resolve<IDtoFactory<Template, TemplateDto>>().CreateDto(template1);

//Update the template from the DTO
container.Resolve<IDtoTranslater<TemplateDto>>().UpdateBusinessObjects(null, null, template1Dto);


Note that the "container.Resolve" code wouldn't really be there, you would use dependency injection. I have used it in this example to avoid turning it into a dependency-injection-container blog :-)

The "null, null" here are
  • The object space that the app layer has created and is using for your domain instances to work inside. Also known as your unit of work, transaction, or other.
  • An addition context. For example when updating a Template the TemplateDtoTranslater will resolve services to translate each PropertyDto it encounters, and pass the Template as the context so that we know which aggregate root we are working with.
For now I will link to the full source code for the example. If I get time I will blog some more explaining how the code works.

Comments

Anonymous said…
Hi Pete

I was wondering about the actual "Entity To DTO" factory method you resolve here:

TemplateDto template1Dto = container.Resolve[IDtoFactory[Template, TemplateDto]]().CreateDto(template1);

That factory - how does it read properties from your entity (in this case the Template class)?

I am asking because Greg, in the debate on the domaindrivendesign mailing list, mentioned that public properties are evil.

I do not know if that is your opinion too?

BTW: I cannot download your zipped source code.

Thanks, Jørn
The download URL has been fixed. For some reason the website decided to add "blogger.com" to the start of the URL I entered.

The factory merely copies public properties. I saw that Greg seems to have been advocating not having public properties but was sure I was misunderstanding him.

I suppose it depends on how you work. I'm in the camp that says an object can be invalid but must be made valid before it may be saved, whereas Greg is possibly in the camp that says an object must never be invalid so any updates to the state must occur via a method accepting a chunk of data.
Anonymous said…
> I saw that Greg seems to have been advocating not having public properties but was sure I was misunderstanding him.

Well, I believe I am starting to understand him ... the point is that he is reading data from a completely different place than the domain objects. So he creates DTO's independently of the domain objects.

For performance reasons he queues up commands from the UI, executes them (updating through the domain objects). The result is then synchronized to a separate read-only datastore where the UI reads from.
Anonymous said…
guest00:

Pete, thank you for the direction. I have few questions.
1. Why did you suggest not to generate DTOs for all classes in the Model?
2. Is idea about custom ECO DTO service too bad?

Thank you,
Regards,
Alex
You should create a DTO for each task your client needs to perform. If you create 1 DTO for each domain object you end up making your client either

A: Bound too tightly to the domain structure rather than focusing on the tasks it allows the users to perform, or
B: Too much like a "data editor" and not enough like a task-driven application.

>Is idea about custom ECO DTO service too bad?

I don't know what you mean. But anyway, I would have the DTO services in a layer above the domain (and therefore not an ECO service) otherwise you fill your domain with knowledge it doesn't need.
Anonymous said…
guest00:

Hi Pete,
My initial idea was to discover the Model, create the DTO for each class and then create the custom ECO service with implementation of the custom interface.Class "Item", method inside the interface is "GetItemsForOrder(bla-bla-bla)". The implementation/translation is inside the custom ECO service.

Thank you,
Regards,
Alex
The problem with auto-creating DTO classes is that they end up as a 1-1 match with your domain. This means you are not free to refactor your domain without also having to update your client.

It also means that the user basically "edits" domain objects rather than performing specific tasks on them.

A rental department might hire out a vehicle so they will need to see availability + registration numbers, the repairs department will fix the vehicle so will need to see mileage + part warranty history + maintenance schedules.
Anonymous said…
guest00:

Hi Pete,
Before your post I was sure that the Model is the "center of the Universe" of my solution and all my projects I tried to build around the Model. And to put as much as possible inside the Model.

Now I am quite disappointing. It is time for me to reread Booch's books.
:)

Thank you,
Regards,

Alex
The model *is* the centre of the universe from an operational point of view. Without the right model *nothing* will be right.

What we are talking about here though is the layer that mediates between the user and the model.

Search for "Onion architecture"
2O said…
All in all, I found reading your article very enlightening and I encourage you to keep it up. You are doing a very good job.
Thanks.

Although I have since discovered AutoMapper (google for it) which is very good!

Popular posts from this blog

Connascence

Convert absolute path to relative path

Printing bitmaps using CPCL