@@MilanJovanovicTech Awesome! I love the promotion of defensive programming. Thanks for the tutorial. I do C++ and defensive programming is a must! Love to see it being implemented in other languages.
At 11:31, it might be a good idea to check if FirstName and LastName (as Value Objects) are not null within the ChangeName method. While their inner "Value" property is assured to be non-null or non-empty, the objects themselves could still be null
Turn on "Nullable reference types" - the compiler will warn you if you try pass in a null. Otherwise, there is no chance that a null will make it to the domain.
@@MilanJovanovicTech Unfortunately, the compiler does not enforces it, neither on the caller or the callee side. So any code calling your function may pass in a null reference. Therefore you still must guard against null, even when Nullable reference types is turned on. But this is the only guard you still have to write. The value object with the more complete validation is a nice touch, even if I would have made a single PersonName with both firstName and lastName, as the two are tightly coupled.
Be careful in your example, connectionString wasn't an argument, your expectation was that it was set in the environment but you were guarding a side effect from that. Nice job suggesting the packages, much better than hand baking a lot of code. The new dotnet guards are a nice addition too.
Explicit null check also suppresses all "possible NRE" warnings on using checked variable. Guard classes are hiding that check from the code analyzer, but attribute `[NotNull]` for value argument in method `IsNotNullOrWhiteSpace` can instruct analyzer that the string is fine when the Guard method does not throw an exception.
I was wondering when I watched your pragmatic course, why you don’t use the guard clauses there. In our team, we are using ardalis library, and we check almost everything, even dependency services passed to constructor.
I like using guard clauses, but I prefer the result pattern more. It does make me wonder what is the point of some guard clauses. For example services from DI. When will they be null?
I know that it's a rather silly example, but FirstName and LastName still can technically be null, right? I don't see anything preventing from writting following code FirstName firstName = null; person.UpdateFirstName(firstName);
Little by little I'm learning new things and starting to understand how and why to use different techniques. Can I just ask, is it essential to implement eventhandlers for the domain?
IMO FluentValidation is more fittable in complex scenarious when you need to validate complex objects. Guards just throw exceptions, behind the scenes it's just the same exception throwing. I see no reason to use it, for example, for extracting connection string because it's simple object and it's validated in one place
.NET has built in guard clauses now too. You can use ArgumentNullException.ThrowIfNull(value) for null checks. You can use ArgumentException.ThrowIfNullOrEmpty(stringValue) for strings. In .NET 8 they are adding a bunch of new ones as well under an ArgumentOutOfRangeException. The best part is that the null checks will resolve nullable reference compiler warnings whereas the make your own ensure class method does not as the compiler doesn’t recognize that as a null check.
The stack trace on that guard clause will point to the guard clause, correct? I assume the gaurdclause and throw library will cause the same thing. Also, i would prefer the result pattern for value object creation as well, which Vladimir Khorikov demonstrates in several of his DDD and Validation Fundementals courses on pluralsight.