How to Use Value Objects to Solve Primitive Obsession

Поділитися
Вставка
  • Опубліковано 6 жов 2024

КОМЕНТАРІ • 196

  • @MilanJovanovicTech
    @MilanJovanovicTech  2 роки тому +5

    Want to master Clean Architecture? Go here: bit.ly/3PupkOJ
    Want to unlock Modular Monoliths? Go here: bit.ly/3SXlzSt

  • @eddypartey1075
    @eddypartey1075 10 місяців тому +4

    What a pleasure to have such a brilliant developer and also teacher for .NET community!

  • @fieryscorpion
    @fieryscorpion 2 роки тому +17

    You're very talented and bound to be a great teacher. Loved the video and subbed instantly!

  • @EmptyGlass99
    @EmptyGlass99 Рік тому +2

    Your explanations are very comprehensive and logical.

  • @pureevil379
    @pureevil379 Рік тому +5

    Great video on this topic. I agree the complexity is increased and you have to weight up what you gain. Looking forward to the input validation video (going through them all).
    What I've done before for validation is a isValid method that lives within the member entity. If not valid it returned null and I checked for that.
    After watching your other video I'd use a result instead :)
    Thanks as always!

    • @MilanJovanovicTech
      @MilanJovanovicTech  Рік тому +1

      Check out FluentResult library if you don't want to write your own

  • @YuriWithowsky
    @YuriWithowsky 2 роки тому +4

    Excellent Milan! Please never stop! Your videos are very good

    • @MilanJovanovicTech
      @MilanJovanovicTech  2 роки тому +1

      Thank you Yuri. As long as there are awesome people like you supporting me, I will have a reason to create more videos :)

  • @Benke01
    @Benke01 10 місяців тому +2

    Good thing pointing out the negative aspects in the end. 👍 Everything are tradeoffs and not all patterns fit everyone and sometimes not even most ones.

  • @EldonElledge
    @EldonElledge Рік тому +2

    Great video. You are ver very good at explaining complex subjects in an easy to understand way. I also like how you explain the trade-offs for the different approaches.

  • @kinggrizzly13
    @kinggrizzly13 2 роки тому +1

    Great example. When I built my ValueObjects I also used added domain service functionality using an interface to be able to pull my constraints from a database, e.g. MaxLength, MinLength, IsRequired etc.

    • @MilanJovanovicTech
      @MilanJovanovicTech  2 роки тому

      Oh, so you were fetching constraints from the database? Why wouldn't you force you database constraints from the code?

    • @kinggrizzly13
      @kinggrizzly13 2 роки тому +2

      @@MilanJovanovicTech it was a business requirement. They wanted the ability to change those without us compiling the code, even though the chances of those values changing were small… either way it was a fun exercise to implement.
      Basically we created tables to store value object constraints and pulled from them. Hopefully that is a better explanation of the requirement.

  • @implicat
    @implicat 2 роки тому +2

    It would be great to add a link to the video you mentioned during your speech. For example "If you didn't watch my video about Result,..." and video appears in right top corner.
    It will increase the amount of views, I'm sure :)

    • @MilanJovanovicTech
      @MilanJovanovicTech  2 роки тому

      Still learning about that stuff. I have to purposefully remember to say that while recording a video. My newer videos are better in that regard.

  • @joshem32
    @joshem32 2 роки тому +2

    Excellent Milan, I really enjoy your videos, it's always nice to know about different views or different possibilities on how to structure your solution!!

    • @MilanJovanovicTech
      @MilanJovanovicTech  2 роки тому

      Thank you very much, Jose. Did you work with Value Objects before?

    • @joshem32
      @joshem32 2 роки тому

      @@MilanJovanovicTech not really, this is all new to me, maybe because I'm afraid of adding extra complexity to my projects, but at the end of the day it totally makes sense.

  • @adisilagy
    @adisilagy 2 роки тому +2

    Great video Millan!
    I just want to point out that if we wish to work with Dapper alongside EF, we should create SqlMapper for each of the value objects so Dapper will know how to map these properties

    • @MilanJovanovicTech
      @MilanJovanovicTech  2 роки тому

      Or maybe have a separate model for Dapper?

    • @adisilagy
      @adisilagy 2 роки тому +1

      @@MilanJovanovicTech What is the benefit of having a seperate model?
      As I View it, by adding a mapper for each value object you can you simply use the existing model entities.
      Then I use EF for commands and Dapper for queries with extension method on the DbContext

  • @joga_bonito_aro
    @joga_bonito_aro Рік тому +2

    Wouldn't a record type be a sufficient replacement for a ValueObject? You get type safety, immutability and equality out of the box and way shorter to type.
    Am I missing something?
    I see that it's already answered way down below

    • @MilanJovanovicTech
      @MilanJovanovicTech  Рік тому

      I've recently started using records, and honestly find them just fine for ValueObjects :)

  • @yazanshakhshir3049
    @yazanshakhshir3049 Рік тому +1

    Thanks for the great video as well Milan.
    I am a big big fan of your content and I really enjoy learning new stuff from you.
    I have a dilemma, now I know the basic approach which is using the primitive types, and currently, I just learned a new approach which is using the value objects.
    In the end, you recommended not to use this always and we have to weigh the pros and cons, which is really difficult tbh, that is, it is hard to tell when I should use this over that!
    so, I am telling you :D, it would be really great and helpful if you create a new video that illustrates and gives a real use case that when to choose either of the two approaches and how to weigh the pros and the cons.
    Thanks a lot again,
    keep up the great work.

  • @matesebestyen3142
    @matesebestyen3142 2 роки тому +1

    Great video Milan, I really like your work, especially because I've encountered many similar issues at my workplace - working at a startup as a freshly graduated Software Engineer.
    I actually prefer throwing exceptions when domain rules are broken - you save the added complexity on the caller side, and you have to write less code - of course, the tradeoff is losing the control of handling errors neatly (hate try-catch).
    Keep up the good work, you are making better software engineers for tomorrow!

    • @MilanJovanovicTech
      @MilanJovanovicTech  2 роки тому

      Thank you very much! I'm humbled :)
      Newtonsoft.Json can get by with a private constructor I believe?

    • @matesebestyen3142
      @matesebestyen3142 2 роки тому +1

      Looked it up and you are right! :)

  • @CousinAnthony
    @CousinAnthony 2 роки тому +1

    Without starting a debate on Guid vs int primary keys,,
    what about making the Entity class Entity where you would have the option to specify the Primary key type?
    I attempted this on my own, but I am getting a compile error in the Equals methods
    return other.Id == Id;
    return entity.Id == Id;
    Both these line generate the following error:
    Operator 'operator' cannot be applied to operands of type 'T' and 'T'

    • @MilanJovanovicTech
      @MilanJovanovicTech  2 роки тому

      That's a good option if you want to do strongly typed Ids. But I felt it would be too much for this video.
      I may make a separate video on that topic.
      What you can do is use create a marker interface for the EntityId, and implement it as a record.

  • @batressc
    @batressc 29 днів тому

    Just a little comment about the Value Objects and my own experience: Definitively ValueObjects it's the better approach when you developing an application using the DDD patterns, but if you are using Entity Framework you must be careful about how that Value Objects will be translated to a T-SQL or other SQL language (postgres, mysql, etc). By default, Entity Framework don't have a idea how can do that, you can apply some tricks (casting and creating implicit operators on the Value Object) but this is ok in scenarios were you have the full control how create the Linq sentences; if you use a control suite like a DevExpress or Telerik, on backstage the controls don't know how to handle that kind of objects and you can find some error on controls like DataGrid were cannot translate the linq sentences.

    • @MilanJovanovicTech
      @MilanJovanovicTech  28 днів тому

      We can pass primitive types to the external components, right?

  • @dimitricharles9784
    @dimitricharles9784 2 роки тому +1

    Very nice video, very well explained. I will use this technique to create new instances

    • @MilanJovanovicTech
      @MilanJovanovicTech  2 роки тому

      Awesome, I'm glad you liked it. Just don't overdo it! 😁

  • @TheMostafa5000
    @TheMostafa5000 Рік тому +1

    Thanks Milan for the video and I really got excited when I saw World Of Warcraft behind you 🤩

  • @makinibryan
    @makinibryan 11 місяців тому +1

    I get your point, but what if we have many properties that we need as value objects? Does it mean we go through route?

    • @MilanJovanovicTech
      @MilanJovanovicTech  11 місяців тому

      Depends on how much you want to complicate your domain, and if there's value in that

  • @MarvijoSoftware
    @MarvijoSoftware Рік тому +1

    Great video as always Milan. I advise devs to default to primitive types, with attribute constrains or using specific setter methods (as opposed to public property setters). This lead to better productivity. I believe that level of value objects should be used in niche cases

  • @isobato
    @isobato 2 роки тому +2

    I love your videos Milan! I did not catch if we made the ctor private. We could bypass the Create method and bypass the limitations we established, right? Love your channel, even thought I've subscribed I'm still manually checking for new videos on daily basis 🤣

    • @MilanJovanovicTech
      @MilanJovanovicTech  2 роки тому +1

      Thanks a lot Bratislave! 😁 Yes, I believe I made the ctor private, precisely for that reason.
      The release schedule is Tuesday-Friday usually.

    • @peymanebrahimi7756
      @peymanebrahimi7756 2 роки тому +1

      Same here. I check manually 2 or 3 times a day for new video.

  • @xBalaDeCanhaox
    @xBalaDeCanhaox 2 роки тому +1

    Great explanation of ValueObjects although it's really complex to implement. And this is just the FirstName, I'm wondering a bunch of fields to validate and create factory methods. lol

    • @MilanJovanovicTech
      @MilanJovanovicTech  2 роки тому

      It gets cumbersome, but when working in complex systems it's worth it to be honest.

  • @brechtlaitem
    @brechtlaitem 2 роки тому +8

    Why not using a record instead of a class to achieve immutability?

    • @MilanJovanovicTech
      @MilanJovanovicTech  2 роки тому +2

      Great question, Brecht! I don't recommend using records because it is very easy to break constraints with records.
      Records allow modifying the instance using the *with expression*, which calls the copy constructors.
      True, they solve immutability. But they fall short of the other important attributes.

    • @allannielsen4752
      @allannielsen4752 2 роки тому +3

      @@MilanJovanovicTech in my understanding, 'with' still calls the constructor and creates a new instance. It does not alter the original instance. This scenario is exactly what records are good at. I really like your static Create returning the result though.
      try the following:
      var bob = new FirstName("bob");
      var jane = bob with { Value = "jane", Mandatory = false };
      Console.WriteLine(bob);
      Console.WriteLine(jane);
      public record FirstName(string Value) : Names;
      public abstract record Names(int Length = 50, bool Mandatory = true);
      OUTPUT:
      FirstName { Length = 50, Mandatory = True, Value = bob }
      FirstName { Length = 50, Mandatory = False, Value = jane }

    • @MilanJovanovicTech
      @MilanJovanovicTech  2 роки тому +1

      @@allannielsen4752 Now try enforcing a maximum f 50 characters in the first name.
      You have to put that logic inside of the setter.
      Possibly throw an exception?
      And then you have to do that for each property that can be set.

    • @connorradeloff5597
      @connorradeloff5597 2 роки тому +1

      @@MilanJovanovicTech If you needed to enforce validation you could expose an function to set the value which performs the validation internally and returns some sort of Validation/Option result which contains the new instance of the mutated object, otherwise a Failure/Nothing. This way the caller knows if the operation succeeded or not, and we maintain immutability. If the setter on the property is private, you cannot use the with operator to set the value.

    • @9_vko
      @9_vko 3 місяці тому

      Great article))) Despite the fact that it's great approach of immutability. But imagine when your entity is getting bigger for instance could have 20 properties. In that case I should create ValueObject for of each of them. How to support this kind of code?

  • @rezvlt9285
    @rezvlt9285 Рік тому +1

    I wish my code was as clean as yours. That's why I'm watching ;)

    • @MilanJovanovicTech
      @MilanJovanovicTech  Рік тому +1

      You can do it! Just write it badly 100x times and then figure out how to write it better

  • @100kshooter5
    @100kshooter5 2 роки тому +3

    Awesome video, l was actually waiting for this notification, what do you think about having validation being the responsibility of something like FluentValidation in a DDD project, on the handlers?

    • @MilanJovanovicTech
      @MilanJovanovicTech  2 роки тому +8

      I try to split my validation into two parts:
      1. Input validation - which I solve in the Application layer using FluentValidation (coming out in a future video)
      2. Domain validation - which I usually implement by enforcing constraints using DDD principles.

    • @herve952
      @herve952 2 роки тому

      @@MilanJovanovicTech What's the point of having 2 validations in 2 different places, doesn't that introduce code duplication ? Moreover the application layer doesn't actually use those inputs so why should it care about it, in my opinion validation of its state belongs to the domain, doesn't it ?

    • @stjepankarin
      @stjepankarin 2 роки тому +4

      An example to the rescue, I hope… 😀
      Let’s say you have an API-endpoint expecting to get an e-mail address for whatever reason. Now, an input validation would ensure that incoming string is actually a valid e-mail address, like format-wise, ensuring the string is in a form of whatever current RFC standard rules. If format is wrong we would respond with status code 400. If format is ok, we would move on (hopefully creating an Email value object or something) to the business side.
      Domain rule, on the other hand, might say that only specific e-mail addresses are allowed, like from specific domains or from some hard-codes list, or maybe that only unique ones are allowed, or whatever. So you have an e-mail address, but not the “right” one. 😃
      Those rules are part of our business and needs to be handled differently, hence domain validation. If an e-mail address is invalid at this point you might for ex. throw a domain exception which would be handled slightly differently, responding with status code 403/409/whatever. If it is good one, we proceed and complete our case.
      As you see, there is different meaning/reason for validation, so keeping this separate will ensure we can more easily evolve the overall solution.

  • @Embriiii
    @Embriiii Рік тому +1

    What about a "protected string Value" to enforce our system to use only instances of value objects and preventing the use of the primitive Values? When it's not necessary to expose the Value of course

  • @zutafe
    @zutafe Рік тому +1

    It would be great if you can cover a niche topic on Strongly-typed Ids (guard types) that EF Core 7 has a built-in support (but still need to write more code).

    • @MilanJovanovicTech
      @MilanJovanovicTech  Рік тому

      What built-in support? I didn't hear 🤔

    • @zutafe
      @zutafe Рік тому

      @@MilanJovanovicTech I can 't post link here, you can check "Value generation for DDD guarded types" in EF Core documentation for details.

  • @eduugr
    @eduugr Рік тому

    Good stuff... old wolf getting up to date on software architecture here, well exemplified sir! Thanks

  • @Philippe42460
    @Philippe42460 Рік тому +1

    Hi Milan and thank you for this interesting video.
    Now I have a question: can the value object be replaced by a mere record? I think it could since a record already implements Iequatable. Thus all it need is making the validating logic you described... or maybe now there could be a more generic way to do it?...

    • @MilanJovanovicTech
      @MilanJovanovicTech  Рік тому +1

      Yes! And I've been using records in recent times for Value Objects

  • @wilfriedkinda
    @wilfriedkinda Рік тому +1

    Hi Milan, thank you for this instructive video as always,
    I was just wondering if we could replace Sealed FirstName class with a reord type!?

    • @MilanJovanovicTech
      @MilanJovanovicTech  Рік тому

      Yes, I've actually been exploring that recently and I think records are fine for value objects

  • @mahmoudalballah3387
    @mahmoudalballah3387 Рік тому +1

    Awesome but I think FirstName is not a good choice to explain value object with I think Address would be great example.

    • @MilanJovanovicTech
      @MilanJovanovicTech  Рік тому

      You may have a good point there, but keep in mind I was still a fresh content creator at the time 😅

  • @vivekkaushik9508
    @vivekkaushik9508 Рік тому +1

    Is there a reason why we should prefer Value Objects over Structs?

    • @MilanJovanovicTech
      @MilanJovanovicTech  Рік тому +1

      Structs always have a default ctor which breaks encapsulation, with a class I can impose stricter constraints

  • @evilTano
    @evilTano 2 роки тому +1

    Hi Milan, great video! But I've some questions:
    The Entity should ALWAYS accept a valid FirstName? So the check should be done outside of the Entity? Could this provide a lot of duplicate check in every CommandHandler? For example if I have two CommandHandler one for the Creation and one for the Update?
    Thanks.

    • @MilanJovanovicTech
      @MilanJovanovicTech  2 роки тому

      Indeed, duplication is a common theme here. In general, I don't mind it. But if you think it's problematic, you can try to think of a way to reduce duplication

  • @majormartintibor
    @majormartintibor 2 роки тому +1

    Thanks for this. I will refactor my code based on this.

    • @MilanJovanovicTech
      @MilanJovanovicTech  2 роки тому +1

      Important: Ask yourself if you really do need this complexity? If yes, go for it! 😁

    • @majormartintibor
      @majormartintibor 2 роки тому +1

      @@MilanJovanovicTech yes, yes, I know what you mean. I am a fan of keeping things simple for as long as it works fine. I will not blindly change all primitives, only those where it does make sense.

  • @ayalamac
    @ayalamac Рік тому +1

    Hey Milan, what do you think of ValueOf? and, How would you use this with FluentValidation? meaning, I'd like to have value objects for common properties among my entities and I'd have specific IValidators ... ?

    • @MilanJovanovicTech
      @MilanJovanovicTech  Рік тому

      I haven't worked with it, so I'm not very familiar to be honest

  • @Rev1211b
    @Rev1211b Рік тому

    I'm really enjoying this stuff. Nice work!

  • @aah134-K
    @aah134-K Рік тому +1

    I love to use value objects but the main issue is when mapped to database, usually they dont have ids which make them uneasy to use with entityframework

    • @MilanJovanovicTech
      @MilanJovanovicTech  Рік тому +1

      That is the point precisely, they are not supposed to have Ids! You want to map them to columns inside of the principal table.

    • @ppenxhchqlz3113
      @ppenxhchqlz3113 Рік тому

      They are properties of an entity basically, why would you want them to have ids?

    • @aah134-K
      @aah134-K Рік тому

      @@ppenxhchqlz3113 i dont but how to know where they fit in the system without ids?

  • @DeejayWazzouille
    @DeejayWazzouille 2 роки тому +1

    Hi Milan, great video !
    I wondering if there is a simple way to do the same but when your firstname max length can be different depends on the model.
    ex :
    class Person, firstname max lenght is 100
    class Company, firstname max length is 200
    regards,

    • @MilanJovanovicTech
      @MilanJovanovicTech  2 роки тому

      Create an abstract *NameWithMaxLength* ValueObject, and inherit from it for different lengths? 🤔

  • @JonathanPeel
    @JonathanPeel 2 роки тому +1

    I have heard of systems being described as "Stringly Typed"

  • @lukaskuchta1010
    @lukaskuchta1010 Рік тому +1

    Good job man! One question here. Why the ValueObject implementation is class and not struct? Or why is there IEquitable for class ? I seen somewhare that main beneffit of this is for struct types and no boxing - like with standard way of Equals. Thanks.

    • @MilanJovanovicTech
      @MilanJovanovicTech  Рік тому +1

      Structs don't have inheritance, so that's one thing. Records could be a really nice alternative.

    • @rebinmod
      @rebinmod 9 місяців тому

      @@MilanJovanovicTech But isn't mapping records to Ef core a problem by itself ?

  • @RoaringOrange
    @RoaringOrange 2 роки тому +2

    It is an obsession indeed! Chasing phantom issues...

    • @MilanJovanovicTech
      @MilanJovanovicTech  2 роки тому

      Why do you think this is the case?

    • @RoaringOrange
      @RoaringOrange 2 роки тому

      @@MilanJovanovicTech it complicates the code, rapes the memory with all the extra objects allocated/deallocates, messes with the model. Now you need to not only convert from/to user input to your obsession models, but also to/from database models. You will still have to expose normal types in api if you planning to have any, unless you want the consumers to be forced into this insanity. The idea is really cute though. And I use the word phantom, cos it still doesn't prevent the user from swapping first and last name. Your best bet - write a test and validate the output. That way you know that inside your code there are no issues like you described and you don't have to obsess over anything.

  • @JordiC354
    @JordiC354 8 місяців тому

    Hi Milan, often Currency is considered a value object, but if it allows crud operations (add new currency, update currency description), Is it still a value object? Aggregates, like BankAccount, could reference Currency object or just id (CurrencyId)?

    • @MilanJovanovicTech
      @MilanJovanovicTech  8 місяців тому

      I think you're confusing data management with behavior

  • @MrJonnis13
    @MrJonnis13 2 роки тому +1

    Hi Milan and thank you for the effort.
    If you don't throw here a validation exception for firstName, how are you going to signal the error in the presentation layer (controllers) to the UI/FE side, so that the end user knows that the member couldn't be created if we just log the error ?
    Maybe I am missing what the "Unit.Value" contains

    • @MilanJovanovicTech
      @MilanJovanovicTech  2 роки тому +1

      At this point in the series, there still wasn't any error propagation to the API. What I did in a later video is return the Result to the Controller level, and convert the error into a ProblemDetails

    • @MrJonnis13
      @MrJonnis13 2 роки тому +1

      @@MilanJovanovicTech I see. Thank you

  • @jesusantoniomartinezhernan2791
    @jesusantoniomartinezhernan2791 2 роки тому +1

    Hi Milan. I used to use a abstract class for value objects, but, i think that record type provide enough usefulness for value objects. What do you think about it???

    • @MilanJovanovicTech
      @MilanJovanovicTech  2 роки тому

      The problem with records is you can easily break invariants using the *with* expression.
      You can add guard clauses inside of the property setters, but that completely misses the point to be honest.

    • @jesusantoniomartinezhernan2791
      @jesusantoniomartinezhernan2791 2 роки тому +1

      @@MilanJovanovicTech i get it. Well, i think that with expression dont change a record instance, but it build a new instance with this change inside, so, a long short story, can preserve invariants. Well, for things that comparaision and equality, it's works.

    • @MilanJovanovicTech
      @MilanJovanovicTech  2 роки тому

      ​@@jesusantoniomartinezhernan2791 Yes, but you aren't preventing breaking the invariants of the new instance. Are you?

  • @prathameshshende4
    @prathameshshende4 Рік тому +1

    after all this error exception setting, do also need to set this in client side project like blazor? So it will be the double work, right? For this setting exceptions setting can we use FluentValidation instead of valueobject for each class and property?

  • @injypal
    @injypal 2 роки тому +1

    Please make video on aggregates and aggregate root

  • @David-rz4vc
    @David-rz4vc Рік тому +1

    Does this mean first name will be it's own table if we use entity framework?

  • @microtech2448
    @microtech2448 Рік тому +1

    It was small class with couple of properies,why we can't use data annotations to handle validations? So that we don't end up with thousands of classses in a project.

    • @MilanJovanovicTech
      @MilanJovanovicTech  Рік тому

      You missed the point

    • @microtech2448
      @microtech2448 Рік тому

      @@MilanJovanovicTech and what is that point? You mentioned in the end that it is one of the options available but still want to know if data annotations could be used for validation instead of value objects?

    • @MilanJovanovicTech
      @MilanJovanovicTech  Рік тому

      @@microtech2448 Do you want to pollute your domain objects with data annotations? How/when will you invoke validation?

    • @microtech2448
      @microtech2448 Рік тому

      @@MilanJovanovicTech I wouldn't want to fill my project with tons of value objects either. I just wanted to know if there is other way to validate properties, and I thought of data annotations as an option but may be that wouldn't work in this case

  • @TimurBabyuk
    @TimurBabyuk 2 роки тому +1

    For this purpose, OOP style is over complicated. For example, in F# with structural equality and discriminated out of the box all domain with these value objects and validation would fit into a dozen lines of code. By using bind, no need to throw exception on every data field validation. I'd recommend checking out Scott Wlaschin's "Domain modelling made functional" book and his numerous talks on UA-cam, including railway oriented programming

    • @MilanJovanovicTech
      @MilanJovanovicTech  2 роки тому +1

      Well F# is very different from C# in that regard, no wonder it's much less code.
      I really like ROP, and I intend to show it in some of the future videos!

  • @dilipmevada
    @dilipmevada 3 місяці тому

    Hi @Milan, In realtime example where Value object will be helpful?

    • @MilanJovanovicTech
      @MilanJovanovicTech  3 місяці тому

      They're used for domain modeling and expressing invariants/business rules

  • @Mefhisto1
    @Mefhisto1 2 роки тому +2

    Good stuff.

  • @jon-slem
    @jon-slem 3 місяці тому

    This maybe old, but would a record does the same thing relating to structure integrity and immutable?

    • @MilanJovanovicTech
      @MilanJovanovicTech  3 місяці тому

      Conceptually yes. But records have some downsides: www.milanjovanovic.tech/blog/value-objects-in-dotnet-ddd-fundamentals

  • @10199able
    @10199able 2 роки тому +2

    something something Domain Modeling Made Functional: Tackle Software Complexity with Domain-Driven Design and F#

    • @MilanJovanovicTech
      @MilanJovanovicTech  2 роки тому

      I haven't read the book yet. Do you recommend it?

    • @10199able
      @10199able 2 роки тому +1

      @@MilanJovanovicTech There is literally string50 type in it!

  • @maxpuissant2
    @maxpuissant2 2 роки тому +1

    I don't understand how are you going to rehydrate this value object, if constructor is private whitout setter or init, ef core is not going to be able to spawn this object?
    Also, would you use it for the Guid? If so ef core would need to be configured accordingly I guess.

    • @MilanJovanovicTech
      @MilanJovanovicTech  2 роки тому

      I configure a value converter behind the scenes. And EF can call private constructors without issues!
      I didn't understand your second question about Guid. What did you mean?

    • @maxpuissant2
      @maxpuissant2 2 роки тому

      @@MilanJovanovicTech do you use a value object to encapsulate the Guid type? If yes then is there configuration of EF needed? Thank you

  • @paulbarton2280
    @paulbarton2280 2 роки тому +1

    You do not implement implicit type conversion in your Value Objects to maintain argument clarity?

    • @MilanJovanovicTech
      @MilanJovanovicTech  2 роки тому

      Hey Paul! 😁 I sometimes implement the implicit conversion from Value Object to primitive type, but I didn't find it useful to show in this video. Do you think it would've been valuable to add?

    • @paulbarton2280
      @paulbarton2280 2 роки тому

      @@MilanJovanovicTech I generally use them, however I like how not implementing them enforced strongly typed static factory prams in your example. It left no way to mistakenly pass email instead of first name for example; Something that is difficult to unit test. In short, I guess mentioning them with pros and cons could have been useful.

  • @stunna4498
    @stunna4498 2 роки тому +1

    Do you normally use Guid for primary keys by default?

  • @justgame5508
    @justgame5508 Рік тому +1

    I built these with structs, it always annoyed me though that there was no way to 100% remove potential ambiguity from whatever uses the object. If you go with your approach then (in theory) someone can just pass null! so null check is still technically needed in the function and with the struct approach it can’t be null, but all structs have a default parameterless constructor so the validation can be ignored. Have not found a way around this yet and although it’s probably very unlikely someone passes null! to a function then is surprised at a null reference exception it still bothers me there is no way to make this compiler guaranteed to be safe

  • @vmamore
    @vmamore 2 роки тому +1

    Hey Milan, thanks for the video!
    Can you share the code of the project?

    • @MilanJovanovicTech
      @MilanJovanovicTech  2 роки тому

      Hey, you can get access to the source code by supporting me on Patreon. However, feel free to reach out to me on LinkedIn and I will give it to you for free this time :)

  • @syedib
    @syedib 2 роки тому +1

    Why dont we create Name ValueObject and encapsulate firstname and lastname. do you think still we have chance to pass Name.Lastname instead of Name.Firstname ?

    • @MilanJovanovicTech
      @MilanJovanovicTech  2 роки тому

      That's also a good option, assuming we want the same rules.
      You could also implement Name with all the business logic, and then just create FirstName and LastName which inherit from Namw for the strong typing.

  • @serjioto88
    @serjioto88 День тому

    sorry how to move the solution explorer to the left?

  • @pigrebanto
    @pigrebanto Місяць тому

    Milan you are a great guy. But let me tell you. Here we are saying that those initial 3 lines of code are not safe (?), not robust (?), not elegant (?) thus I start to write more than 100 LOC quite complex to read and write to justify the greateness of my code. Do you think companies are willing to pay that? How much time do I need to spend? What about an alternative solution using maybe no more than 10 LOC instead? I think software engineering is partially theoretical (good for courses, teaching and video) and partially (the most important part) is to solve real problem in production with still good enough code. Sorry just my 2 cents.

    • @MilanJovanovicTech
      @MilanJovanovicTech  Місяць тому +1

      Sadly, the value behind most of the DDD ideas are missed because devs can't see the value in simple examples. And if you use complex examples, you can't explain the concept in a simple way. Go figure.

  • @Skillamu
    @Skillamu Рік тому +1

    Great video!

  • @alexisfibonacci
    @alexisfibonacci 2 роки тому +1

    For the functionality around equality and related matters, will you have benefitted from just making the base ValueObject class a record?

  • @JANDRESCR
    @JANDRESCR 8 місяців тому

    can we use records instead of inherits fron ValueObject class?

  • @aseneda
    @aseneda 2 роки тому +1

    Hey! Excellent content Milan. How about to use Struct for comparison needs than Class for Value Objects?
    Keep going.
    Regards

    • @MilanJovanovicTech
      @MilanJovanovicTech  2 роки тому +2

      You won't have inheritance. That will prevent you from doing generics with ValueObject as the base class, which can be very useful.

    • @aseneda
      @aseneda 2 роки тому

      I got your point.
      Are there some situations where you don’t need inheritance (and a property Id)? Date, e-mail or currency

  • @oscartolaba8093
    @oscartolaba8093 Рік тому +1

    Whats the shortcut for search method?

  • @mahmoudalballah3387
    @mahmoudalballah3387 Рік тому +1

    Where can I find this project?

  • @XMaverick20
    @XMaverick20 Рік тому +1

    Is anyone using this in production code?

  • @LordErnie
    @LordErnie Рік тому +1

    If you want value objects, create structs, ref strucs, or record structs. It hurts to see you make abstract and complicated code for something that could be taken care of in a much cleaner way.

    • @MilanJovanovicTech
      @MilanJovanovicTech  Рік тому

      There are some nuances there

    • @LordErnie
      @LordErnie Рік тому

      @@MilanJovanovicTech Could you elaborate on that? I would love to know what the pros and cons would be.

    • @MilanJovanovicTech
      @MilanJovanovicTech  Рік тому

      @@LordErnie This is good reading on the topic:
      enterprisecraftsmanship.com/posts/net-value-type-ddd-value-object/

    • @LordErnie
      @LordErnie Рік тому

      @Milan Jovanović An interesting read for sure. However, it does state something that is in a way untrue due to irrelevance. It states that structs can not have custom constructors, which is true as far as I'm aware. But later on, it says that this is a bad thing because you want an exception to be thrown whenever an object is initialized in a non correct way, and that structs can not achieve this in a proper way. This is false. C# had included the required keyword, which forces an object to have any fields or props with set keyword to be assigned during either construction or later via the object initializer.
      In the article it never gives any other reason that validates the leaving out of structs as DDD value objects enough to actually consider it. Am I missing something here?

  • @somyarathee
    @somyarathee 7 місяців тому

    how to do a mny to many where one of the classes is a value object in .net6?

    • @MilanJovanovicTech
      @MilanJovanovicTech  7 місяців тому

      Check the EF docs

    • @somyarathee
      @somyarathee 7 місяців тому

      Hi, I am getting this error, I defined the Id as auto generated primary key , when i run the seeding i get this issue:
      The seed entity for entity type 'Article' cannot be added because a non-zero value is required for property 'Id'. Consider providing a negative value to avoid collisions with non-seed data.
      @@MilanJovanovicTech

  • @rcarubbi
    @rcarubbi 3 місяці тому

    why not create the ValueObjects as Records?

    • @MilanJovanovicTech
      @MilanJovanovicTech  3 місяці тому +1

      It'll look similar if you need to add a private constructor. Another issue with using records is avoiding value object invariants using the with expression.

    • @rcarubbi
      @rcarubbi 3 місяці тому

      @@MilanJovanovicTech thanks for replying Milan! Your content is excellent! I'm addicted to your playlist.

    • @rcarubbi
      @rcarubbi 3 місяці тому

      @@MilanJovanovicTech I got it. So the consumer would basically be able to bypass the validation logic in the fabric method applying a change using the with expression. That's a very valid point!

  • @vitahvitah5823
    @vitahvitah5823 2 роки тому +1

    I would like to watch a code source...

  • @m.waheedanwar7105
    @m.waheedanwar7105 4 місяці тому

    You are best

  • @guard13007
    @guard13007 2 роки тому +1

    Setting a 50 character limit on a name is barbaric. :\

  • @darkogele
    @darkogele 2 роки тому +2

    So much code for one string hmmm don't like it boyy 🤣🤣

    • @MilanJovanovicTech
      @MilanJovanovicTech  2 роки тому +1

      Well if it is "just a string" then there is no point to bother with this.
      If the string actually represents something complex, then it's an entirely different matter.

  • @HenrryPires
    @HenrryPires Рік тому +1

    I find this way to work horrible. You end up with hundreds of classes. Memory allocation is so high.
    The constrains are also horrible, you have logic spread everywhere, if you need a extra validation depending on the context it's just impossible. Please follow the KISS principle
    what prevents you from by mistake doing this?
    var firstName = FirstName.Create(request.LastName)
    Don't get me wrong, i like your videos, i just think this specific one spread a VERY bad idea of how to do your code. For the fist time i have to leave a dislike on one of your videos

    • @MilanJovanovicTech
      @MilanJovanovicTech  Рік тому

      Hopefully we catch silly mistakes like that in development, PR reviews, testing. It's not a problem of value objects. You can make the same mistake assigning to a property, or passing that value to the constructor.

    • @HenrryPires
      @HenrryPires Рік тому

      ​@@MilanJovanovicTech that's my point, value objects do not prevent that kind of mistakes and introduce an extra layer of complexity, with no real gain

  • @adambickford8720
    @adambickford8720 2 роки тому +1

    This would be trivial in java Spring. A few annotations and it does all the data binding/validation for you:
    @Value
    class User {
    @NotNull
    @Size(min = 1, max = 50, message = "First name must be between 1 and 50 character")
    String firstName;
    @NotNull
    @Size(min = 1, max = 50, message = "Last name must be between 1 and 50 character")
    String lastName;
    }
    Fully immutable, encapsulated, value-based equality, etc. like you're achieving with all the wrapper types in a few annotations. You also get a sane dev experience where you can just concat/arithmetic/logic values w/o having to unwrap everything first. In the case where you hand craft a user you can just pass the object off to a `Validator` and w/1 line get all of your constraint errors (which can be in a constructor, factory, etc). It really is that easy!
    I get the type-safety argument; the juice just isn't worth the squeeze. Type safety is proving its well-formed but what you really need is proof its correct. Your tests should be verifying you didn't call `getFirstName()` when it should have been `getLastName()`, at a fraction of the cost! We're way beyond the 80/20 point IMO.

    • @MilanJovanovicTech
      @MilanJovanovicTech  2 роки тому

      I tend to heavily avoid annotations (attributes in C#) in my Domain layer. They obscure domain logic. I would rather make it explicit with a little bit more work. It's a tradeoff that is worth it to me.

  • @haruundk
    @haruundk Рік тому +1

    How would a configuration look like in Entity Framework?
    I've tried with:
    builder.OwnsOne(p => p.FirstName, opt => opt.Property(p => p.Value).HasColumnName("FirstName").IsRequired() );
    I am not able to do _dbContext.Users.FirstOrDefaultAsync(e => e.FirstName ==Firstname) i have to do _dbContext.Users.FirstOrDefaultAsync(e => e.FirstName.Value ==Firstname.Value) for it to work?
    Do i have to do it the last way? meaning select .Value every time? :/

    • @MilanJovanovicTech
      @MilanJovanovicTech  Рік тому

      Unfortunately that's how you have to do it if you want to use value objects :/