Master the Fluent Builder Design Pattern in C#

แชร์
ฝัง
  • เผยแพร่เมื่อ 25 ธ.ค. 2024

ความคิดเห็น • 106

  • @MilanJovanovicTech
    @MilanJovanovicTech  6 หลายเดือนก่อน +5

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

  • @tattooineste
    @tattooineste 6 หลายเดือนก่อน +5

    If I "stay awesome", it's because I keep coming back for more Milan. Awesome... as ever.

  • @awright18
    @awright18 6 หลายเดือนก่อน +3

    I really like fluent builders. I typically have a private field of the type that is getting built. Then each fluent method sets it's properties with the build method returning that member, but this implementation works, too. Nice video!

    • @MilanJovanovicTech
      @MilanJovanovicTech  6 หลายเดือนก่อน +1

      That works from the outside, although it's not a proper builder unless you build in the last step.

  • @joergw
    @joergw 6 หลายเดือนก่อน +1

    Thanks 🤗

    • @MilanJovanovicTech
      @MilanJovanovicTech  6 หลายเดือนก่อน

      Thanks a lot! And I'm glad you enjoyed the video :)

  • @bartlomiejuminski
    @bartlomiejuminski 6 หลายเดือนก่อน +5

    More videos on real life design patterns like this please :)

  • @ugochukwuumerie6378
    @ugochukwuumerie6378 6 หลายเดือนก่อน +3

    A useful video right there. Also glad you mentioned their use cases because that seems like a lot!

  • @TroyCSchmidt
    @TroyCSchmidt 6 หลายเดือนก่อน +1

    Great tutorial that gets straight to the point and jets me know if i want to watch or not. Definitely great video flow and great content as well!

    • @MilanJovanovicTech
      @MilanJovanovicTech  6 หลายเดือนก่อน

      Glad you liked this. Experimenting with a new video format, so good to hear some feedback

  • @xNeoGenesis
    @xNeoGenesis 6 หลายเดือนก่อน +1

    Great video as always: useful, insightful, concise and straight to the point. Some months ago I started using this pattern and I really like how easy and seamless it can make instances creation and I found your tip to use Actions in methods really useful to improve what I've worked on.

    • @MilanJovanovicTech
      @MilanJovanovicTech  6 หลายเดือนก่อน

      Very cool. Do you use it for entity creation or some different objects?

    • @xNeoGenesis
      @xNeoGenesis 6 หลายเดือนก่อน +1

      @@MilanJovanovicTech Different objects, I've not had the need to use it for entity creation yet. Specifically, I made a library intended to make all kinds of Http requests and I use it to configure them, like the url, headers, cookies, method, download location if needed, post body, number of retries, etc. This allows it to be very flexible and extensible.
      Regarding entities, your DDD videos have been very useful recently to me to implement support for an external API with many entities and complex relations. As an amateur and hobbyist programmer I've been learning a lot with your content.

  • @m31coding
    @m31coding 6 หลายเดือนก่อน

    Thank you very much for this awesome video, Milan! I’ve added your example to the M31.FluentAPI library, implementing it twice: once with an arbitrary call order for the builder methods and once with a forced order. The library uses source code generation to create the builder boilerplate.
    Happy coding!
    Kevin

    • @MilanJovanovicTech
      @MilanJovanovicTech  6 หลายเดือนก่อน

      That is an awesome library! 😁

    • @m31coding
      @m31coding 6 หลายเดือนก่อน

      @@MilanJovanovicTech Glad you found it useful! The first creator to make a video about it could get a lot of views 😉.

  • @mylesdavies9476
    @mylesdavies9476 6 หลายเดือนก่อน +1

    Great content, would like to see more like this, design patterns in practice 👌

  • @pavfrang
    @pavfrang 6 หลายเดือนก่อน

    Thanks Milan, great content as always!

  • @cuIo_
    @cuIo_ 5 หลายเดือนก่อน

    this helped a LOT, thank you!!

  • @danko_v3
    @danko_v3 6 หลายเดือนก่อน

    Builder pattern is really cool. There is interesting approach called stepwise builder, which also can be implemented with fluent api. This helps you provide validation where for example one property should be validated based on another property value or force developers specify values in a specific order

    • @MilanJovanovicTech
      @MilanJovanovicTech  6 หลายเดือนก่อน

      That's an excellent extension on top of this implementation

  • @EHBRod13
    @EHBRod13 5 หลายเดือนก่อน

    You’re such a baller! Thanks, bro!

  • @empathetic24671
    @empathetic24671 4 หลายเดือนก่อน

    Thanks man. You're great

  • @PhilipDanielHayton
    @PhilipDanielHayton 6 หลายเดือนก่อน

    Nice video👍🏻 I love this pattern as a consumer

  • @Brendan2Alexander
    @Brendan2Alexander 6 หลายเดือนก่อน

    Very very nice video. Tips here are things I will implement immediately in my code. Thank u!

  • @ajaysingh-sc1qt
    @ajaysingh-sc1qt 6 หลายเดือนก่อน

    Great explanation with example !!

  • @samwalker5438
    @samwalker5438 6 หลายเดือนก่อน +3

    Question: what’s the point of Empty()? Build() is creating a new object at the end.

    • @MilanJovanovicTech
      @MilanJovanovicTech  6 หลายเดือนก่อน

      Returning the initial empty builder instance

    • @timur2887
      @timur2887 6 หลายเดือนก่อน +3

      @@MilanJovanovicTech why not just use new () instead?

  • @christianmarpert3844
    @christianmarpert3844 5 หลายเดือนก่อน

    thks! great video and explanation! Still got one question, when using records, you could use the With syntax , along with Empty(), also getting immutability ... not sure if it is rather a preference (as this approach comes with ot of boilerplate) or has more advantages...
    Anyway, well explained, will add it to my tool box!

    • @MilanJovanovicTech
      @MilanJovanovicTech  5 หลายเดือนก่อน +1

      The boilerplate also acts as a safeguard in what you can do. Using the records with expression you can do anything, and possibly something you shouldn't be allowed to. So there's a slight difference.

  • @intermixhector2902
    @intermixhector2902 6 หลายเดือนก่อน

    god god you out control love you love you love you

  • @shadowsir
    @shadowsir 6 หลายเดือนก่อน +1

    You can go a little bit further still:
    If you have mandatory fields, you can create a "steps" builder. Suppose orderId is mandatory, and you don't want to set it up so the builder can generate one at random (trivial example to show what I mean):
    public class OrderBuilder
    {
    public OrderBuilderStep1 WithOrderId(string orderId) {
    return new OrderBuilderStep1(orderId);
    }
    public class OrderBuilderStep1(string orderId) {
    private string _addressLine1 = "";
    public OrderBuilderStep1 WithAddressLine1(string addressLine1)
    {
    _addressLine1 = addressLine1
    }
    public Order Build()
    {
    return new Order { OrderId = orderId, AddressLine1 = _addressLine1 };
    }
    }
    }
    This also makes it possible to create even more complex scenario's where, depending on the input, you can return a different next step.

    • @MilanJovanovicTech
      @MilanJovanovicTech  6 หลายเดือนก่อน +2

      The steps builder is a beautiful approach! Didn't want to add too much in one video, but I might cover that in part 2.

    • @samwalker5438
      @samwalker5438 6 หลายเดือนก่อน

      Interfaces can be used here too to indicate the next available steps. I’ve seen another channel explaining that.

  • @simoneesposito8447
    @simoneesposito8447 6 หลายเดือนก่อน

    Thank you very much for the great video!
    Could you tell me if there is a way to automate the creation of the builder from a DTO?
    Also, is it recommended to use FluentValidation to validate the object during the Building phase?

    • @MilanJovanovicTech
      @MilanJovanovicTech  6 หลายเดือนก่อน

      What do you mean by: "automate the creation of the builder from a DTO"

  • @pedrosilva1437
    @pedrosilva1437 6 หลายเดือนก่อน

    Very nice video! One question: how to do you recommend doing data validation in the builder pattern? For example, let's say the Address must have a Street property, but the user doesn't call the WithStreet method in the AddressBuilder. Would you check the required parameters in the Build method call and fail if the street member hasn't been set? And what about the format of the data, like the zip code format... would you validate that in the WithZipCode method, or with constraints validation in the Address constructor? Thanks.

    • @andreibicu5592
      @andreibicu5592 6 หลายเดือนก่อน +1

      I am also interested to know that. I'm thinking to combine it with the Result pattern, but don't know yet if it's a good approach.

    • @MilanJovanovicTech
      @MilanJovanovicTech  6 หลายเดือนก่อน

      You can validate the format of fields as soon as you call the WithX method. And a great place to do validation is in the Build method. I would probably opt for throwing exceptions in the WithX method - or return a Result in the Build step.

    • @pedrosilva1437
      @pedrosilva1437 6 หลายเดือนก่อน

      @@MilanJovanovicTech That makes sense... I was thinking along these same lines.

  • @Kupnu4000
    @Kupnu4000 6 หลายเดือนก่อน +1

    The video is very useful, thank you! One minor thing - fully white screens are pretty painful for the eyes, especially when they go after the dark ones

    • @MilanJovanovicTech
      @MilanJovanovicTech  6 หลายเดือนก่อน +1

      Sorry about that! It wasn't entirely white, instead it's a shade of gray. I'll see how I can make this easier on the eyes in future videos.

    • @Kupnu4000
      @Kupnu4000 6 หลายเดือนก่อน

      @@MilanJovanovicTech Thank you sir!

  • @FarukLuki111
    @FarukLuki111 หลายเดือนก่อน

    Thx for this! I really got the idea but I do not see „enough“ PROs compared to CONRTA of writing much more code! I do not see me using this pattern

  • @NahomKW
    @NahomKW 6 หลายเดือนก่อน

    Hey Milan, I've always wondered about the type of unique identifier big companies like Netflix, Amazon, etc., use and how they utilize these identifiers for querying entities. Could you shed some light on that as well?

    • @MilanJovanovicTech
      @MilanJovanovicTech  6 หลายเดือนก่อน

      Will do some research, it's probably either a UUID (Guid) or some sort of timestamp based random ID like snowflakes or ULID

  • @sunzhang-d9v
    @sunzhang-d9v 6 หลายเดือนก่อน

    Is the address sub-item of the order and the address maintained by the customer the same table?

  • @sunzhang-d9v
    @sunzhang-d9v 6 หลายเดือนก่อน

    For example, in the order details table, it will not be too complicated to have too many fields

    • @MilanJovanovicTech
      @MilanJovanovicTech  6 หลายเดือนก่อน

      What do you mean?

    • @sunzhang-d9v
      @sunzhang-d9v 6 หลายเดือนก่อน +2

      @@MilanJovanovicTech If there are too many fields, will the use of builds be too large and not conducive to writing and maintenance?

  • @nucasspro1
    @nucasspro1 6 หลายเดือนก่อน

    thanks, I use this pattern when the class has a lot of properties and it hard see the code when creating an instance, to make the code readable. But not sure it is a good choice or not :)

    • @MilanJovanovicTech
      @MilanJovanovicTech  6 หลายเดือนก่อน

      Is it only getters/setters? Probably overkill for that. If there is some logic inside, then it makes sense.

  • @sujitbhandarkar886
    @sujitbhandarkar886 6 หลายเดือนก่อน

    So what i think real use could be instead of extension method we can go for this builder pattern where same object reference is shared while in extension method not

    • @MilanJovanovicTech
      @MilanJovanovicTech  6 หลายเดือนก่อน

      How would it help?

    • @sujitbhandarkar886
      @sujitbhandarkar886 6 หลายเดือนก่อน

      @@MilanJovanovicTech hi, sorry my bad I thought the a different object reference or instance is passed on every extension method call but that's not the case with the class type which I performed a poc and checked. Below is the code. Thanks !
      MyClass myClass = new(5);
      Console.WriteLine("Hash Code before " + RuntimeHelpers.GetHashCode(myClass) + "
      ");
      var myClassResult = myClass.AddOne().AddDouble();
      Console.WriteLine("
      Hash Code after Result " + RuntimeHelpers.GetHashCode(myClassResult));
      Console.WriteLine(myClass.IsSameInstance(myClassResult));
      Console.ReadKey();
      public class MyClass
      {
      public int Value { get; set; }
      public MyClass(int value)
      {
      Value = value;
      }
      }
      public static class MyClassExtension
      {
      public static MyClass AddOne(this MyClass myClass)
      {
      Console.WriteLine("Hash Code before" + RuntimeHelpers.GetHashCode(myClass));
      myClass.Value += 1;
      Console.WriteLine("Hash Code After" + RuntimeHelpers.GetHashCode(myClass));
      return myClass;
      }
      public static MyClass AddDouble(this MyClass myClass)
      {
      Console.WriteLine("Hash Code before" + RuntimeHelpers.GetHashCode(myClass));
      myClass.Value *= 5;
      Console.WriteLine("Hash Code After" + RuntimeHelpers.GetHashCode(myClass));
      return myClass;
      }
      public static bool IsSameInstance(this MyClass instance, MyClass other)
      {
      return ReferenceEquals(instance, other);
      }
      }

  • @PhobiaQQ
    @PhobiaQQ 6 หลายเดือนก่อน +1

    What if I have rich domain model and my business rules are validated inside the entity?

    • @MilanJovanovicTech
      @MilanJovanovicTech  6 หลายเดือนก่อน

      Then you'll probably control the creation from the domain model?

  • @marcociaschini4616
    @marcociaschini4616 6 หลายเดือนก่อน +3

    I don't agree on this kind of use of the fluent builder pattern. Apart from the benefit of the fluent style, this is just an over complicated constructor. The main target of a constructor is to cleary define all mandatory dependencies of an object, and this approach seems to transform everything into optionals.
    In my opinion, the context where fluent builder really shines, is when it implements a kind of buidling workflow for complex object, where you have to plugin and configure various components at different steps of buidling flow.

    • @MilanJovanovicTech
      @MilanJovanovicTech  6 หลายเดือนก่อน

      Right, and showing this with a complex example would make the video too complicated. This is why I choose to showcase simple examples and discuss the concepts behind the pattern. You're welcome to make an improved version. 😁

  • @timur2887
    @timur2887 6 หลายเดือนก่อน

    Nice and fancy! But with double overhead...

    • @MilanJovanovicTech
      @MilanJovanovicTech  6 หลายเดือนก่อน

      True! But use the idea, not the example.

  • @alexmel8448
    @alexmel8448 5 หลายเดือนก่อน

    Why does the empty method is static ?

    • @MilanJovanovicTech
      @MilanJovanovicTech  5 หลายเดือนก่อน +1

      Just to start off the builder.
      Or you could do
      new OrderBuilder()
      ....

  • @shabanelmogy7912
    @shabanelmogy7912 6 หลายเดือนก่อน

    we need big tutorial for blazor and whats your opinion in it ?

    • @MilanJovanovicTech
      @MilanJovanovicTech  6 หลายเดือนก่อน +1

      I don't use it, but I like the tech

  • @Rick-mf3gh
    @Rick-mf3gh 6 หลายเดือนก่อน +1

    Nice clean and easy to follow video. 👍
    I can see how this would be good if there are only optional properties, like in the AuthenticationBuilder.
    But for POCOs, I find required+init to be less prone to mistakes, as the compiler tells you if you forget to set a mandatory property.
    e.g. public required string Name { get; init; }
    I prefer knowing that if (e.g.) I add an additional mandatory property then the code will not build unitl I have updated all the places I construct the class.

    • @MilanJovanovicTech
      @MilanJovanovicTech  6 หลายเดือนก่อน

      What if you're deserializing?

    • @modernkennnern
      @modernkennnern 6 หลายเดือนก่อน +1

      ​​@@MilanJovanovicTechDepends on your serializer of course, but STJ supports required properties

    • @Rick-mf3gh
      @Rick-mf3gh 6 หลายเดือนก่อน

      @@MilanJovanovicTech I have not experienced any issues using 'required' nor 'init' when using either Newtonsoft or System.Text.Json.
      IIRC, both 'required' and 'init' are compiler-enforced constraints that have no effect during runtime.

  • @ajdinhusic2574
    @ajdinhusic2574 4 หลายเดือนก่อน

    What do you think about the importance of using immutability in builder patterns?

    • @MilanJovanovicTech
      @MilanJovanovicTech  4 หลายเดือนก่อน

      Making each operation immutable? I think it's enough that we encapsulate state within the builder and only allow it to be modified through the builder methods. Returning a new builder instance for each operation would eat up a lot of memory. Did you have a different opinion on this?

    • @ajdinhusic2574
      @ajdinhusic2574 4 หลายเดือนก่อน

      @@MilanJovanovicTech Yes, you're correct that it depends on the use case of course.
      We used to have some bugs in the past because we used a builder pattern that mutates its internal state, but the builder had to reuse the configuration three times and then return three separate builder results, with slight changes.

  • @eduardoandrescastilloperer4810
    @eduardoandrescastilloperer4810 6 หลายเดือนก่อน

    Wait, can you edit the code while it is RUNNING?

  • @rahulsheelavantar2110
    @rahulsheelavantar2110 6 หลายเดือนก่อน

    Nice one, but don't know where I should use it

  • @konarksharma6524
    @konarksharma6524 2 หลายเดือนก่อน

    Wow clappings।

  • @emfi8744
    @emfi8744 6 หลายเดือนก่อน +1

    Interesting, but it's not functional, as it contains an internal state

    • @MilanJovanovicTech
      @MilanJovanovicTech  6 หลายเดือนก่อน

      Is there a way to build a builder without holding state?

    • @emfi8744
      @emfi8744 6 หลายเดือนก่อน

      @@MilanJovanovicTech Right, I was not clear: the builder you implemented is changing its internal state.
      To be functional, you should return a modified copy of the builder.
      For example, IServiceCollection follows your pattern, so I can write:
      services.AddTransient(x);
      services.AddTransient(y);
      And still services will have both dependendies.
      But let's not call it functional

    • @TroyCSchmidt
      @TroyCSchmidt 6 หลายเดือนก่อน

      So what would you change to be functional then? I'm not sure I'm following your point cause it still seems functional because it is composable by allowing the daisy chaining just like if you called services.AddTransient(x).Add transient(y)

    • @emfi8744
      @emfi8744 6 หลายเดือนก่อน

      @@TroyCSchmidt Every time you invoke WithXxx(""), you should return a new instance of the builder, with the updated value.
      Functional comes with immutability. As it is implemented now, if you pass it to a method, the method may change your builder.
      It's the same difference you have between List and IEnumerable. When you add an element, the List is modified, the IEnumerable returns a new instance of IEnumerable.
      Guess who invented Linq?

    • @emfi8744
      @emfi8744 6 หลายเดือนก่อน +1

      @@TroyCSchmidt Having a fluent syntax does not mean being functional

  • @orhanaliyev9774
    @orhanaliyev9774 6 หลายเดือนก่อน

    Мда я это проходил в универе в 2011 году ))