I like the term Passive Data Object; PDO. If you're going to transfer something to somewhere else you might call it a DTO, but it is implicitly just a Serializable.
@Miggleness4 ай бұрын
I don’t know. I don’t want to ask the question “have you created the ‘p-do’ class yet?”
@Ardalis4 ай бұрын
I've never seen that term before. You're welcome to try to make it a standard term, but regardless you'll run into folks already using the terms DTO and/or POCO in the dev community today. As I noted in other comments, it's useful for everyone to understand these terms so when folks use them, there's no confusion.
@simonkalu3 ай бұрын
Nice... Thanks for sharing
@Ardalis3 ай бұрын
Welcome 😊
@ItsSalesGabriel4 ай бұрын
Nice video, tkss
@Ardalis4 ай бұрын
You're welcome!
@kwameaddo10074 ай бұрын
Thank you
@Ardalis4 ай бұрын
You're welcome
4 ай бұрын
”Microservices experiment” :) I have been there.
@Ardalis4 ай бұрын
:)
@mgkeeley4 ай бұрын
Where do you think validation should go? On the DTO? Or in the ingress function that accepts the DTO? For our internal Model classes, our team likes to do validation in the constructor; but I'm not so sure that is really necessary. Validation should perhaps only be for external interfaces?
@VoroninPavel4 ай бұрын
Both ;-) DTO requires structural correctness (required fields have values of the required length, etc), POCO (or Behavior Rich Object) guards business invariants. As you told, this is usually a responsibility of constructor or method whcih mutate state. And ther's also a validation which covers a set of aggreates, this is effectively a buiness requirement.
@ErazerPT4 ай бұрын
Think. What is validation? Behavior. What does DTO's NOT have? ... Or to put it simply, it it has anything but data, it might be many things, a DTO is NOT one of them. Which is why, IMHO, thinking about DTO's without instantly thinking "Interfaces at transmission boundaries" is deleterious, because it muddies the waters if there is no a) interface or b) transmission. Structure it like this, for a "perfect world": - there's a plain data class PDC that presents the DTO in it's simplest form, ie, plain data - there's an interface PDI that presents the DTO in it's simplest form, ie, plain data - PDC, by force of definition implements PDI and should have a constructor that takes in PDI to create a PDC - there's a business class BC that has a .New(PDI) method that returns BC ( if all is well ), or even a Tuple where enum represents either OK or some error - at the egress side of the transmission, you create a PDC, and ship it out as PDI Now at the ingress side of the transmission you have two options: - you receive PDI and turn it into PDC via PDC's PDI constructor, and pass it around as plain data, which has no guarantees of sanity, or - you receive PDI and turn it into BC via BC's .New(PDI) method. And HERE is where you perform validation. If all is well, the data was sane and you move forward, if not, you do whatever is appropriate While this might sound like a lot of work, it's a minute or two per DTO, and literally peanuts if you do it from the go. And you win on the fact that a) both sides MUST respect the interface and nothing but the interface and b) with strong "gatekeeping" of the interface, nobody will go around changing it willy nilly and breaking things. It might ruffle some feathers at the start, because some people are cowboys, but eventually they "fall in line".
@Ardalis4 ай бұрын
I prefer separate classes for non-trivial validation, using a library like FluentValidation. This keeps DTOs from having this. I will say that it's generally fine for DTOs to have *static* methods on them for things like validation or mapping, for instance. The *objects* are DTOs and have no behavior; the static methods are just functions that happen to be attached to those class definitions. You generally do not want to have a bunch of guard clauses or constructor-enforced rules in your DTOs. They should just be getters and setters. No encapsulation. No checks. They can have attributes to assist with validation. Why? Because you don't want serialization/deserialization to fail midway or with an exception. You'd almost always rather have a nice list of validation errors than a serialization exception with a stack trace.
@Ardalis4 ай бұрын
I don't usually use interfaces for DTOs - in fact that's a likely subject of an upcoming video. There are times when it's useful but most of the time the DTO *is* the contract and there's no need to add another type that mirrors it. I have seen some good uses cases for using interfaces with DTOs but usually they're not 1:1 but rather an interface that requires a certain subset of properties that many different DTOs might have which then allows for those DTOs to be treated a certain way collectively. But having one interface for ever one DTO class isn't something I've seen value in.
@ErazerPT4 ай бұрын
@@Ardalis I don't use it as a requirement but as a "nice to have". IMHO, the interface defines the DTO and the DTO implements the definition. By using an interface, which by definition is JUST a contract, both side are STRONGLY discouraged from relying on any internals of the DTO. Or even thinking about them ;) It does add a bit of work on the deserialization side, but i find that to be worth it in the long run.
@alphamaster24 ай бұрын
Now lets throw "Model" into the mix
@Ardalis4 ай бұрын
Yeah, that's an overloaded term, which is why usually it's prefixed. There's Domain Model on one end and ViewModel on the other. I also see (and have used) ApiModel (since there are no views) as well, and a host of other variants. Domain Models should not be DTOs but ideally should be POCOs as much as possible. ViewModels in MVC should be DTOs, typically. But in MVVM frameworks ViewModels are where the logic lives, so it's not a 100% rule at all - depends on context. ApiModels I've only seen in MVC and are just used for transferring data over the wire, so should be DTOs.
@kurokyuu4 ай бұрын
Does a record really fit into the dto defintion? There's bunch of compiler generated code like PrintMembers, Deconstructer, a different constructor where you can also pass the record and it'll set the values from the object. it has it's own equality operators and stuff like that. Wouldn't a record in this case be more like a Poco since it has behaviour. If you're really nitpicky under the hood properties are compiled down to getter/setter which then change a private backing field~ Or, to come back to the main question, does compiler generated code not count and as long as you don't put custom code in it the record would still be considered as a dto?
@Ardalis4 ай бұрын
You're not wrong, records *do* have a bunch of behavior, but it's mostly hidden away. They're very frequently used purely for data transfer purposes, though, which is why I tend to consider them DTOs by default, assuming no methods are added to them. Of course once you add your own custom/bespoke methods to your records, they cease to be DTOs.
@kurokyuu4 ай бұрын
@@Ardalis Okay, thanks for the answer. I'm using records too very often since they're very handy if your data-object is not too complex, and can be created very fast. One of the best features they've added into C#.
@josephmoreno97333 ай бұрын
@@kurokyuu Take care, most of the built-in behaviors in records are not used but can increase the complexity of some scenarios, especially when using AOT.
@jz51534 ай бұрын
To be honest this knowledge will be useful on interview and only there :).
@yetanotherdex4 ай бұрын
not quite. just had a discussion about this with a dev 2 days ago
@Ardalis4 ай бұрын
This would be a pretty dumb interview question to ask, IMO. It's useful mostly for clarity on teams. If one team members suggests to another to use a DTO for something, that *means* something specific. Likewise if a complicated class is depending on some 3rd party library, suggesting replacing it with a POCO means something very clear as well... as long as you know what a POCO is. If you think POCO and DTO are the same thing, you're going to be be confused (or make the wrong change).
@br3nto4 ай бұрын
@@ArdalisI don’t think DTO is a useful concept to talk about. It’s useful to talk about the design of the public APIs of your app; e.g where there are modular boundaries, network boundaries, public API boundaries, it’s useful to talk about what that interface looks like. But the language used should always use the terminology of the problem domain - e.g a web request returns a response; it doesn’t return a DTO. Based on the primacy of your chosen domain model nomenclature, DTO or Dto should never be added to a class name or namespace. Otherwise you’d get superfluous names like “Responses.UsersDto” or “UsersResponseDto” or “Dtos.Users”.
@kaustavchakraborty32444 ай бұрын
So the classes the Mediatr library uses as commands are not either DTO or POCO, as it depends on an interface.
@Ardalis4 ай бұрын
They don't have behavior, so I consider them to be DTOs. You're correct, they're not POCOs, because they require a (marker) interface to function. My next video will probably be on the topic of interfaces and DTOs (and then I hope to move away from the whole DTO topic, but I might do ONE more after that).
@kaustavchakraborty32444 ай бұрын
@@Ardalis looking forward to that.
@VitaliiShmidt-UA4 ай бұрын
Which kind of behavoir in Domain models do you talk about? Didn't get that one. All the examples of DDD's structure that I've seen uses anonymic domain-models.🤔
@Ardalis4 ай бұрын
Like these, for very simple examples: github.com/ardalis/CleanArchitecture/blob/main/sample/src/NimblePros.SampleToDo.Core/ProjectAggregate/Project.cs github.com/ardalis/CleanArchitecture/blob/main/sample/src/NimblePros.SampleToDo.Core/ProjectAggregate/ToDoItem.cs Obviously in real world systems there would be even more special cases, invariants, events, etc.
@VitaliiShmidt-UA4 ай бұрын
@@Ardalis very interesting, thank you for sharing 🫡
@nkesteren4 ай бұрын
All these years I felt free to share a video with friends. Oh noes. 😂 👍😉
@Ardalis4 ай бұрын
What? Without explicit permission from the KZbinr? How could you! 😉
@redspidermkv45254 ай бұрын
I'm a little confused.. your DTOs have public setters but the POCO you demonstrated with an Update method isn't a DTO. So a DTO can only have a default setter, if you have logic in your setter (or getter) its not a DTO?
@Ardalis4 ай бұрын
Correct. DTOs are just properties. I mentioned in another comment you could have static methods for mapping or validation or whatever and that doesn't break this rule because they're not on the instance.
@peepeefrog_4 ай бұрын
Why would I care about the theoretical definitions?
@JansthcirlU4 ай бұрын
Rumpelstiltskin principle
@Ardalis4 ай бұрын
For better team communication mainly. If one team members suggests to another to use a DTO for something, that *means* something specific. Likewise if a complicated class is depending on some 3rd party library, suggesting replacing it with a POCO means something very clear as well... as long as you know what a POCO is. If you think POCO and DTO are the same thing, you're going to be be confused (or make the wrong change).
@br3nto4 ай бұрын
The difference between a POCO and a DTO: a DTO has Dto in the class name or namespace. TL;DR - don’t do that. They are both just classes, records, or structs. All classes, records, and structs are used to transfer data, otherwise there wouldn’t be a point to them. I’ve never encounter a situation where it’s useful to make a distinction that a POCO is being used as a DTO, because that’s just naturally what they are for. It’s just not useful to talk about Dto’s. Maybe you have some classes that specifically represent the API between an app boundary like between modules, or returned from web requests, or from library calls; it’s better to name these classes based on your domain model - a class, record, strict, or namespace should never have Dto or DTO in the name.
@Ardalis3 ай бұрын
Yeah like I said elsewhere, use the “Dto” suffix as a last resort. And no, not all classes are used to transfer data. Consider any service, and in particular stateless services.
@occularmalice4 ай бұрын
We still have to explain the difference between POCOs and DTOs to people in this day and age? Faith in humanity lost.
@Ardalis4 ай бұрын
Every day someone starts their programming journey. Actually, a lot of someones...