DTOs & Mapping : The Good, The Bad, And The Excessive

preview_player
Показать описание
DTOs (Data Transfer Objects) have a lot of utility but like many things are often overused, used incorrectly, or used when you don't have the problem they solve. Here's why they are useful, the problem they solve and when to use them.

🔗 EventStoreDB

💥 Join this channel to get access to a private Discord Server and any source code in my videos.

🔥 Join via Patreon

✔️ Join via YouTube

0:00 Data Transfer Object
2:42 Entity Services
5:30 Benefits
8:17 Inside & Outside
Рекомендации по теме
Комментарии
Автор

I find DTOs really important for 2 reasons: 1) it's a contract. You can change your DB all you want - but you often have to retain a contract. V1 of the API is FName, V2 is FirstName. Both can map to the same entity and to the same DB but you can support different contracts. 2) Especially in DDD, Domain models don't match DTO / JSON needs. In code, value objects often get exposed as simple strings, ints, and other primitives. My EntityId value object ends up in a DTO as a Guid. If I directly To Json EntityId, I'd end up with Id: { Value: "12345" } instead of simply Id: "12345"

People who say it's not needed in DDD often have anemic models that are pure DB entity models.

Just my two cents. Everyone's experience with it is different

EliGassert
Автор

Have you ever tried doing some code review? I always find your videos so insightful. I believe it would be great to see your thought process on some real world examples.

boooooooky
Автор

I really enjoy your no-nonsense and undogmatic approach to programming.

patriceroy
Автор

Nice video! Fully agreed.
My takeaways and thoughts:
- use DTOs to reduce coupling
- if there's no coupling you can reduce (e.g. 1-1 db to model) then get rid of unnecessary DTOs
- think about team's/module's (DDD bounded context) responsibilities: a DTO within your boundaries might be useless (pure overhead). The coupling that does make you suffer is cross-module/cross-team/etc. coupling.
- think about the higher-level dependencies that you might want to loosen - and not your internal code dependencies.

DucinDev
Автор

Another very important reason is the performance. I've seen many projects and companies who read the whole entity (a Product for example) from the database, and when you go forward you see they only wanted the Description from that entity. So, yes, DTO's are very important.
To make it clear for everybody here, there are 2 types of APIs:
1) Microservice API (API-to-API call)
2) BFF API (BackEnd for FrontEnd)
You may not need a DTO for the first type, but for the second type it's 100% important.

SinaSoltani-tfzo
Автор

Worst mapping experience I ever came across was a REST API mapping requests and payloads to service layer DTO's, which then were passed along to the domain, which was then mapped to ORM objects, which were used to store into a RDBMS. And of course upon successful persisting, that "saved ORM" was then mapped back into the domain, which was then mapped back to a DTO broadcasted from the service layer, which was then mapped into a serializable form (JSON) to return to the frontend client.
We had driving adapter (the rest api) and one driven adapter (the jpa layer). But you know, "what if" the business wanted to also accept queue or drive an integration event stream?
At least all that indirection was nice to test the domain in isolation. And it was a great project for learning. "But at what cost?"

TimSchraepen
Автор

Yes, 130 database models modified directly from the frontend. It is the horror project I joined last time. It was not a problem until new requirements appeared to integrate automatic synchronisation from the external 3rd party services. All protections were implemented as validators for the HTTP layer, so not existing domain layer couldn't protect consistency of data coming from external services launched by queues or CRON, because it does't use HTTP validators. It is real nightmare now. Adding domain layer is extremely time-consuming and there is required some strategy to apply it correctly, small piece after another. Work for a few next years. More over implicit syntax sugars minimised HTTP controllers to zero lines of the code giving automatic access to the database-model directly from the HTTP request. So there is no domain and controller layer you can explicitly customise.

abogdzie
Автор

I've only really found DTOs useful once, and it was when the particular nosql storage I was using was annoying to work with so we wanted to keep all of that logic hidden from the rest of the application.

For example writing one record actually required writing it in many different ways by different keys, because there were no secondary indexes, but the logic in the app just wants to write it once, not have to worry about partition keys and row keys or denormalization or table scans vs point reads or etags etc.

It also allowed us to update the client library we were using without causing breaking changes in the rest of the application, and eventually we got sick of it and switched databases entirely, and this made that a bit easier to do.

georgehelyar
Автор

DTO's map to some UI component somewhere. You are fetching some data from your api and then displaying that data in some UI component: That could be a html element, a BI element, a record in some reporting system, its going somewhere. And the shape of that data is usually a subset of the aggregate and/or data model. It might map bijectively to some SQL View, but not the data model itself. I find myself jumping from one end to the other and its usually in redards to a particular process. Starting on the FE, (injestion), some transformation on the BE, and finally loading it back on the FE. Every time its a mini ETL pipeline. And at the center is some process. Anytime you are crossing a boundary, I always thought there should be a DTO to decouple the consumer from the API. This way the API can vary and be extended without effecting the consumers of the API. A DTO acts as an Inteface, so you don't have to expose low-level details (The Business Logic) to higher level calling code (The Consumer). If one thing is for sure, Requirements will change, because business is always changing, so I do not think its over-engineering to have that indirection in place. It is not a matter of "if" but a matter of "when" are requirements going to change. I think this really boils down to money. If I don't need it now, then I do not want to pay for it now. Which is understandable. This was a good video. It made me think, which is what this post really has been, me thinking out loud.

tanglesites
Автор

I think the problems stem from developers being bad at abstraction and separating the contexts. A words might mean different things depending on the context and use. That is why these videos are so useful, because they, you teach, teach developers that it is not just about the code itself, but what the code represents, what it means, and how to express that. Not just for yourself but for others through code.

A program is more than its sum of methods/functions.

marna_li
Автор

Hey Derek. Quick question. I have case for orchestration when one request to create user is splitted into two requests - to user service and auth service. User service accepts all user data when auth service accepts user id and credentails. How would you implement it especially for error handling.

Masteroxify
Автор

This is a typical example of why CQRS is a good idea! For the read side, I sometimes even just run database queries straight inside the API endpoint. No fuss needed. Just grab the data you need and map it to a format that you want.

Coburah
Автор

I haven’t had much problems with a 1:1 mapping for DTOs

It’s a very thin mapping function anyway. Updating the mapping doesn’t take much work. Accidentally leaking data sounds more scary.

matowang
Автор

This is an interesting topic. I mainly use DTOs just for my queries, path parameters, body, etc that I get on the request that way I can validate the data that is coming in using something like class-validator. At the other end I use entities for my response now for the entity I just use transform it and exclude/include any properties I want in the response also transforming names etc. Straight forward with the class-transformer package.

josealejandrosalazarmarcan
Автор

I just return the model, that runs through a filter and removes sensitive data :D I know its bad, but man, I am not willing to create hundreds of dtos.

DavidSmith-efeh
Автор

I get the part of using DTO to composing data and managing coupling.
But in your sample code the Shipment logical boundary seems to have data for the Client, which means I can expose the data together with the Shipment. Otherwise, if I just have the reference ID inside Shipment boundary I can't do this composition, am I right?
Therefore we end up with almost the same model as the beggining (the CRUD one you showed in the video). But additionally we could put the actions and so on (HATEOAS) and we can only do the composition in the API gateway or even in the Client side

heikenem
Автор

I Like Immutable DTOs to act like diodes in software, making it much harder to create cycles in systems. Like oh I have 10000 records, and oh now I'll iterate over 10, 000 of them to change one field, and then oh I'll just do it again to change another field. If the data is immutable and sufficiently hard to change, then as well as an outward (not necesarrily public) contract, they also enforce directionality, and encapsulate behaviour. Past this point you can't just save this record without coming back in from a less privileged state.

Great video!

LewisCowles
Автор

High quality advice and best practices 👍

Just image an API which returns a customer along with its addresses but having at least two tables in a relational database. I have seen people doing a "soft join" to avoid mapping and API design but this is just the same bad practice.

I have seen so much code tightly coupled with the internal data model where it is impossible to change anything, neither in the code nor in the database schema. It should be obvious but seems to be so hard to get, even when there is a maintenance problem which everyone clearly sees.

I personally dislike ORMs altogether and the way some frameworks abuse them to lock you in by implementing "best practices" and magic with "models", leaving no room for a better aproach.

The REST example is a brilliant example of the same thing in the other direction. While the mapping of REST to CRUD is just beautiful to simplify and standardize API implementation, it still is not a good idea to pass the data just through to avoid additional work. No it is not smart.

Only when you give the idea of encapsulation and interfaces a try and get some practice, then you will see how much less work you will have and how much more flexible you are, at least with legacy applications that have a certain lifetime under constant modification.

You cannot bypass anything, someone will pay the price. Better be smart with decoupling. When in doubt, watch this over and over again. It could not be more to the point 👍

ulrichborchers
Автор

Most de-coupling architectures are built on the principles of just-in-case and scalability

zimcoder
Автор

6:13 "when you dont have that problem yet", right.. but DTOs are part of a layered architecture that you use because you're trying to think ahead, the bigger picture. They're also about creating a visible layer and pattern that your colleagues easily can follow. You never need to debate if "something should be a DTO or not" because the layered architecture dictates that everything should be a DTO.

TheMikkelet