Treating Primitive Obsession with ValueObjects | DDD in .NET

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hello everybody i'm nick and this i'm going to show you how you can cure primitive obsession in.net by using value objects now this whole concept is very much a ddd specific topic but in my opinion whenever you're dealing with any sort of domain it does actually make sense to tackle it in this specific way because it will actually in my opinion solve a lot of problems now what is primitive obsession well primitive obsession is the idea of using primitives to represent domain ideas an example would be that you would use a string to represent the postcode or in our case you would use a double to represent temperature like celsius by using those values and those types you're assuming all the values that this specific type can take and are valid values for the thing you're representing for example i live in the uk the uk postcodes have a very specific format for validity so not every string is a valid postcode in that scenario i would fix that by using a value object called uk postcode and then i would allow this object to only take values that adhere to the rules of the uk postcode system and why would i do that well imagine that you have an application that customers sign up in the provider uk postcode in your registration form you would have to validate that this postcode is valid upon creation but you might also have to validate it somewhere else in your application or maybe when you want to send them like a post in the mail and and that sort of thing so you end up duplicating a lot of validation logic for what truly is uh a postcode the uk postcode so what we're trying to fix here is to centralize that logic in the domain object itself the value object itself and that's not the only thing we also want to bake immutability because value objects are by nature immutable so the value cannot change and there's a lot of things around it that really clear a lot of ambiguity in your code now i totally understand that might not be something that everybody can use or everybody should use for that matter uh but i think you can actually benefit if you're working in such an environment especially from what we talked about in the clean architecture video which actually had value objects now enough with that let's just dive straight into the code and actually see what the value object is what period of obsession is and how we can fix it very easily so i'm going to go to this api and i'm gonna just run it before i show you the code to see what we have here so first and foremost i have a single forecast endpoint and when i click send i do get um all those weathers and that's the example that you see whenever you create a web api in dot net so very straightforward nothing special about it now let's just dive into the code a little bit and see how this is structured right so we have uh the controller which is calling the eye weather forecast service and then it's getting the forecast and then it's mapping it to a response so we have the main layer and the contract layer and we're doing a mapping on that for the purposes of this video i have it in this single project but in reality the domain folder would be a domain project and the responses which is the contracts might actually be better suited to be in a dot contract project as well and this is how i'm representing my weather forecast i have a date i have a temperature in celsius and then i have a summary and those are init only properties because they're immutable for that specific thing they're representing the date sorry the weather in that specific date time the problem with this specific use case is here i'm using a double to represent the celsius now a double can have a wide variety of numbers from minus a lot to plus a lot and it's exceeding what a temperature in celsius can be temperature is a concept that actually has a maximum and a minimum the maximum is not quite clear yet but it's a very big number um the minimum is zero kelvin or absolute zero and with a conversion in celsius this is minus 250 something degrees celsius so the value cannot physically be less than that number by using a double we're assuming that it can ensure you can have validation like i said before in different stages of your application but then you're giving that responsibility to something else to do the validation of your domain object for you um which can be problematic what if we could represent this value in a more domain driven way and we actually can we can use something called the value object now a value object can be represented with multiple ways microsoft gives you a class i think in the documentation to do that i've been experimenting with records to actually create uh value objects without because one of the things with value object is that um equality when you're comparing them is based on their properties not on their reference so you have to override things like equality operators get hash code all those things like tostring as well sometimes so there's a lot of things you have to do to build the value object for this video which i treat as a very much introductory video on the topic i will actually recommend a library called valueof valueoff is made by the same creator who created one of which we've actually made a video in this channel and you can click on the top right corner of the screen right now to watch that and value off actually gives you a very straightforward way of creating a value object so what i'm going to do is i'm going to go to manage nuget packages i'm going to say value off and i'm going to find this package and i'm going to install it and that's it and what i want to do is i want to go into the main folder and i want to create a new directory called value objects and in there i'm going to put my value objects that i will use in first place of the application because that can be a postcode it can be a temperature like i said it can be uh the number of items in your basket which cannot be negative uh a whole list of things so i'm gonna go ahead and create a new class and i'm gonna call it um celsius and the way i will build that value object goes as follows i'm gonna extend the value of a class and then the first thing in that generic type parameter there's two generic type parameters the first thing is the underlying primitive that will be used to represent this value object in our scenario is the double and then the second thing is actually a reference of the type itself so it's celsius and if i go ahead and fix the import on this value of it's this bit and i have my value object the value object or the value of class actually comes with a lot of things that we need already done for us for example validation backed into it it has the overridden equality checks and operators so if you have two different value of objects with the same value behind the scenes and you do any quality check it will actually return true because it checks the value it doesn't check the reference but that's diving a bit too deep and that's all i need to represent my temperature and i'm going to copy that and i'm going to go back to my domain object and i'm going to change from a double to celsius and now i just showed my primitive obsession for this specific type if i go to the services you will see that now the code doesn't compile so how can i convert this double to a celsius value object so i'm going to copy that and i'm going to say celsius dot from and the double in there and now this celsius value object and immutable value object is representing temperature correctly well almost correctly because like i said we can also validate in a value object the values that this thing can have so how can we do this in value of well you can just say override the validate method and you can put any validation in there so i'm going to say private const and i'm going to say absolute 0 in cell cs and you get to actually put that detail in this class in this value object because it's actually the owner of that property it should know what the absolute zero in celsius is and i'm gonna go real quick and google it because i have no idea what it is by heart oh and that should be a double like we said and then that is minus 273.15 degrees celsius and then what i want to do in that validate method is i want to say if value and this value is how value off is actually storing the primitive behind the scenes is less than absolute zero and celsius throw new argument exception in fact you don't want to throw an argument exception it might be a better idea to create your own exception called temperature below absolute zero exception and that should be a class and that should extend exception and then in there you override the constructor base and then we just provide the value that the user provided for that thing so degrees and then you say something like message is temp temperature cannot be below absolute zero current value and then you just provide the value for debugging purposes and that's it and then i can actually return this and i get i can give you the value and and that's it so now my value object knows how to validate itself so i don't need to split that logic in a thousand different places i mean i am exaggerating here but it can actually get confusing i've seen places where you are in different locations um and for different things and if there is a misalignment with that validation it can actually cause inconsistent behavior for your application so now that we have that what i want to do is well first i want to debug the application again to show you that nothing changed on a functional level the application is still acting the exact same way so if i go here and i click it again as you can see we're getting the exact same thing over and over again by the way many of you might be wondering want to be a performance head by using an object instead of a primitive and some of the things records is actually addressing there but on a very theoretical level yes and actually there is a practical uh decrease in performance because obviously you're newing up a new object but the benefit from what you're doing um highly outweighs any performance concern you might have with this it's so negligible that like if you're worrying for newing up objects in your application like i'm sure you have bigger problems than this basically so as we can see everything works fine what i want to show you is what would happen if that value was below absolute zero and how that will play out so i'm going to go back to this service which randomizes the values and i'm going to say -500 here and then maximum 55. so we're going to give it a chance to fail and i'm going to debug this again and show you what happens so back here we're going to click send now and as you can see there was a bad request because the domain object threw an exception and i'm getting a 400 response back saying temperature cannot be below absolute zero current value and that's the value that the double randomized and gave to the celsius object and you'll see it showing over and over again in this scenario we didn't have any um going below that but if i keep clicking it eventually will go below that because it just chances so as you can see just in a single place and that can be the same for returning something or creating something or in so many levels um and it's very clear what it is right you don't need to search in validators or whatever you just look at the object itself i think that is a great way to get introduced to the concept there's multiple ways you can do it like i said uh the value object that microsoft provides um records a bunch of other stuff i want to very quickly show you what i was talking about before with the equality checks because that's a bit important uh what i have here is a simple console application i will stop running this project i'm going to go to the console app and i'm going to delete this and what i'm going to do is i'm going to go to new packages and add value of in that console application and in that very same project i'm going to create the uh in fact i could just refer to the project but let's just copy that celsius thing i'm going to paste that celsius value object here so what i want to do is create two temperatures like temp 1 i want to say celsius.from and i want to give it a value 50 and then attempt 2 and i want to give it the value 100 and then i want to give it the value of our temp 3 equals celsius from 50 again three completely different objects right one two three but what i'm gonna do is like console.writeline and then i wanna say actually i'm not gonna say i'm just going to do the comparison because i want to keep it simple so temp 1 equals temp 2 and this should return true or false and then temp 1 equals stem 3. so i'm going to compare the completely different objects temp 1 and temp 2 and then temp 1 and m3 the value between 1 and 3 is the same but the object reference is technically different so if i am to change that and i just run it let's see what we get back so you can see first one says false second one says true but normally this comparison even though the value is the same would return false because the reference is different however value object and value of actually behind the scenes will compare the value itself and that's one of the concepts behind the value object that it's not the object it's the value behind it so if you were to implement your own you should be using that approach as well again not something that you can apply everywhere but if you can apply it and if you're dealing with dddn domain related classes i highly recommend you take a look at it and trying to fix this or address it that's all i have for you for this video thank you very much for watching special thanks my patreons for making these videos possible if you want to support me as well you're going to find the link in the description down below leave a like if you liked this video subscribe for more content like edition ring the bell as well and i'll see you in the next video keep coding
Info
Channel: Nick Chapsas
Views: 24,752
Rating: 4.9524283 out of 5
Keywords: Elfocrash, elfo, coding, asp.net, .netcore, dot net, core, C#, how to code, tutorial, asp.net core, csharp, development, software engineering, microsoft, microsoft mvp, .net core, nick chapsas, chapsas, clean architecture, clean code, ddd, domain driven design, valueobject, valueof, primitive obsession, primitive, Treating Primitive Obsession with ValueObjects, DDD in .net, dotnet, .net
Id: h4uldNA1JUE
Channel Id: undefined
Length: 15min 49sec (949 seconds)
Published: Mon Feb 08 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.