Domain Validation With .NET | Clean Architecture, DDD, .NET 6

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
how do you approach validation in the domain layer i think one of the most important things in software engineering is having options so in this video i'm going to give you two options for solving validation in the domain layer and then i'm going to leave it up to you to decide which option you are going to use in your own application hi my name is milan and welcome to another video in the clean architecture and domain driven design series this video is going to focus on the validation of the domain layer as i said i'm going to give you two popular solutions that i have used before successfully one is using exceptions and the other one is using result objects so let's see how we would implement these approaches we're going to start off from the gathering entity that we created in one of the previous videos if you didn't watch that video by any chance i suggest you go ahead and watch that one first before proceeding with this one inside of the gathering entity we have a few methods where we perform validation the first one i'm going to check is gathering create let's see what we have here as you can see in the create method we first create a new gathering instance by calling the constructor and then we call the calculate gathering type details method to perform some calculation let's see what we have inside of that method as you can see we are performing separate validations based on the gathering type which is an enum and it has two possible values one is with a fixed number of attendees the other one is with expiration for invitations depending on the gathering type we perform a different validation in this case we are throwing exceptions to implement our validation logic when our domain rule is broken we throw an exception signaling to the caller that something is wrong this is also going to be the first approach that we are going to use for implementing validation so whenever we have a domain rule that is broken we throw an exception in this case we are throwing the base exception class which isn't really the recommended approach because it doesn't carry any meaning other than the message that we are passing in what i usually advise is to create a custom exception for each validation rule so here i prepared in the exceptions folder a base domain exception class that we are going to use for implementing exceptions in the domain layer as you can see it's very simple it just inherits from the base exception class and defines only one constructor let's go ahead and create a few domain exceptions the first one is going to be for the gathering maximum number of attendees in case when it is now so gathering maximum number of attendees is now the main exception i'm going to use the file scope namespace remove the unnecessary using statements i'm going to make the pub the class public also make it sealed because i'm not going to inherit from it and it's going to inherit the domain exception let's go ahead and implement the constructor and at this point we can already use this exception in our gathering entity so instead of throwing the base exception here i'm going to throw our custom exception and everything remains as before let's also do the same for the second exception this one is the when the invitations valid before in hours is null so i will use the same approach gathering invitations valid before in hours is null domain exception i'll do the same thing again public sealed inherits from the main exception and defines the constructor and we can go ahead and use it in our gathering entity at this point this is looking much better and let me explain why what is the benefit of throwing exceptions for implementing validation in this case we are achieving a few things whenever our code reaches this line is going to throw an exception this means that our code will not proceed and we won't end up creating an entity in an invalid state so this is defending our entity constraints another benefit is that throwing exceptions is going to generate a stack trace so that you can catch the exception in one of the upper layers log the error and then in the log you will be able to see which line of code through the exception this will make it easier for you to perform debugging one more benefit is when you are using specific custom domain exceptions and you see that exception in the error logs you are able to accurately pinpoint what caused the exception of course everything is a trade-off in software engineering so when we are using exceptions to enforce validation we gain all of the things that i just mentioned but this comes at the cost of performance in the case when the exception is actually thrown so please keep this in mind if you want to use this approach i'm going to leave this as is and now i'm going to show you the second approach let's move over to the send imitation method as you can see this one was also implemented in a similar fashion where we are again throwing exceptions to enforce our entity constraints however this time i'm going to show you a completely different approach we won't be using exceptions at all we are going to be using result objects for this purpose i already prepared a result object so i won't be implementing it from scratch the result class is very simple as you can see it contains an is success and is failure flag with an error attached to it i also added the generic result class which additionally has a value of type t value which is a generic parameter and let me also show you the error class the code is used so that we can uniquely identify our errors and the message is used to provide some additional debugging information let's go back to the gathering class and see how we would use this result object the first thing i'm going to do is change the return type of the send invitation method i'm going to change it to result of invitation so now we are returning a result object instead of rowing exceptions i'm going to return a failure result so here i'm going to say return result dot failure of invitation because it has to be of the same type and i need to create a new error which accepts two arguments one is the message which i'm going to leave as is and the first is the code which uniquely identifies this error so i'm going to say gathering and i'm going to say inviting creator because this is the validation that we are performing and just fix this so that everything compiles this is how validation would look like when we are using result objects let's also do the same here i'm going to cut this message i'm going to return a result here so i'm going to say return result failure passing the invitation as the generic argument create a new error and the code this time is going to be gathering already passed because this is the validation where we are checking that the gathering has not already completed and we will use the same message as before all right this covers our validation logic so if we are trying to send the invitation to the creator of the gathering we're going to return a gathering inviting creator error if the gathering has already passed we are going to return a gathering already past error and in the happy path we just return the invitation notice that here i'm returning an invitation object but the result here is of type result invitation how this works is inside of the generic result class i defined the static implicit operator for converting an object of type t value into a result of t value let's go back to our gathering entity remember that we change the result of the send imitation method so this affects the color of our code and as you can see we have one reference here inside of the send mutation command handler i'm going to go there and fix the compile issues let's make the result of the send imitation method explicit so that it is more clear what we are working with as you can see here we have an invitation result so let's rename the variable accordingly so invitation result and remember that now we don't have an exception being thrown if our validation fails so we have to check if the invitation result is failure let's for example log error and just return and leave the method otherwise the invitation result is successful and we can safely access the value which represents our invitation object and everything remains the same as before let's again go back to the gathering entity notice that we are always creating a new instance of the error and this is kind of cumbersome to always have to create a new error instance what i usually like to do is to define these errors ahead of time so i'm going to do that now i'll create a new folder i'll call it errors and inside of that folder i'm going to create a new class which i will call domain errors i'm going to make this class static and i'm also going to create another static class inside of it which will be called gathering and inside of the nested gathering class i'm going to define our errors so i'm going to copy paste the error from here so that i can save some time i'll create a new public static read-only field of type error which i will call inviting creator and i'm going to assign it the value that we just copied okay let's also do that for our second error this one is already passed so back in domain errors i create another public static read-only field that returns an error which is called already passed and i sign it the value that we just copied the point of going through all of this trouble is now we can go back to our gathering entity and instead of creating a new error here we can say domain errors gathering and in this case inviting creator and we can do the same thing below domain errors gathering already passed now take a look at how much more expressive our code is we are explicitly saying which error occurred in this line which are occurred in this line and it makes our method much more readable comparing that to the above approach where we are throwing custom exceptions you can see that it's not as nice to read however i would argue that this approach is much more readable and much more maintainable of course this approach also comes with its own trade-offs so let me discuss what i think are the pros and cons when we are using the result object we are making our methods explicit so that the color of our method knows to expect that this method can either succeed or fail and act accordingly additionally when we have validation failures we don't have to pay the performance cost of throwing exceptions we just return a failure result which is performant one additional thing is as the project grows and you keep creating more and more domain errors over time you start to create a very nice catalog of possible errors in the domain layer this can be very useful for anybody new that is coming to the project they can just take a look at all of the possible errors in the domain and get a nice idea of what are some of the possible error types when using result objects you lose the benefit that you have with throwing exceptions where at the line that you throw an exception the execution of your code stops an exception is thrown and a stack trace is generated in this case there is no stack trace so it makes it a little bit harder to figure out where in the code a certain error occurred but this can be mitigated with proper logging in your system so i don't consider it much of a downside alright i showed you two possible approaches for implementing validation in your domain layer one approach was using custom domain exceptions and throwing exception when we have a broken rule the other approach was using result objects and returning failure results when we have a certain domain constraint that has been broken i also discussed what are the pros and cons of each approach and i leave it up to you to decide which approach you like the most and which one you would possibly use in your applications i would really like to know how you approach domain validation so please let me know in the comments and we can start a nice discussion on this topic if you like this video make sure you leave a like subscribe to my channel so that you don't miss any of my future videos and until next time keep being awesome you
Info
Channel: Milan Jovanović
Views: 49,327
Rating: undefined out of 5
Keywords: .net validation, .net 6, .net 7, .net 5, domain validation, ddd validation, domain-driven design validation, .net 6 validation, .net 7 validation, ddd validation .net, ddd validation .net 5, ddd validaton .net 6, ddd validation .net 7, domain-driven design validation .net 6, clean architecture validation .net, clean architecture validation .net 6, clean architecture validation .net 7
Id: KgfzM0QWHrQ
Channel Id: undefined
Length: 12min 30sec (750 seconds)
Published: Tue Aug 30 2022
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.