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!
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 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.
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
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
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!
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.
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.
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?
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.
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.
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
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?
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 :)
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 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); } }
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.
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. 😁
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 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.
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?
@@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.
@@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
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)
@@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?
Want to master Clean Architecture? Go here: bit.ly/3PupkOJ
Want to unlock Modular Monoliths? Go here: bit.ly/3SXlzSt
If I "stay awesome", it's because I keep coming back for more Milan. Awesome... as ever.
Thanks a lot! :)
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!
That works from the outside, although it's not a proper builder unless you build in the last step.
Thanks 🤗
Thanks a lot! And I'm glad you enjoyed the video :)
More videos on real life design patterns like this please :)
Will do! :)
A useful video right there. Also glad you mentioned their use cases because that seems like a lot!
Glad it was helpful!
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!
Glad you liked this. Experimenting with a new video format, so good to hear some feedback
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.
Very cool. Do you use it for entity creation or some different objects?
@@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.
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
That is an awesome library! 😁
@@MilanJovanovicTech Glad you found it useful! The first creator to make a video about it could get a lot of views 😉.
Great content, would like to see more like this, design patterns in practice 👌
More to come!
Thanks Milan, great content as always!
Much appreciated!
this helped a LOT, thank you!!
Glad it helped!
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
That's an excellent extension on top of this implementation
You’re such a baller! Thanks, bro!
Happy to help!
Thanks man. You're great
You're welcome!
Nice video👍🏻 I love this pattern as a consumer
That's who it is meant for 😁
Very very nice video. Tips here are things I will implement immediately in my code. Thank u!
Glad it was helpful!
Great explanation with example !!
Glad you liked it!
Question: what’s the point of Empty()? Build() is creating a new object at the end.
Returning the initial empty builder instance
@@MilanJovanovicTech why not just use new () instead?
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!
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.
god god you out control love you love you love you
Thanks 😅
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.
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.
Interfaces can be used here too to indicate the next available steps. I’ve seen another channel explaining that.
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?
What do you mean by: "automate the creation of the builder from a DTO"
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.
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.
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.
@@MilanJovanovicTech That makes sense... I was thinking along these same lines.
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
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.
@@MilanJovanovicTech Thank you sir!
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
Fair enough
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?
Will do some research, it's probably either a UUID (Guid) or some sort of timestamp based random ID like snowflakes or ULID
Is the address sub-item of the order and the address maintained by the customer the same table?
That's a persistence decision
For example, in the order details table, it will not be too complicated to have too many fields
What do you mean?
@@MilanJovanovicTech If there are too many fields, will the use of builds be too large and not conducive to writing and maintenance?
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 :)
Is it only getters/setters? Probably overkill for that. If there is some logic inside, then it makes sense.
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
How would it help?
@@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);
}
}
What if I have rich domain model and my business rules are validated inside the entity?
Then you'll probably control the creation from the domain model?
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.
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. 😁
Nice and fancy! But with double overhead...
True! But use the idea, not the example.
Why does the empty method is static ?
Just to start off the builder.
Or you could do
new OrderBuilder()
....
we need big tutorial for blazor and whats your opinion in it ?
I don't use it, but I like the tech
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.
What if you're deserializing?
@@MilanJovanovicTechDepends on your serializer of course, but STJ supports required properties
@@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.
What do you think about the importance of using immutability in builder patterns?
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?
@@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.
Wait, can you edit the code while it is RUNNING?
Yeah, with hot reload
Nice one, but don't know where I should use it
I shared a few use cases
Wow clappings।
Thanks!
Interesting, but it's not functional, as it contains an internal state
Is there a way to build a builder without holding state?
@@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
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)
@@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?
@@TroyCSchmidt Having a fluent syntax does not mean being functional
Мда я это проходил в универе в 2011 году ))
Yes. it's a classic design pattern