POCO or DTO?

Поделиться
HTML-код
  • Опубликовано: 8 сен 2024
  • POCO or DTO
    Hey everyone, I'm Steve Smith aka ardalis, of NimblePros!
    In this video we're going to define and compare POCOs and DTOs. Not everything is 100% cut and dry, even for me, but mostly the rules are pretty straightforward.
    Let me know what you think or share your own tips in the comments below!
    Links
    ardalis.com/dt...
    dometrain.com/...
    Check out my courses:
    dometrain.com/...
    dometrain.com/...
    dometrain.com/...
    ardalis.com/tr...
    www.pluralsigh...
    Team Consulting and Training: NimblePros.com and @nimblepros
    Developer Group Mentoring: devBetter.com
    Free Weekly Email Tips:
    ardalis.com/tips/
    If you read this far, I hope you liked this video and will consider subscribing to my channel!
    Find me:
    ardalis.com
    / ardalis
    github.com/ard...
    / stevenandrewsmith
    bsky.app/profi...
    #dotnet #visualstudio #csharp #clr #dto #poco #microservices

Комментарии • 48

  •  Месяц назад +3

    ”Microservices experiment” :) I have been there.

  • @jz5153
    @jz5153 Месяц назад +12

    To be honest this knowledge will be useful on interview and only there :).

    • @yetanotherdex
      @yetanotherdex Месяц назад +3

      not quite. just had a discussion about this with a dev 2 days ago

    • @Ardalis
      @Ardalis  Месяц назад +1

      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).

    • @br3nto
      @br3nto Месяц назад +1

      @@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”.

  • @alphamaster2
    @alphamaster2 Месяц назад +2

    Now lets throw "Model" into the mix

    • @Ardalis
      @Ardalis  Месяц назад +1

      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.

  • @ItsSalesGabriel
    @ItsSalesGabriel Месяц назад +3

    Nice video, tkss

    • @Ardalis
      @Ardalis  Месяц назад

      You're welcome!

  • @kwameaddo1007
    @kwameaddo1007 Месяц назад +3

    Thank you

    • @Ardalis
      @Ardalis  Месяц назад

      You're welcome

  • @simonkalu
    @simonkalu 14 дней назад

    Nice... Thanks for sharing

    • @Ardalis
      @Ardalis  12 дней назад

      Welcome 😊

  • @tunawithmayo
    @tunawithmayo Месяц назад

    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.

    • @Miggleness
      @Miggleness Месяц назад

      I don’t know. I don’t want to ask the question “have you created the ‘p-do’ class yet?”

    • @Ardalis
      @Ardalis  Месяц назад

      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.

  • @mgkeeley
    @mgkeeley Месяц назад +1

    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?

    • @VoroninPavel
      @VoroninPavel Месяц назад +3

      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.

    • @ErazerPT
      @ErazerPT Месяц назад +2

      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".

    • @Ardalis
      @Ardalis  Месяц назад

      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.

    • @Ardalis
      @Ardalis  Месяц назад +1

      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.

    • @ErazerPT
      @ErazerPT Месяц назад

      @@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.

  • @kurokyuu
    @kurokyuu Месяц назад +1

    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?

    • @Ardalis
      @Ardalis  Месяц назад +1

      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.

    • @kurokyuu
      @kurokyuu Месяц назад

      @@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#.

    • @josephmoreno9733
      @josephmoreno9733 Месяц назад

      @@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.

  • @kaustavchakraborty3244
    @kaustavchakraborty3244 Месяц назад

    So the classes the Mediatr library uses as commands are not either DTO or POCO, as it depends on an interface.

    • @Ardalis
      @Ardalis  Месяц назад +1

      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).

    • @kaustavchakraborty3244
      @kaustavchakraborty3244 Месяц назад

      @@Ardalis looking forward to that.

  • @vitaliishmidt3704
    @vitaliishmidt3704 Месяц назад

    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.🤔

    • @Ardalis
      @Ardalis  Месяц назад +1

      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.

    • @vitaliishmidt3704
      @vitaliishmidt3704 Месяц назад

      @@Ardalis very interesting, thank you for sharing 🫡

  • @nkesteren
    @nkesteren Месяц назад

    All these years I felt free to share a video with friends. Oh noes. 😂
    👍😉

    • @Ardalis
      @Ardalis  Месяц назад

      What? Without explicit permission from the RUclipsr? How could you! 😉

  • @redspidermkv4525
    @redspidermkv4525 Месяц назад

    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?

    • @Ardalis
      @Ardalis  Месяц назад

      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_
    @peepeefrog_ Месяц назад +3

    Why would I care about the theoretical definitions?

    • @JansthcirlU
      @JansthcirlU Месяц назад

      Rumpelstiltskin principle

    • @Ardalis
      @Ardalis  Месяц назад

      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).

  • @br3nto
    @br3nto Месяц назад

    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.

    • @Ardalis
      @Ardalis  Месяц назад

      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.

  • @occularmalice
    @occularmalice Месяц назад

    We still have to explain the difference between POCOs and DTOs to people in this day and age? Faith in humanity lost.

    • @Ardalis
      @Ardalis  Месяц назад

      Every day someone starts their programming journey. Actually, a lot of someones...