5 Rules For DTOs

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hi everybody it's me r Dallas in this video I'm going to share with you my five rules for writing better dto in your software so let's start with what is a dto a dto is a data transfer object that's what the acronym stands for and that is essentially what defines such an object dto are meant to be used to transfer data which often means they are easily serialized to and from other formats like Json or XML this also means that when the destination of the transfer receives the data it should be free to do so using whatever type it wants even a dynamic type so long as it can hold the values and that means dto should not have any Behavior because you cannot expect the receiver of your data to deserialize the data into a type that has the same behavior as the thing that you serialized when you sent it only the data values are going to transfer so the first rule of dto is dto should not have any logic or behavior if they do then they're not a dto now before we get into the next rules I want to let you know about a course I published recently on modular monoliths I really think this is the approach you should be using for most non-trivial net applications I've published it with Nick chap' Dome train site and he does an amazing job of recommending it so let me turn it over to him for just a moment now before I move on I'd like to let you know that we just launched the the brand new C on dome train called Deep dive into modular monoliths and it's a direct sequel from the getting started delivered a few weeks ago by aalis of Steve Smith Steve did an amazing job with that first course and you loved it so we had to get out the Deep dive one as fast as possible just to see how the whole application is completing and being ready to go to production with more features and more modules to have a complete modular molth which in case you don't know I think it's the goldilock zone between microservices and old bad monoliths it's where most people and by most I mean almost everyone should start before they feel like they have to go anywhere else maybe microservices or maybe even further both the Deep dive and the getting started should be taken by every net developer working in mod and.net there's so many best practices you're going to learn there and to celebrate the launch of the Deep dive you can use code modular 20 at checkout or use the link in the description to claim 20% off that course and you can also add the getting started course in your basket for a month mive discount if you don't have that already and on top of that we also have a from Zero to Hero modular monolith bundle now which allows you to combine both courses with a 20% discount okay now back to the video okay now let's jump into some code all right in this simple application I'm going to show you a few examples of dto using different types in C let's start with a simple person dto we can create a dto by just using a record type and have a very suin way of defining them and then we can use that dto inside of our code like this and of course if we run this you can see that one of the nice features of record types is that we automatically see all the properties and their values without having to override two string or anything like that all right now one trick with this is that we can't do person. last name equals Smith that's going to fail because we don't have a public setter on last name all of the fields that we specify on the Constructor of a record type are in it only okay but what about serialization that's the main thing we want to use these types for well we can take our person and we can serialize it into some Json and then round trip that by deserializing it back and see what we get so I'm going to go ahead and use system. text. Json Json serializer here and we're going to serialize the person into a string we're going to write out that string and then we're going to deserialize it into a new variable and print that out and you can see here that we get the same values all the way around we started out with person dto John Doe and that's what we have when we're done so this is working as you would expect now there may be some reasons why you don't want to use a record type and you'd prefer to use a class so let's see what that would look like in this case we're going to use a customer dto just so we have a different name for the type and you can see that we can set it up with init only properties also so when we work with this it's going to work pretty much the same way we can create the customer we can print out the customer however in this case you can see that all we get is customer dto when we print it because classes don't have an automatic implementation of two string that shows all of their properties once again if we try and set an iton property we're going to get the same error saying that we can't do so but with the class we can easily change this to allow setting and at that point this should start to work if we want to be able to print this out easily we have to override two string now what this init only behavior is doing is ensuring that these instances are immutable right so when we create one of these whether we deserialized it from something or we created it ourselves we're not going to be able to change it now immutability can be a valuable feature of these objects to have especially if we are deserializing something we can work with that type within a context within a method body for instance and know for the life of that method that we are working with the thing that was passed in the message that we received the thing that we deserialized there's no way for you to change it because it's immutable however when you're going to create a message that you want to send sometimes you don't know all the data upfront and it can be burdensome to have to create a bunch of local variables to to pull everything in and then finally create the dto so sometimes when you're creating these dto yourself it's much more convenient to have public Setters on your class that make it easier for you to just set the items as you get them rather than having to create it all at once so immutability can be a nice feature it's not a rule for dto your dto could be immutable or they might not be do what makes sense in your context all right so what about encapsulation in object oriented programming one of the key values of how we build objects is that we encapsulate by hiding information from outside collaborators so when you create for instance an entity we are able to make it so that the logic of how certain operations are performed within that entity are hidden from the user we're also able to encapsulate the state so that the only way you can mutate the state of an entity is by going through certain uh well-known public interfaces or methods and not by allowing anyone to just manipulate the state anytime they want so these are all good things for objects like entities but for data transfer objects there shouldn't really be any encapsulation what that means is that your dto typically aren't going to have any private or protected members everything in the dto should be public and in many cases it should be mutable as we already discussed with the immutability concern and so that's rule number two for your dto your dto should not enforce en capsulation they're not going to need private or protected members inside of them now once you tell developers that dto don't need any encapsulation a very common next question is going to be well in that case do we need to use properties can we just use fields for everything since we're not trying to encapsulate the members or the state of this object and the reason why we prefer to use properties mostly has to do with the fact that properties have first class support in C for a VAR of things and Fields often don't have any support or that support is much later in coming so for example let's create another dto we'll call it order dto and we'll just use public fields for the different values that it has the ID the order number and the total we can create an order like this one and we can print it out just like we've seen already because it's a class it's just going to print out the type name now let's try in serious ize it just like we did previously and here you can see that the serialized order that only had Fields ends up giving us just an empty set of curly braces for the Json so serialization doesn't work on fields it does work on properties that brings us to my third rule for dto which is that dto should use properties and never Fields if you are using Fields you're likely to run into problems when you try and serialize or der serialize these types so that brings us to the next question that's very frequent with these types is what should we name them now you've seen me use the suffix dto so far and that's a pretty common thing to use for individual records that we're talking about uh whether it's you know a a person or a customer or an order often those are going to just be called dto to indicate that that's what that purpose is you might choose to use dto or uh if you prefer you might use dto with just a capital D either one of those is fine however what you want to avoid is using that suffix too much for things that happen to be dto but have a more specific purpose so what I'm talking about here are things like uh view models or request types or response types which are dto but you don't want to just call it a dto and if you know that it's a view model and you know that view models are dto then it's redundant to say it's a view model dto so you can use view model or request or response as the suffix and don't use view model dto request dto Etc so that brings me to my fourth rule on dto which is that they should only use dto as the suffix of their name as a last resort otherwise you should name them for how they are used if you're building an API I strongly encourage you to look at the reaper design pattern which I covered in a recent video now if you're using this approach when you go to create a record for instance a person you may have a person dto but when you go to create that thing it's usually better if you have a custom dto just for that creation so for example you might have an endpoint that takes in a create person request and yeah it happens to have a first name and a last name just like person d o does but it's much more clear that this is the type that we're going to expect for this endpoints request and it may be that we'll have additional things that will need to be added or things that we won't need when we create a person but later on will be added so for example maybe a person dto also at some point will have a created date as a property well the create person request is not going to include that because we don't want the end client code to send us that create date we want that be something we generate when we successfully create the person and so there's often going to be some disparity between the message formats that you use for requests and responses for commands and queries and events even though they might look very similar to your dto that represents a concept in the system we want to have discrete separate types for those which are themselves dto but which have a specific purpose and a specific suffix name they use for that purpose so that brings me to rule number five these types of things should all be modeled as dto API request and response objects MVC view model objects database query result objects and messages like commands events and queries and because they're going to be modeled as dto and since everybody knows that they're dto you don't need to put dto in their name those should just be suffixed with request or view model or you know query result or something like that now since we're talking about view models let me point out that I'm specifically referring to MVC style view models if you're building something using WPF and the mvvm approach those view models often do have behavior and should not be dto so if we look at a ven diagram describing dto MVC view models and mvvm view models it's going to look something like this where all MVC view models should typically be dto and mvvm view models typically should not let's look at some examples of dto following rule number five here you can see you can imagine that there might be an API that takes in a create user request inside of that API endpoint it might create a create user command that create user command might be dispatched to a Handler within that Handler it may need to check whether or not that user already exists and so it may issue a query using a user exist query dto and then once it knows that that user doesn't exists it can successfully create that user triggering a user created event dto now once that's done it can return back a create user response to the consumer of the API and then if necessary it might also have a user details view model that would be used to display inside of a view or a razor page the details of a user all right that's it for the five rules now I want to cover one last thing which is what about validation and attributes you might add attributes to say that particular properties are required or have to have a certain format and then you can leverage some of the built-in validation features of asp.net or other libraries in order to use that validation without having to write that validation yourself for example we can create a record create user request with attributes that says it has an email and a password the email is decorated with an attribute that says it's an email address and the password has a minimum length of eight these are attributes that are coming from the system. component model. dataannotations namespace and of course we can do the same thing with a class so here's the exact same type but this time with a class instead of a record now how do you actually perform this validation well you can use the built-in functionality of MV C or similar types or you can do it yourself in code so for this example I'm creating a create user request with attributes it has an email address of john. do.com which is not a valid email and it has a password of 123 which is not a minimum length of eight so to perform the validation we create a list that we're going to get the validation results in we call this is valid uh by calling try validate object on validator validator is a system. component model data annotations type and when it's done we're going to get back this is valid as a Boolean as well as the validation results so we can validate this request and say is it valid we're expecting it to not be valid and you can see right here the request is valid so what's going on here the issue is described in this GitHub issue where validator that Tri validate object does not handle record types I don't know why this issue is been around for a couple of years now but as of net 8 as of April of 2024 when I'm recording this this still is not handled what if we do the same thing with our class version we've got the create user request with attributes Class Type same inputs all the same logic we're going to see is the class request valid and here you can see that it is not and we see the email field is not a valid email address the password field must be a string or array type with a minimum minimum length of eight so it works as we expect personally I favor using fluent validation which is a very popular uh Nate package that you could use for your validation if we use fluent validation this becomes something that works for both records and classes so let's see what that would look like with fluent validation you have to create your own class for each validator and so here I've created a validator for create request with attributes that's the record and it inherits from abstract validator of T where is that record type and in here I'm going to set up a couple of rules one rule for email one rule for password then to run this we're just going to create an instance of that validator and call its validate method assuming that it's not valid we can cycle through the validation result errors and see what those errors are down here at the bottom we see the fluent validation section and of course you see email is not valid and password must be at least eight characters long that's on the record type the Class Type works exactly the same way and you would write the same validator for that I hope you found this helpful feel free to share these rules with your co-workers share this video with anyone you think would benefit from it if you want the source code there's a link in the description where you can request it and then you can play with the sample and and see how everything works yourself as well if you have any questions please leave them in the comments if you have any requests for new videos let me know about those in the comments as well until then keep improving
Info
Channel: Ardalis
Views: 37,341
Rating: undefined out of 5
Keywords: csharp, dotnet, .net, asp.net, asp.net core, visual studio, vs code, oop, dto, data transfer object, web api, web apis, aspnetcore
Id: W4n9x_qGpT4
Channel Id: undefined
Length: 17min 56sec (1076 seconds)
Published: Fri Apr 05 2024
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.