One rabbit hole which ValueObject might make you stumble: operations over them... Should Celsius have a addition operation? A subtraction operation? A multiplication, a division? For the ones you said yes, which type should be the return? The same type, a raw float, something else entirely? TL,DR: We can only subtract and weighted averages. And for subtraction the result is a class CelsiusDifference which can get get below -273.15ºC and it can have all the 4 operations(addition and subtraction with other Celsius differences, multiplication and division with raw floats). Read more about that by searching for"affine space". Temperatures are actually a nice example to explain it. The mathy name for this kind of structure is an affine space... Addition over temperatures kinda breaks logic: 0ºC + 0ºC = 0ºC But 0ºC = 273.15 K... so 0ºC = 0ºC + 0ºC = 273.15 K + 273.15 K = 546.3 K = 273.15ºC Which is an absurd.... hence addition here is absurd.... this happens to affine spaces because they don't have a "special" 0... in particular, the 0 in Celsius is not the 0 in Kelvin... so addition on both scales give different result(a.k.a. the absurd math I did). On a similar argument you could argue multiplication and division are nonsensical. But, somehow, we still can calculate means... (0ºC + 0ºC) / 2= (0ºC)/2=0ºC (273.15 K + 273.15 K) / 2 = (546.3 K)/2=273.15 K This happens because the means are midpoints... And we still can calculate midpoints. Instead of thinking "(a+b)/2", think: start at "a", then find the vector from "a" to "b", then divide by 2 and apply to "a" a + (b-a)/2... which you check out the math to get (a+b)/2... you can even check that if we flipped "a" and "b", we would have gotten the same result. But note we are using subtraction and division... And here is the hope: we can use subtraction. When we subtract temperatures... the result is not a temperature of an object, but a temperature *difference*, which isn't bound by the same restrictions: you can have an object go from 1000ºC to 0ºC... resulting in a temperature difference which is below the -273.15ºC which would be the absolute 0. Here, the 0 actually has a meaning: no temperature difference, the 2 temperatures compared are the same. So a 0K difference is a 0ºC difference, but 0K temperature is -273.15ºC temperature. Another point which is important to note is that affine differences can be multiplied and divided without breaking logic... And, *sometimes*, they can be added to an element of the affine space to get another element of the affine space: Like I did before: A and B are affine objects B-A and (B-A)/2 are affine differences So A + (B - A)/2... or (A+B)/2 is an affine object... If the coefficients add 1(a.k.a. we have a weighted average), then this is guaranteed to work. Else we are in a wild territory.
I LOVE this! We had an issue w/ postal codes at some point because we're handling them as strings, and this sort of design decision would have been a HUGE help in preventing the massive time waste of looking for where things went wrong. Thank you so much for all of your videos, they're definitely helping add to my "Good ways to program" folder.
I wish more people were open to this kind of idea. So many developers are content validating the same string over and over, at every step in their application. A common response would be "Is it worth making a class just for Celcius?". Yes. Yes, it is.
It's almost three months now I'm trying to get my head around DDD with no success. Please make a whole series on DDD! The way you are good in explaining difficult things, I'm very confident I will be a guru in next to no time. Thanks for the great work up to so far. thanks.
I'm kinda in the same struggle, but luckily for just a week. I feel like this whole DDD/Clean Architecture stuff is the must-know that would enable me to finally tackle software engineering from a very high quality point of view. In fact, I asked above about some resources such as books, online courses and videos that could bring me a very comprehensive view, in order to study it seriously.
@Nickolai Paromov Thank you very very much for the info! I'm taking a look at his stuff, reviews on his books and people look like very happy with his work. I have a question: do you think that is approach to teaching and explaining is similar to the Nick Chapsas's one? Because I often see that even if some mentor explains the same concepts, patterns and shows some code about it, some of them present much hand-written code, so for me it's more difficult to follow, while others tend to keep things more easily understandable by using Nuget packages, like Nick does, such as Automapper, MediatR and stuff and concentrating on higher level concepts.
Looking this video up again because someone recently showed me some code with tons of primitives + implicit mutations in its control flow & they had asked me to find a bug, but I couldn't explain in a short time the full implication I felt went into the refactorings. It's tough to tell sometimes whether another person shares one's understanding without properly scoped reference materials, & that is for sure what brings me back to the channel. Thanks again buddy. Def going to check out value of + one of later
Your mileage may vary, but basically at beginning I thought this is really a nice idea, so I used it at one medium-size project with DDD, event sourcing and all the jazz. After a while my opinion is that the merits does not out-weight the negatives. Negatives being an explosion of types "converters" around serialization, deserialization and AutoMapper, which are needed for translation back and forth of those value type objects to scalar values, which serializers do understand. In short, they look and work quite nice in domain model and suck everywhere else.
Perfect Nick, your channel is awesome In my opinion, for these short lived small value objects, structs are better or records. Temperature, Vector, etc. If we should not use structs for small immutable value objects, where should we utilize structs? Maybe, it is useful to talk about that in a video, I mean where you employ structs
Hi Nick, I'm so grateful for your content! You're opening my eyes on a bigger vision about software development, because I'm not an advanced developer and I'm alone at my company taking challenging decisions. You're DDD approach and the video about Clean Architecture are opening a new world of possibilities: can you give me some information where I can study all the necessary content about this kind of development? Online courses, books and videos, any kind of structured content. I love your work and you're my light, keep working!! 🚀🚀🚀
That's what I always try to explain to people. If you're writing data validation services thay you then inject into your application there's one or both of these problems: 1) You misconfigured the DB, so your engine is not validating the right stuff when you're persisting data 2) You misdesigned the domain, or maybe you didn't misdesign it, but it's still anemic
Wonderful explanation of ValueOf. But I strongly suggest to refrain from using the unit of temperature as the type name. This definition error leads to 'Celsius' being the magnitude holder, when it should be 'TemperatureInCelsius'. Please trust me on that one. The moment you have to convert from one to the other unit within ISQ scope, you will find yourself rewriting all your code. The presented code contains 'Celsius' as a convention, known to the developer only. It could have been 'Bob' as well. ;)
@@milanstevic8424 You did not get my point at all. Please consider that you may have totally misinterpreted my comment by inducing your level of knowledge on a topic that requires a deeper understanding. You've questioned the wrong things, based on your understanding. Try not to throw types, instances, properties, behaviour and naming into one big container, which you may stir vigorously. Degrees Celsius (°C) is a derived unit without any magnitude, so it's neither a quantity nor a measure itself. I suggested the type to be called 'TemperatureInCelsius' as a more understandable quantization compared to naming the type 'Celsius'. See how you proved my point already by totally mixing up things. But have a look at the definitions by yourself, please: en.wikipedia.org/wiki/International_System_of_Units
I like the practice of creating these domain specific primitives, but shouldn't value objects be created as a struct instead of a class so it doesn't need to get allocated on the heap?
how and where something is allocated should not affect your decision making for value objects. use classes (or even better, records) unless you have specific reasons for using structs. Id only use structs if I have something that NEEDS optimisation by “locality” of data.
I know it might not be necessery for the purpose of the video but the TemperatureBelowAbsoluteZeroException class should be [serialisabile] and the best practice is to implement additional constructor with the inner exception.
Yeah that's 100% true. Originally I wouldn't make this exception at all when I first scripted the video but as I was on it i realised that I could talk about domain specific exceptions instead of the generic ArgumentException, which is why I didn't "properly" do it.
The other big advantage to this approach is related to conversions if you have a temperature in Celcius and another in Farnihite then it stops you from adding two temperatures that have differing scales. unless you create the required conversion code so in Celcius class you can have a method to do the conversion from a Farnihite temp so that addition / subtraction works etc. You can also block other math that does not make sense IE you cannot multiple two temperatures together, but it still makes sense to double for example a tempature
The ValueOf library will implicitly convert Celcius to a double, so this is not a benefit in this example. If you use a C# 9.0 record, none of the arithmetic operators are overloaded automatically; the compiler is primarily concerned with equality. Furthermore, if you try to flesh out the example, as soon as you overload the minus operator, you have a problem since a temperature delta can be negative on the Kelvin scale.
Technically you could have made Celsius a struct and have validation inside getter / setter of Temperature property. It will also be more performant option if used in big collections because there will be no boxing/unboxing performance penalty. What do you think, are we not reinventing the wheel at this point?
I had the same thought, honestly. I know this is an example, but don't you want to typically avoid throwing exceptions for stuff like this as well? Because exceptions have to be dealt with, but returning a minimum -253 avoids the client having to deal with it.
Love your videos. If I am making an API with MediatR and FluentValidation, should I still use value objects for things like postcode. If so, should I have a static Validate method to us from the vlaidator instead of getting an exception from the constructor when I map the Command to domain object?
Yeah records make it very easy to use and implement ValueObjects, that's actually exactly what I'm doing since C# 9. I wanted to show the concept to people that might not be familiar with it using a libary that streamlines it.
I will say that with records, there may be a use case for having the value as a collection, which in that case, Microsoft's implementation is best docs.microsoft.com/en-us/dotnet/architecture/microservices/microservice-ddd-cqrs-patterns/implement-value-objects
A note on postal codes - they shouldn't be stored as a string ValueObject, they should be a class of two enums and an int (in the UK). Stringly typed systems are still stringly typed if you add encapsulation. General rule for avoiding stuff like that is that strings generated by code should not be consumed by code, and anytime you're creating a postal code without user input you would break that rule.
Nice! Any particular method you recommend when you want the property to play nice with EF migrations? Not immediately apparent how EF would know how to map that to a particular field type in SQL.
ValueOf has explicit and implicit operators so it should treat it as the primitive that is backing it up but EF to me is the data layer and it doesn’t have to know about domain specific concerns like Celsius for example
I watched this video when you released it. I understood it back then, but I didn't really understand how important it was. Since then I got my first job, and realized just how bad some codebases are when it comes to this. More or less our entire backend database is strings(even for numbers :| ) - some ints, but almost nowhere were any structs or similar used. It's better now that I've started working on it, but still a lot of shit
I love that trick with generics where you have the derived class as one of the generic parameter of the base class. I learned that when I was doing business objects in the .NET Framework 2.0 days, and I thought it looked weird at the time.
Hi. I understand the pros of using the ValueOf library, but why don't use structs insteead of classes for that? Isn't that why the concept of structs were made? Btw, I just discovered your channel 2 weeks ago. I love it and have learned a lot. Thank you.
Structs have conceptual issues with the encapsulation required for ValueObjects. Records actually solve those concerns and come with all the necessary equality check overriding done for you out of the box, so if you are familiar with the concept I would recommend them over ValueOf and structs. Structs were not made to represent value objects though, no.
@@nickchapsas could you please expand on this? I'm using older version of .Net (no records available) and I use structs for this purpose. Great videos, I like your style of coding
11:33 - You also may say "if you care about small decrease in performance - why would you use a language that uses JIT in the first place? Use C++ and manage memory by yourself or use C and almost never touch the Heap!" ;-)
@@igorthelight The idea that you can solve something as complex as software optimization with don't use jit and don't use heap is at best naive. The topic is a lot more complex than you give it credit. Jit languages use precompiled machine-specific libraries, and they can compile machine-specific instructions. AOT code has to branch to support hardware-specific features, and it can never take advantage of hardware optimizations (not all CPUs are the same) that were not implemented in the compiler when it was built. AOT will outperform JIT for short-lived applications, but shot-lived is not the typical use case, and short-lived hardly needs optimization (except for showing off in benchmarks). There is no inherent reason AOT should be faster except for the startup time. The stack vs. heap (want speed? pass by value) is oversimplified again. What is most performant depends on the size and access pattern of the data. A large class or struct object would require a copy of the data; primitives are much faster when passed as values. At least, that is what my education taught me.
Hi, @Nick Chapsas Thanks for sharing the information. What if the Primitive types do not contain any validation? Example: Accepting any Positive number (uint) Public Class Test { public uint Id {get; init;} } Will it be a good idea to move "Id" own class (ValueOf) ?
My biggest issue with wrapping of primitives is that they are purely spefic to your application and nothing else supports them. Database entities, serializers, UI libraries and other things need their values in a an actually primitive format. You might be able to make adapters for some cases (eg. Newtonsoft JSON definitely allows you to make those), but that just adds an unnecessary amount of conjured complexity and library lock-in (good luck switching serialization library or DB library if you have invested into a whole bunch of adapters...). And if the bulk of your application is just shifting values between external interfaces and database (as many are), wrapped primitives are a complete overkill. Another constraint is that you must not particularly care about performance of your code throughout the bulk of your application. I could see using these in eg. a game, since those tend to have a lot of logic, complexity of which could be potentially by having clearly marked values such as Damage or Health (and relationships between those being coded into the wrappers), but then you start coding some basic AI and discover that you lose a % of your FPS to peddling these wrappers. Not that something like Unity would particularly encourage you to introduce novel ways to represent numbers...
How do you handle the exception in the controller so elegantly? Are you catching exceptions in your Startup class? And how do you map the Validation errors specifically to a BadRequest response?
my boss and a bunch of people around him actually thinks the opposite of this... keep telling me "don't use date objects, use strings instead"... the justification is supposedly that timezones keep messing things up, but imo the solution is to just handle localization as intended... I ask myself every day why I still work for them.
Why wouldn't we use the same validation logic in the Celsius class constructor, but without using ValueOf nuget package? I see it simpler this way. The only thing that will not be the same will be the equality check, but that's not like an advantage, but more like a difference. What do you think?
The equality check is way more important of an element for ValueObjects than the validation. Immutability and equality are the building by blocks of the concept. As long as you honour them, the rest is implementation details.
Hi Nick. Great videos here! About the exceptions, don't you think using a Result would be better than throwing an exception? I've seen cases with long computation time, where the performance is bad because of the exceptions.
It really depends on your application. Usually services that use DDD in that capacitiy are bigger and performance isn't the absolute number one priority, so for those services it makes sense to use something like this. I personally work with performance being a feature so for me, this isn't something I can use.
Hi Nick, greate video! Could you explain the advatages of using ValueOf over Record in c# 9? And about the validation that you implemented for Ceucius ValueObject woudn't it be better implement a notification technic instead of throwing an exception? Thank you very much!
The common pattern with ValueObjects is to throw on validation because notification based erroring messes up the flow. Tha being said, you can do whatever works for you. The core concept is wrapping the primitives with obejcts to represent them, not the validation. About Record, yeah I'm using records to do this personally but the counter arguement is that sometimes, not all properties need to be taken into account for equality purposes so it gets a bit more complicated. It's a rare thing but it can happen.
How should business objects propagated between for example an SPA backend and frontend code? The backend is in C# but the frontend is a nodejs app (angular + ts). So if I wanna have the exactly same validation logic on both side, what is the recommended way to do so?
ValueObjects are very useful, but I think the Celcius class is a poor example. The Celcius class seems like an attempt to use a custom ValueObject type to compensate for C#'s (and the CLR's) lack of a units-of-measure feature like the one in F#. In the scientific computing domain, I would strongly suggest using a double here. Otherwise one can't do any math on the value without overloading additional operators or converting back and forth between the primiative type and the custom type. It is worth noting that the ValueOf class will implictly convert to the underlying primative, so adding two Celcius objects will result in a double not a new Celcius object (which would be the domain-based expectation).
What about orm and code first approach? For example: we have an Person type with property CardNumber as string and now we decide to use custom type CardNumber instead of primitive string. If I understand correctly we have an option like "value object" or records, right? So... how we should configure entities (ef core) properly?
This has nothing to do with a database and these objects would not be saved in a db. This is about the domain layer which is about 2 layers away from the database.
Hi Is it possible to use ValueOf directly in the http request? For instance, I have a POST controller method: public async Task SaveTheWeather(WeatherForecast weather) { } Does the framework correctly bind a value got from http payload to Celsius field?
It is possible but you should do it. The donation layer should not leak through the Api layer. You should map it to a version api response contract instead
Hey, it's been a year since you posted this. I've seen that ValueOf is not being updated anymore. Can you provide some insights about mixing FluentValidation with Value Objects? (using current version of Valueof of or any other library)? Thank you for all of your amazing content.
At 3:15 he mentions a domain project and a "dot contracts project". I've done quite a bit of googling but I cannot determine what that is. Can anyone provide me with a little more information please?
Hey John. I'm refering to the project names here. The Domain and Contracts folders would be projects themselves instead of being in the PrimitiveObsession.Api project. The names I was referring to are the final names of those projects which would be PrimitiveObsession.Domain and PrimitiveObsession.Contracts
Hi, I've been struggling to do a simple thing with this library. Let's say I wan't to enforce Uppercase when I new up my value object. In this case I should use the VO.From(s) but I have no way to override the from method. Am I doing something wrong? thanks for your help.
@@nickchapsas Thanks for your quick response Nick. I don't want to validate it. I just want to transform my values, give lowercase strings to the From method store them as uppercase. In a constructor I would simply ToUppercase my strings before assigning them to private fields.
@@richardhaughton9633 It's not the responsibility of the ValueObejct to mutate the data. It's it's responsibility to validate that the data used fit specific criteria .
These are just in-code objects they wouldn't translate to db objects. In DB you would use their actual value and that's done with implicit operators which will automatically convert them to their backing primitive
The API response is a contract so things like the valueobject don't matter. It will be mapped to a double to be returned to the client. That being said ValueOf is implementing implicit and expicit operators so it would do the conversion for you.
The implicit operator in ValueOf has been removed, so perhaps a different approach may yield better results. Change the WeatherForecast class and add a constructor, accepting a Celsius value. Expose a readonly property TemperatureC, which just returns the Value of the Celsius object. e.g. public class WeatherForecast { private readonly Celsius _celsius; public WeatherForecast(Celsius celsius) { _celsius = celsius; } public DateTime Date { get; set; } public double TemperatureC => _celsius.Value; public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); public string Summary { get; set; } }
The action method in the controller would also require a minor change: [HttpGet] public IEnumerable Get() { var rng = new Random(); return Enumerable.Range(1, 5).Select(index => new WeatherForecast(Celsius.From(rng.Next(-20, 55))) { Date = DateTime.Now.AddDays(index), Summary = Summaries[rng.Next(Summaries.Length)] }) .ToArray(); }
Records can be used but there isn't really a "better" choice but rather what type of implementation of ValueObejct you wanna go with. ValueOf comes with a lot out of the box and it will do a lot of stuff that records will do for you, but you can re-implement it in records and you will be fine
You've got a lot of very interesting videos. Keep going. One question though. I am trying to combine valueof and mapster and doesnt seem to be working. I tried with that model. public class Birthday:ValueOf Mapster then always wants to new up Birthday but cannot do because the Value property is protected. It should Birthday.From instead. Is there any trick to achieve that? Birthday = p1.Birthday == null ? null : new Birthday() {Value = p1.Birthday.Value} should do the following Birthday = p1.Birthday == null ? null : Birthday.From(p1.Birthday.Value)
Lovely video Nick! Primitive obsession is not something I was aware of before watching this, but trying to combat it makes a lot of sense. Gonna keep this in mind :D
Hi Nick. What is your opinion on using Value Objects for everything, including Id fields etc.? Please tag me if you respond :) else I don’t get a notification.
Wouldn't the ValueOf be just better as a struct? You don't change the value, just instantiate it every time, and you don't compare references but values. If I would see it in the wild, but without the knowledge how it works, I suspect it would confuse me.
This is actually mostly false. Properties will be allocated on the heap along with their parent class, no matter whether they are a reference of a value type. Also passing value types around is way more expensive than passing reference types around. Also the whole idea of the value object is that it's created in a very controlled way and structs don't lend themselves for such a usecase due to their initializaiton contraints. At this point your best bet in C# for a value object is a record.
@@nickchapsas Yes, I see what you mean. I personally don't like this inheritance, as I prefer having control over how things flow, and inheriting from ValueOf everytime makes me indirectly lose control over the creation process of the object. I would prefer rewriting GetHashCode and .Equals everytime (it's not that annoying and a lot clearer to me). I tend to limit inheritance as much as possible, and only use it when it actually makes sense for things to inherit from another. Storing everything on the heap makes me quite worried about garbage collection (working in the video game industry, I absolutely need to have the least amount of garbage created to avoid frametime issues when the GC is triggered).
If only we could inherit from structs then this performance issue wouldn't exist... I get why structs vs classes were designed the way they were in .NET but come on, let the developers do what they want. If we want a value-based class, let us have it.
I really like using domain objects but not sure how I feel about adding an entire package for a 95 line class, it feel like we didn't learn the lessons of trivial packages from . Also, looks like it is a class rather than a struct, so anything more than trivial usage is likely to start tanking performance
Tanking is a strong word. If newing up objects is your performance bottleneck then you must be wrtting really optimized code. ValueOf is not everyone's cuppa but it's a perfect introduction to the concept. Structs don't play nicely with ValueObjects due to their encapsulation caveats.
@@nickchapsas It wouldn't be newing them but accessing them off the heap that would be slow. I think a solution that uses source generators would be the best; no need to write the boiler plate code and it would allow nice optimal structs that don't have heap allocations and calls
@@jonohiggs I run some benchmarks out of curiosity to get the actual performance degradation on this. Turns out that the hit is on average 16.5 nanoseconds in terms of speed (from 1.8ns without ValueOf) and double the original allocated memory for the underlying type. If you bake validation logic in there then it hits 4ns which is still 4.5 times faster than Value of but we are talking nanoseconds so feels more like premature optimization to me to tell someone who is just starting with the concept to not use this library due to performance issues.
@@nickchapsas I'd be curious to see the differences between a primative, a struct and the ValueOf, I suspect a struct would have some overhead and the class to have more. Thing with a simple benchmark is that it will probably be able to have all bit of memory in L1 cache at the same time, so in a larger system when accessing the value from somewhere else on the heap result in a cache miss would have a much higher perf penalty
Could you not do the same with a record (so that you get immutability and equality) and use properties with backing fields so that you can add validation within the property setters?
This was addressed in another comment. Stucts have issues when used in this concept because of their encapsulation capabilities. The recommended approach, if you don't wanna use ValueOf IMO is to use a Record since it comes with a lot of the ValueObject concepts out of the box (immutability, equality based on values etc)
One thing I think is if you want to have a base value object class having some common behaviour for equality comparer or any other behaviour, than you can not use inheritance in structures (you can implement interface though but, u may not want to repeat same logic on every value object declaration) ,I am also on learning curve so if I have misunderstood this ,please do let me know.
Hi Nick. After watching this video, I looked at the source code of this library and I was not happy. So a friend and I wrote a library, and then we wrote a second one that extends the first, but uses FluentValidation to perform the validations. We publish both libraries on Nuget. Both are open-source. This is the repository of our project: github.com/ThiagoAcam/ValueOf If you can, please give us an opinion. Thanks
I am very sorry but I won't ask my domains to handle exceptions during "implicit" convertions. In fact, in my humble opinion, domain and application layers should never deal with nulls and exceptions.
The domain throws exceptions it doesn't handle them. By definition they are domain specific exceptions. If you wanna get your ValueObjects to validate in a different way you are more than welcome to do it, but traditionally, this is how it works.
@@nickchapsas There is no real truth only (some) experience and opinion here. If we get a step back, values that need their parsing/conversion to be validated comes from an external source. In DDD, that source is responsable of that. If it's from UI or any datasource, external service, etc... the implementation layer should deal with that kind of issues. I tend to have my domain do only business logic. It deals only with Either (yes, functional) failures, domain models and abstracted infrastucture services. Thank you very much Nick for your great content and amazing quality of your videos.
Please make a whole series on DDD! You’re great, keep it up!
Would love to see it too
Absolutely thumb up
Thumb Up
yes pleeease
You know what? What's a pretty good idea
"A double can have a wide variety of number, from Plus a lot to Minus a lot" - Nick Chapsas, 2021
One rabbit hole which ValueObject might make you stumble: operations over them...
Should Celsius have a addition operation? A subtraction operation? A multiplication, a division?
For the ones you said yes, which type should be the return? The same type, a raw float, something else entirely?
TL,DR: We can only subtract and weighted averages. And for subtraction the result is a class CelsiusDifference which can get get below -273.15ºC and it can have all the 4 operations(addition and subtraction with other Celsius differences, multiplication and division with raw floats). Read more about that by searching for"affine space".
Temperatures are actually a nice example to explain it. The mathy name for this kind of structure is an affine space...
Addition over temperatures kinda breaks logic:
0ºC + 0ºC = 0ºC
But 0ºC = 273.15 K... so
0ºC = 0ºC + 0ºC = 273.15 K + 273.15 K = 546.3 K = 273.15ºC
Which is an absurd.... hence addition here is absurd.... this happens to affine spaces because they don't have a "special" 0... in particular, the 0 in Celsius is not the 0 in Kelvin... so addition on both scales give different result(a.k.a. the absurd math I did).
On a similar argument you could argue multiplication and division are nonsensical.
But, somehow, we still can calculate means...
(0ºC + 0ºC) / 2= (0ºC)/2=0ºC
(273.15 K + 273.15 K) / 2 = (546.3 K)/2=273.15 K
This happens because the means are midpoints... And we still can calculate midpoints.
Instead of thinking "(a+b)/2", think: start at "a", then find the vector from "a" to "b", then divide by 2 and apply to "a"
a + (b-a)/2... which you check out the math to get (a+b)/2... you can even check that if we flipped "a" and "b", we would have gotten the same result.
But note we are using subtraction and division...
And here is the hope: we can use subtraction. When we subtract temperatures... the result is not a temperature of an object, but a temperature *difference*, which isn't bound by the same restrictions: you can have an object go from 1000ºC to 0ºC... resulting in a temperature difference which is below the -273.15ºC which would be the absolute 0.
Here, the 0 actually has a meaning: no temperature difference, the 2 temperatures compared are the same.
So a 0K difference is a 0ºC difference, but 0K temperature is -273.15ºC temperature.
Another point which is important to note is that affine differences can be multiplied and divided without breaking logic...
And, *sometimes*, they can be added to an element of the affine space to get another element of the affine space:
Like I did before:
A and B are affine objects
B-A and (B-A)/2 are affine differences
So A + (B - A)/2... or (A+B)/2 is an affine object...
If the coefficients add 1(a.k.a. we have a weighted average), then this is guaranteed to work. Else we are in a wild territory.
I LOVE this! We had an issue w/ postal codes at some point because we're handling them as strings, and this sort of design decision would have been a HUGE help in preventing the massive time waste of looking for where things went wrong.
Thank you so much for all of your videos, they're definitely helping add to my "Good ways to program" folder.
I wish more people were open to this kind of idea. So many developers are content validating the same string over and over, at every step in their application. A common response would be "Is it worth making a class just for Celcius?". Yes. Yes, it is.
I watched the whole video (excellent video), but your comment actually made me understand ValueObjects. Interesting how that works. Thanks!
People tend to be happy when they have 2 uuids floating around that can now never be confused in code because you did this
It's almost three months now I'm trying to get my head around DDD with no success. Please make a whole series on DDD! The way you are good in explaining difficult things, I'm very confident I will be a guru in next to no time.
Thanks for the great work up to so far. thanks.
I'm kinda in the same struggle, but luckily for just a week. I feel like this whole DDD/Clean Architecture stuff is the must-know that would enable me to finally tackle software engineering from a very high quality point of view. In fact, I asked above about some resources such as books, online courses and videos that could bring me a very comprehensive view, in order to study it seriously.
@Nickolai Paromov Thank you very very much for the info! I'm taking a look at his stuff, reviews on his books and people look like very happy with his work. I have a question: do you think that is approach to teaching and explaining is similar to the Nick Chapsas's one? Because I often see that even if some mentor explains the same concepts, patterns and shows some code about it, some of them present much hand-written code, so for me it's more difficult to follow, while others tend to keep things more easily understandable by using Nuget packages, like Nick does, such as Automapper, MediatR and stuff and concentrating on higher level concepts.
@Nickolai Paromov ok I get it! Thank you very much!
Looking this video up again because someone recently showed me some code with tons of primitives + implicit mutations in its control flow & they had asked me to find a bug, but I couldn't explain in a short time the full implication I felt went into the refactorings.
It's tough to tell sometimes whether another person shares one's understanding without properly scoped reference materials, & that is for sure what brings me back to the channel. Thanks again buddy.
Def going to check out value of + one of later
Your mileage may vary, but basically at beginning I thought this is really a nice idea, so I used it at one medium-size project with DDD, event sourcing and all the jazz. After a while my opinion is that the merits does not out-weight the negatives. Negatives being an explosion of types "converters" around serialization, deserialization and AutoMapper, which are needed for translation back and forth of those value type objects to scalar values, which serializers do understand. In short, they look and work quite nice in domain model and suck everywhere else.
Yep, works in the small using C#, works in the large using F#
@@AnythingGodamnit Never looked at F# much as a C# student. What makes it so great for you to say that DDD works well everywhere in F#?
Perfect Nick, your channel is awesome
In my opinion, for these short lived small value objects, structs are better or records. Temperature, Vector, etc. If we should not use structs for small immutable value objects, where should we utilize structs? Maybe, it is useful to talk about that in a video, I mean where you employ structs
Such a nice explanation, thank you!
Hi Nick, I'm so grateful for your content! You're opening my eyes on a bigger vision about software development, because I'm not an advanced developer and I'm alone at my company taking challenging decisions. You're DDD approach and the video about Clean Architecture are opening a new world of possibilities: can you give me some information where I can study all the necessary content about this kind of development? Online courses, books and videos, any kind of structured content.
I love your work and you're my light, keep working!! 🚀🚀🚀
this is so clever... i never thought of this. i will definitely start doing it in my code!!
Very simple explanation of Value Objects in DDD!
Thanks
That's what I always try to explain to people. If you're writing data validation services thay you then inject into your application there's one or both of these problems:
1) You misconfigured the DB, so your engine is not validating the right stuff when you're persisting data
2) You misdesigned the domain, or maybe you didn't misdesign it, but it's still anemic
Wonderful explanation of ValueOf. But I strongly suggest to refrain from using the unit of temperature as the type name. This definition error leads to 'Celsius' being the magnitude holder, when it should be 'TemperatureInCelsius'. Please trust me on that one. The moment you have to convert from one to the other unit within ISQ scope, you will find yourself rewriting all your code. The presented code contains 'Celsius' as a convention, known to the developer only. It could have been 'Bob' as well. ;)
@@milanstevic8424 You did not get my point at all. Please consider that you may have totally misinterpreted my comment by inducing your level of knowledge on a topic that requires a deeper understanding. You've questioned the wrong things, based on your understanding. Try not to throw types, instances, properties, behaviour and naming into one big container, which you may stir vigorously. Degrees Celsius (°C) is a derived unit without any magnitude, so it's neither a quantity nor a measure itself. I suggested the type to be called 'TemperatureInCelsius' as a more understandable quantization compared to naming the type 'Celsius'. See how you proved my point already by totally mixing up things. But have a look at the definitions by yourself, please: en.wikipedia.org/wiki/International_System_of_Units
One of the best lectures. Tx!
These videos are pure gold nuggets. Let me know if you ever start as a teacher on skillshare - keep up the good work
I like the practice of creating these domain specific primitives, but shouldn't value objects be created as a struct instead of a class so it doesn't need to get allocated on the heap?
how and where something is allocated should not affect your decision making for value objects. use classes (or even better, records) unless you have specific reasons for using structs. Id only use structs if I have something that NEEDS optimisation by “locality” of data.
Thank you for the video.
Small note: My lead would assassinate me for direct comparing doubles for equality xD.
As far as I understand comparing ValueOf doubles compares doubles under the hood so any of the usual problems associated with that still remain.
I know it might not be necessery for the purpose of the video but the TemperatureBelowAbsoluteZeroException class should be [serialisabile] and the best practice is to implement additional constructor with the inner exception.
Yeah that's 100% true. Originally I wouldn't make this exception at all when I first scripted the video but as I was on it i realised that I could talk about domain specific exceptions instead of the generic ArgumentException, which is why I didn't "properly" do it.
The other big advantage to this approach is related to conversions
if you have a temperature in Celcius and another in Farnihite then it stops you from adding two temperatures that have differing scales. unless you create the required conversion code so in Celcius class you can have a method to do the conversion from a Farnihite temp so that addition / subtraction works etc.
You can also block other math that does not make sense IE you cannot multiple two temperatures together, but it still makes sense to double for example a tempature
The ValueOf library will implicitly convert Celcius to a double, so this is not a benefit in this example. If you use a C# 9.0 record, none of the arithmetic operators are overloaded automatically; the compiler is primarily concerned with equality. Furthermore, if you try to flesh out the example, as soon as you overload the minus operator, you have a problem since a temperature delta can be negative on the Kelvin scale.
Technically you could have made Celsius a struct and have validation inside getter / setter of Temperature property. It will also be more performant option if used in big collections because there will be no boxing/unboxing performance penalty. What do you think, are we not reinventing the wheel at this point?
I had the same thought, honestly. I know this is an example, but don't you want to typically avoid throwing exceptions for stuff like this as well? Because exceptions have to be dealt with, but returning a minimum -253 avoids the client having to deal with it.
This is going to save me a lot of time.
Love your videos. If I am making an API with MediatR and FluentValidation, should I still use value objects for things like postcode. If so, should I have a static Validate method to us from the vlaidator instead of getting an exception from the constructor when I map the Command to domain object?
You can use records instead of a ValueObject class pretty easily. Love this new feature
Yeah records make it very easy to use and implement ValueObjects, that's actually exactly what I'm doing since C# 9. I wanted to show the concept to people that might not be familiar with it using a libary that streamlines it.
How's the entity framework support for record? I would like to move away from enums
@@MrBestard works perfectly. Just know that change tracking doesn't work with copied values
I will say that with records, there may be a use case for having the value as a collection, which in that case, Microsoft's implementation is best docs.microsoft.com/en-us/dotnet/architecture/microservices/microservice-ddd-cqrs-patterns/implement-value-objects
Great video. I hope you explore DDD in new videos. Thanks.
Hi Nick, really cool video , hope you'll continue explore this approach(DDD) futher
A note on postal codes - they shouldn't be stored as a string ValueObject, they should be a class of two enums and an int (in the UK). Stringly typed systems are still stringly typed if you add encapsulation.
General rule for avoiding stuff like that is that strings generated by code should not be consumed by code, and anytime you're creating a postal code without user input you would break that rule.
Perfect! I love these videos!
Nice! Any particular method you recommend when you want the property to play nice with EF migrations? Not immediately apparent how EF would know how to map that to a particular field type in SQL.
ValueOf has explicit and implicit operators so it should treat it as the primitive that is backing it up but EF to me is the data layer and it doesn’t have to know about domain specific concerns like Celsius for example
@@nickchapsas So you'd be adding these valueof props to your dtos versus the core data entity that represents the db table?
This is really useful, thanks 🙏
I watched this video when you released it. I understood it back then, but I didn't really understand how important it was.
Since then I got my first job, and realized just how bad some codebases are when it comes to this. More or less our entire backend database is strings(even for numbers :| ) - some ints, but almost nowhere were any structs or similar used. It's better now that I've started working on it, but still a lot of shit
I love that trick with generics where you have the derived class as one of the generic parameter of the base class. I learned that when I was doing business objects in the .NET Framework 2.0 days, and I thought it looked weird at the time.
Hi. I understand the pros of using the ValueOf library, but why don't use structs insteead of classes for that? Isn't that why the concept of structs were made?
Btw, I just discovered your channel 2 weeks ago. I love it and have learned a lot. Thank you.
Structs have conceptual issues with the encapsulation required for ValueObjects. Records actually solve those concerns and come with all the necessary equality check overriding done for you out of the box, so if you are familiar with the concept I would recommend them over ValueOf and structs. Structs were not made to represent value objects though, no.
@@nickchapsas could you please expand on this? I'm using older version of .Net (no records available) and I use structs for this purpose.
Great videos, I like your style of coding
But a single property used as a value object doesn't easily wire up to EF 6, EF Core. Not as I could find it. The solution works well in a vacuum.
11:33 - You also may say "if you care about small decrease in performance - why would you use a language that uses JIT in the first place? Use C++ and manage memory by yourself or use C and almost never touch the Heap!" ;-)
This is just plain wrong...
@@EspenSkaufel Please elaborate :-)
@@igorthelight The idea that you can solve something as complex as software optimization with don't use jit and don't use heap is at best naive.
The topic is a lot more complex than you give it credit. Jit languages use precompiled machine-specific libraries, and they can compile machine-specific instructions. AOT code has to branch to support hardware-specific features, and it can never take advantage of hardware optimizations (not all CPUs are the same) that were not implemented in the compiler when it was built.
AOT will outperform JIT for short-lived applications, but shot-lived is not the typical use case, and short-lived hardly needs optimization (except for showing off in benchmarks). There is no inherent reason AOT should be faster except for the startup time.
The stack vs. heap (want speed? pass by value) is oversimplified again. What is most performant depends on the size and access pattern of the data. A large class or struct object would require a copy of the data; primitives are much faster when passed as values.
At least, that is what my education taught me.
@@EspenSkaufel Fair points!
Hi, @Nick Chapsas Thanks for sharing the information.
What if the Primitive types do not contain any validation?
Example: Accepting any Positive number (uint)
Public Class Test
{
public uint Id {get; init;}
}
Will it be a good idea to move "Id" own class (ValueOf) ?
My biggest issue with wrapping of primitives is that they are purely spefic to your application and nothing else supports them. Database entities, serializers, UI libraries and other things need their values in a an actually primitive format. You might be able to make adapters for some cases (eg. Newtonsoft JSON definitely allows you to make those), but that just adds an unnecessary amount of conjured complexity and library lock-in (good luck switching serialization library or DB library if you have invested into a whole bunch of adapters...).
And if the bulk of your application is just shifting values between external interfaces and database (as many are), wrapped primitives are a complete overkill. Another constraint is that you must not particularly care about performance of your code throughout the bulk of your application. I could see using these in eg. a game, since those tend to have a lot of logic, complexity of which could be potentially by having clearly marked values such as Damage or Health (and relationships between those being coded into the wrappers), but then you start coding some basic AI and discover that you lose a % of your FPS to peddling these wrappers. Not that something like Unity would particularly encourage you to introduce novel ways to represent numbers...
How do you handle the exception in the controller so elegantly? Are you catching exceptions in your Startup class? And how do you map the Validation errors specifically to a BadRequest response?
Shit man, Nick. Just discovered your vids. They're great!
Hi Nick, since the introduction of the record, the valueof nuget package is obsolete right?
my boss and a bunch of people around him actually thinks the opposite of this... keep telling me "don't use date objects, use strings instead"... the justification is supposedly that timezones keep messing things up, but imo the solution is to just handle localization as intended... I ask myself every day why I still work for them.
Why wouldn't we use the same validation logic in the Celsius class constructor, but without using ValueOf nuget package? I see it simpler this way. The only thing that will not be the same will be the equality check, but that's not like an advantage, but more like a difference. What do you think?
The equality check is way more important of an element for ValueObjects than the validation. Immutability and equality are the building by blocks of the concept. As long as you honour them, the rest is implementation details.
Hi Nick. Great videos here!
About the exceptions, don't you think using a Result would be better than throwing an exception?
I've seen cases with long computation time, where the performance is bad because of the exceptions.
It really depends on your application. Usually services that use DDD in that capacitiy are bigger and performance isn't the absolute number one priority, so for those services it makes sense to use something like this. I personally work with performance being a feature so for me, this isn't something I can use.
Hi Nick( or anyone), quick question. Should the below Absolute Zero exception, display the value of Absolute Zero?
I assume the answer would be "if you want it to"
Hi Nick, greate video! Could you explain the advatages of using ValueOf over Record in c# 9? And about the validation that you implemented for Ceucius ValueObject woudn't it be better implement a notification technic instead of throwing an exception? Thank you very much!
The common pattern with ValueObjects is to throw on validation because notification based erroring messes up the flow. Tha being said, you can do whatever works for you. The core concept is wrapping the primitives with obejcts to represent them, not the validation. About Record, yeah I'm using records to do this personally but the counter arguement is that sometimes, not all properties need to be taken into account for equality purposes so it gets a bit more complicated. It's a rare thing but it can happen.
Since you can overload the == operator in C#, how do you check for reference equality?
How should business objects propagated between for example an SPA backend and frontend code? The backend is in C# but the frontend is a nodejs app (angular + ts).
So if I wanna have the exactly same validation logic on both side, what is the recommended way to do so?
Hi Nick, do we have any session on design patters
ValueObjects are very useful, but I think the Celcius class is a poor example. The Celcius class seems like an attempt to use a custom ValueObject type to compensate for C#'s (and the CLR's) lack of a units-of-measure feature like the one in F#. In the scientific computing domain, I would strongly suggest using a double here. Otherwise one can't do any math on the value without overloading additional operators or converting back and forth between the primiative type and the custom type. It is worth noting that the ValueOf class will implictly convert to the underlying primative, so adding two Celcius objects will result in a double not a new Celcius object (which would be the domain-based expectation).
how to deal with VO in EFCore?
Owned types, denormalized database may work ?
What about orm and code first approach? For example: we have an Person type with property CardNumber as string and now we decide to use custom type CardNumber instead of primitive string. If I understand correctly we have an option like "value object" or records, right? So... how we should configure entities (ef core) properly?
This has nothing to do with a database and these objects would not be saved in a db. This is about the domain layer which is about 2 layers away from the database.
Hi
Is it possible to use ValueOf directly in the http request?
For instance, I have a POST controller method: public async Task SaveTheWeather(WeatherForecast weather) { }
Does the framework correctly bind a value got from http payload to Celsius field?
It is possible but you should do it. The donation layer should not leak through the Api layer. You should map it to a version api response contract instead
If you create a course on DDD would not hesitate to buy it
Hey, it's been a year since you posted this. I've seen that ValueOf is not being updated anymore. Can you provide some insights about mixing FluentValidation with Value Objects? (using current version of Valueof of or any other library)?
Thank you for all of your amazing content.
You can try with records. They have these functionality build in by default.
How to you deal with serialization of ValueOf object. It doesn't seems to serialize to the primitive type.
It does. There is serialisation support
Can we just override casting to double with implicit operator for example and get rid of the Celsius.From ?
At 3:15 he mentions a domain project and a "dot contracts project". I've done quite a bit of googling but I cannot determine what that is. Can anyone provide me with a little more information please?
Hey John. I'm refering to the project names here. The Domain and Contracts folders would be projects themselves instead of being in the PrimitiveObsession.Api project. The names I was referring to are the final names of those projects which would be PrimitiveObsession.Domain and PrimitiveObsession.Contracts
Hi, I've been struggling to do a simple thing with this library. Let's say I wan't to enforce Uppercase when I new up my value object. In this case I should use the VO.From(s) but I have no way to override the from method. Am I doing something wrong? thanks for your help.
You don’t need to override the from method. Override the Validate method and add the validation logic there
@@nickchapsas Thanks for your quick response Nick. I don't want to validate it. I just want to transform my values, give lowercase strings to the From method store them as uppercase. In a constructor I would simply ToUppercase my strings before assigning them to private fields.
In other words just apply a transformation to my values. Let's say for an integer you would want to multiply it by 2 for whatever reason.
@@richardhaughton9633 It's not the responsibility of the ValueObejct to mutate the data. It's it's responsibility to validate that the data used fit specific criteria .
Would you also create a table in DB for each value object? Or would you convert the Celsius class to a double - and if so, how?
These are just in-code objects they wouldn't translate to db objects. In DB you would use their actual value and that's done with implicit operators which will automatically convert them to their backing primitive
Love your videos 😁✌️
Great !
Maybe I missed it, but how is the API response unwrapping the value object? I.e it returns ‘“temp”: 30’ and not ‘“temp”: { “value”: 30}’
The API response is a contract so things like the valueobject don't matter. It will be mapped to a double to be returned to the client. That being said ValueOf is implementing implicit and expicit operators so it would do the conversion for you.
Ahh ok, the implicit operator stopped you getting a compilation error in your mapper 👍🏻
The implicit operator in ValueOf has been removed, so perhaps a different approach may yield better results.
Change the WeatherForecast class and add a constructor, accepting a Celsius value.
Expose a readonly property TemperatureC, which just returns the Value of the Celsius object.
e.g.
public class WeatherForecast
{
private readonly Celsius _celsius;
public WeatherForecast(Celsius celsius)
{
_celsius = celsius;
}
public DateTime Date { get; set; }
public double TemperatureC => _celsius.Value;
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
public string Summary { get; set; }
}
The action method in the controller would also require a minor change:
[HttpGet]
public IEnumerable Get()
{
var rng = new Random();
return Enumerable.Range(1, 5).Select(index => new WeatherForecast(Celsius.From(rng.Next(-20, 55)))
{
Date = DateTime.Now.AddDays(index),
Summary = Summaries[rng.Next(Summaries.Length)]
})
.ToArray();
}
I hope this helps 😃
Is there any way to use oneOf with a validation using MediatR's pipeline behaviors?
Do you still need them in C# 9 or are records the better choice there?
Records can be used but there isn't really a "better" choice but rather what type of implementation of ValueObejct you wanna go with. ValueOf comes with a lot out of the box and it will do a lot of stuff that records will do for you, but you can re-implement it in records and you will be fine
Are you not worried about using a random package as such an important dependency?
Idea for next vid: usage with ef
You are amazing
You've got a lot of very interesting videos. Keep going. One question though. I am trying to combine valueof and mapster and doesnt seem to be working.
I tried with that model.
public class Birthday:ValueOf
Mapster then always wants to new up Birthday but cannot do because the Value property is protected. It should Birthday.From instead. Is there any trick to achieve that?
Birthday = p1.Birthday == null ? null : new Birthday() {Value = p1.Birthday.Value}
should do the following
Birthday = p1.Birthday == null ? null : Birthday.From(p1.Birthday.Value)
Lovely video Nick! Primitive obsession is not something I was aware of before watching this, but trying to combat it makes a lot of sense. Gonna keep this in mind :D
Hi Nick.
What is your opinion on using Value Objects for everything, including Id fields etc.?
Please tag me if you respond :) else I don’t get a notification.
How to map this with entityframework
isn't better to use struct for this specific scenario?
is there a similar library for java?
You can make the type system work for you: It can prevent you from passing a postcode when you meant to pass a phone number.
I'm surprised by the comment about performance. Doesn't the compiler replace your ValueObject with the primitive + a static method for validate?!
Not if it’s a class no
Shouldn't the domain type here be "Temperature", not "Celsius"?
It should be Celsius, inheriting from Temperature but I skipped that part since it’s the only temp measurement in the video
I think F# espouses this cure for primitive obsession. It is a language feature.
would you mind sharing that little project on github?
The source code for my videos is available on GitHub for my Patreons
Wouldn't the ValueOf be just better as a struct? You don't change the value, just instantiate it every time, and you don't compare references but values. If I would see it in the wild, but without the knowledge how it works, I suspect it would confuse me.
I can only hear the words “primitive obsession” in Vladimir Khorikov’s voice…
I would use a struct in this case, and not use ValueOf. Allocating all variables on the heap doesn't sound acceptable to me
This is actually mostly false. Properties will be allocated on the heap along with their parent class, no matter whether they are a reference of a value type. Also passing value types around is way more expensive than passing reference types around. Also the whole idea of the value object is that it's created in a very controlled way and structs don't lend themselves for such a usecase due to their initializaiton contraints. At this point your best bet in C# for a value object is a record.
@@nickchapsas Yes, I see what you mean. I personally don't like this inheritance, as I prefer having control over how things flow, and inheriting from ValueOf everytime makes me indirectly lose control over the creation process of the object.
I would prefer rewriting GetHashCode and .Equals everytime (it's not that annoying and a lot clearer to me). I tend to limit inheritance as much as possible, and only use it when it actually makes sense for things to inherit from another.
Storing everything on the heap makes me quite worried about garbage collection (working in the video game industry, I absolutely need to have the least amount of garbage created to avoid frametime issues when the GC is triggered).
te amo
eu tbm
Why not just use struct’s?
If only we could inherit from structs then this performance issue wouldn't exist...
I get why structs vs classes were designed the way they were in .NET but come on, let the developers do what they want.
If we want a value-based class, let us have it.
I really like using domain objects but not sure how I feel about adding an entire package for a 95 line class, it feel like we didn't learn the lessons of trivial packages from . Also, looks like it is a class rather than a struct, so anything more than trivial usage is likely to start tanking performance
Tanking is a strong word. If newing up objects is your performance bottleneck then you must be wrtting really optimized code. ValueOf is not everyone's cuppa but it's a perfect introduction to the concept. Structs don't play nicely with ValueObjects due to their encapsulation caveats.
@@nickchapsas It wouldn't be newing them but accessing them off the heap that would be slow. I think a solution that uses source generators would be the best; no need to write the boiler plate code and it would allow nice optimal structs that don't have heap allocations and calls
@@jonohiggs I run some benchmarks out of curiosity to get the actual performance degradation on this. Turns out that the hit is on average 16.5 nanoseconds in terms of speed (from 1.8ns without ValueOf) and double the original allocated memory for the underlying type. If you bake validation logic in there then it hits 4ns which is still 4.5 times faster than Value of but we are talking nanoseconds so feels more like premature optimization to me to tell someone who is just starting with the concept to not use this library due to performance issues.
@@nickchapsas I'd be curious to see the differences between a primative, a struct and the ValueOf, I suspect a struct would have some overhead and the class to have more. Thing with a simple benchmark is that it will probably be able to have all bit of memory in L1 cache at the same time, so in a larger system when accessing the value from somewhere else on the heap result in a cache miss would have a much higher perf penalty
Could you not do the same with a record (so that you get immutability and equality) and use properties with backing fields so that you can add validation within the property setters?
Sure but a record is still a struct or a class. You'd be reinventing the wheel at that point
Why when I try to serialize it I get additional {"Value" : "{realValue}"}. I try not to write any converter. I used JsonSerializer.Serialize
Why not to use structures this way? Usage is quite the same, performance is a bit better
This was addressed in another comment. Stucts have issues when used in this concept because of their encapsulation capabilities. The recommended approach, if you don't wanna use ValueOf IMO is to use a Record since it comes with a lot of the ValueObject concepts out of the box (immutability, equality based on values etc)
One thing I think is if you want to have a base value object class having some common behaviour for equality comparer or any other behaviour, than you can not use inheritance in structures (you can implement interface though but, u may not want to repeat same logic on every value object declaration)
,I am also on learning curve so if I have misunderstood this ,please do let me know.
@@nickchapsas Hm, thanks for reply. Should dig deeper into records features.
Hi Nick.
After watching this video, I looked at the source code of this library and I was not happy.
So a friend and I wrote a library, and then we wrote a second one that extends the first, but uses FluentValidation to perform the validations. We publish both libraries on Nuget. Both are open-source.
This is the repository of our project: github.com/ThiagoAcam/ValueOf
If you can, please give us an opinion.
Thanks
-273,15 C°
I see more and more projects bloated with tons of string wrappers.
I am very sorry but I won't ask my domains to handle exceptions during "implicit" convertions. In fact, in my humble opinion, domain and application layers should never deal with nulls and exceptions.
The domain throws exceptions it doesn't handle them. By definition they are domain specific exceptions. If you wanna get your ValueObjects to validate in a different way you are more than welcome to do it, but traditionally, this is how it works.
@@nickchapsas There is no real truth only (some) experience and opinion here. If we get a step back, values that need their parsing/conversion to be validated comes from an external source. In DDD, that source is responsable of that. If it's from UI or any datasource, external service, etc... the implementation layer should deal with that kind of issues. I tend to have my domain do only business logic. It deals only with Either (yes, functional) failures, domain models and abstracted infrastucture services. Thank you very much Nick for your great content and amazing quality of your videos.
2 minutes in and the plosives are everywhere, popping my ears. ffs, please stop.