A step-by-step guide to developing a Fluent API from scratch in .NET C# using the Builder Design Pattern. I am sure this is not the first time for you to hear about the . However, I promise you that you would find something different in this article. Builder Design Pattern In this article, we would walk through the whole process of developing a using the , from the early steps of thinking about it, to the latest of testing it. Fluent API Builder Design Pattern Therefore, buckle your seat belts, and let’s begin our trip. What is the Builder Design Pattern? It is a which allows creating complex objects into small simple steps one by one. creational design pattern What are the advantages of the Builder Design Pattern? Some of the well-known advantages of the Builder Design Pattern are: It helps breakdown the process of creating complex objects into small chunks which become more controllable. It enables using a which the end user can relate to. Domain Specific Language (DSL) It helps move from a general definition to a more specific granular definition of the object we are building. What are the disadvantages of the Builder Design Pattern? Mostly, it adds extra layer of complexity to the code which you would notice at the end of the implementation in this article. How to implement the Builder Design Pattern? Yes, this is the question I know you are interested into. However, this time we would not just jump into the code implementation. We would go through the whole process starting from the early stages of design. Therefore, let’s start. The Example First, let’s come up with an example to use through or trip. I chose a simple example of a registration process of a school. In simple words, at some point in the whole solution, you will need to define some teachers and some students. Let’s assume that these Teacher and Student objects are so complex that we need to develop a Fluent API to create them. Disclaimer Some best practices would be ignored/dropped in order to drive the main focus to the other best practices targeted on this article. The example used in this article is just for demonstration purposes. It is not the best candidate for applying the Builder Design Pattern. We can integrate different practices with the Builder Design Pattern like using generics and other things, but, all of these are dropped to keep the example as simple as possible. There is a reasonable variance in the way of implementing the Builder Design Pattern, therefore, you might find some other different implementations than the one we re going to use in this article. Try to use the Builder Design Pattern only when actually needed as it adds complexity to the whole solution. Peek Into The Future If you follow the same exact steps in this article, you should end up with this solution structure: And you would be able to write some code like this: And this: Sketching the Fluent API Now, we would start with a sketch of how our Fluent API should look like. You can do this on a piece of paper, Excel sheet, or whatever sketching tool you like. So, our sketch would be something like this: Notes: is the main entry point. From there we would move to . Builder New Then we can have two options; and . WithName(name) WithAge(age) However, on the next step, if you are already coming from , we are only allowing . And following the same concept, if you are already coming from , we are only allowing . WithName(name) WithAge(age) WithAge(age) WithName(name) Then we would be merging to one common point. From this common point, we have two options; and . AsTeacher AsStudent From , the flow would be >> . AsTeacher Teaching(subject) WithSchedule(schedule) And from the flow would be >> . AsStudent, Studying(subjects) WithSchedule(stydingSchedule) Finally, they all merge to the command. Build() Defining Interfaces Now, let’s start working on the code. Steps Open VS or your preferred IDE. Create a new Class Library or Console Application. I named my project as . FluentApi Inside my project, I created the following folders: Builder Builder\Dtos Builder\Dtos\Descriptors Builder\Implementations Builder\Interfaces Now you need to keep an important thing in your mind, we will need to jump back and forth between and while working on the implementation, this is normal. Interfaces Dtos Now let’s start with our first interface, . Here is an important trick. I created a file under the folder and named it IMemberBuilder Interfaces 01.IMemberBuilder.cs This at the start of the name helps me easily track the sequence of the whole process. Otherwise, for a small change, you might need to go through all the files to spot the place to apply your changes. 01. namespace FluentApi.Builder.Interfaces { public interface IMemberBuilder { IHuman New { get; } } } We know, from the , that our Builder should expose a property and this property should lead us to something that exposes two methods; and . Sketch New WithName(name) WithAge(age) So, the New property should return, let’s say a new Interface called . IHuman Moving to the next step, let’s define the interface. So, create a file, and define the interface as follows: IHuman 02.IHuman.cs namespace FluentApi.Builder.Interfaces { public interface IHuman { IHaveAgeAndCanHaveName WithAge(int age); IHaveNameAndCanHaveAge WithName(string name); } } We know, from the , that the interface should have the two methods and . However, these two methods should have different return types. Why??? Sketch IHuman WithName(name) WithAge(age) Because we want that once the is called, the only available option is to call , not another . And the same applies to . WithName(name) WithAge(age) WithName(name) WithAge(age) : You might also prefer to have only one method which takes in both the name and age, this is also right but I preferred here to seize the chance to show you different options. Note Moving to the next step, let’s define the interface. So, create a file, and define the interface as follows: IHaveAgeAndCanHaveName 03.IHaveAgeAndCanHaveName.cs namespace FluentApi.Builder.Interfaces { public interface IHaveAgeAndCanHaveName { IHasRole WithName(string name); } } We know, from the , that the interface should have the method . And this method should return something that exposes the and properties. Sketch IHaveAgeAndCanHaveName WithName(name) AsTeacher AsStudent Also, following the same way, let’s define the interface. So, create a file (note that the file is numbered as because it is still on the third step on the whole process), and define the interface as follows: IHaveNameAndCanHaveAge 03.IHaveNameAndCanHaveAge.cs 03 namespace FluentApi.Builder.Interfaces { public interface IHaveNameAndCanHaveAge { IHasRole WithAge(int age); } } We know, from the , that the interface should have the method . And this method should return something that exposes the and properties, the same as . Sketch IHaveNameAndCanHaveAge WithAge(age) AsTeacher AsStudent IHaveAgeAndCanHaveName.WithName(name) Moving to the next step, let’s define the interface. So, create a file, and define the interface as follows: IHasRole 04.IHasRole.cs namespace FluentApi.Builder.Interfaces { public interface IHasRole { IAmTeaching AsTeacher { get; } IAmStudying AsStudent { get; } } } We know, from the , that the interface should have the two properties and . And every one of these properties should return something different according to the following step on the sketch. Sketch IHasRole AsTeacher AsStudent Moving to the next step, let’s define the interface. So, create a file, and define the interface as follows: IAmStudying 05.IAmStudying.cs using FluentApi.Builder.Dtos; namespace FluentApi.Builder.Interfaces { public interface IAmStudying { IHasStudyingSchedule Studying(params Subject[] subjects); } } We know, from the , that the interface should have the method . This method should expect an input of type array of . So, we need to define the class . Sketch IAmStudying Studying(subjects) Subject Subject Also, the should return something exposing . Studying(subjects) WithSchedule(subjectsSechedules) So, we create a file inside the folder and the code would be as follows: Subject.cs Dtos using System; using System.Collections.Generic; using System.Linq; namespace FluentApi.Builder.Dtos { public sealed class Subject : IEquatable<Subject> { public Subject(string name) { Name = name; } public Subject(Subject other) { if (other != null) { Name = other.Name; } } public string Name { get; } public bool Equals(Subject other) { if (ReferenceEquals(null, other)) return false; if (ReferenceEquals(this, other)) return true; return Name == other.Name; } public override bool Equals(object obj) { if (ReferenceEquals(null, obj)) return false; if (ReferenceEquals(this, obj)) return true; if (obj.GetType() != this.GetType()) return false; return Equals((Subject)obj); } public override int GetHashCode() { return (Name != null ? Name.GetHashCode() : 0); } public static bool operator ==(Subject left, Subject right) { return Equals(left, right); } public static bool operator !=(Subject left, Subject right) { return !Equals(left, right); } } public static class SubjectExtensions { public static IEnumerable<Subject> Clone(this IEnumerable<Subject> subjects) { return (subjects != null) ? subjects .Where(s => s != null) .Select(s => new Subject(s)) : new List<Subject>(); } } } What to notice here: It only has one property. Name It is immutable. It inherits the interface and we generated all the required members. IEquatable<Subject> We defined the constructor to provide a way of cloning the Subject from another Subject. The cloning capability in the Builder Pattern is so important because at every step you need to deal with a totally separate object (with different reference) than the ones on previous and next steps. public Subject(Subject other) We also defined the extension method to to avoid repeating the same code on different places. Clone IEnumerable<Subject> Inside the extension method, we are making use of the constructor we defined in the class. public Subject(Subject other) Subject Moving to the next step, let’s define the interface. So, create a file, and define the interface as follows: IAmTeaching 05.IAmTeaching.cs using FluentApi.Builder.Dtos; namespace FluentApi.Builder.Interfaces { public interface IAmTeaching { IHasTeachingSchedule Teaching(Subject subject); } } We know, from the , that the interface should have the method . This method should expect an input of type . Sketch IAmTeaching Teaching(subject) Subject Also, the should return something exposing . Teaching(subject) WithSchedule(sechedules) Moving to the next step, let’s define the interface. So, create a file, and define the interface as follows: IHasStudyingSchedule 06.IHasStudyingSchedule.cs using FluentApi.Builder.Dtos; namespace FluentApi.Builder.Interfaces { public interface IHasStudyingSchedule { ICanBeBuilt WithSchedule(params SubjectSchedule[] subjectsSchedules); } } We know, from the , that the interface should have the method . This method should expect an input of type array of . Sketch IHasStudyingSchedule WithSchedule(subjectsSchedules) SubjectSchedule Also, the should return something exposing the method . WithSchedule(subjectsSchedules) Build() So, we create and files inside the folder and the code would be as follows: Schedule.cs SubjectSchedule.cs Dtos using System; using System.Collections.Generic; using System.Linq; namespace FluentApi.Builder.Dtos { public class Schedule { public Schedule(DateTime from, DateTime to) { From = from; To = to; } public Schedule(Schedule other) { if (other != null) { From = other.From; To = other.To; } } public DateTime From { get; } public DateTime To { get; } } public static class ScheduleExtensions { public static IEnumerable<Schedule> Clone(this IEnumerable<Schedule> schedules) { return (schedules != null) ? schedules .Where(s => s != null) .Select(s => new Schedule(s)) : new List<Schedule>(); } } } using System.Collections.Generic; using System.Linq; namespace FluentApi.Builder.Dtos { public class SubjectSchedule { public SubjectSchedule(Subject subject, Schedule schedule) { Subject = subject; Schedule = schedule; } public SubjectSchedule(SubjectSchedule other) { if (other != null) { Subject = new Subject(other.Subject); Schedule = new Schedule(other.Schedule); } } public Subject Subject { get; } public Schedule Schedule { get; } } public static class SubjectScheduleExtensions { public static IEnumerable<SubjectSchedule> Clone(this IEnumerable<SubjectSchedule> subjectsSchedules) { return (subjectsSchedules != null) ? subjectsSchedules .Where(s => s != null) .Select(s => new SubjectSchedule(s)) : new List<SubjectSchedule>(); } } } Here we follow the same rules as in the class. Subject Moving to the next step, let’s define the interface. So, create a file, and define the interface as follows: IHasTeachingSchedule 06.IHasTeachingSchedule.cs using FluentApi.Builder.Dtos; namespace FluentApi.Builder.Interfaces { public interface IHasTeachingSchedule { ICanBeBuilt WithSchedule(params Schedule[] schedules); } } We know, from the , that the interface should have the method . This method should expect an input of type array of . Sketch IHasTeachingSchedule WithSchedule(schedules) SubjectSchedule Also, the should return something exposing the method . WithSchedule(schedules) Build() Moving to the next step, let’s define the interface. So, create a file, and define the interface as follows: ICanBeBuilt 07.ICanBeBuilt.cs using FluentApi.Builder.Dtos.Descriptors; namespace FluentApi.Builder.Interfaces { public interface ICanBeBuilt { MemberDescriptor Build(); } } We know, from the , that the interface should have the method which returns the final composed . Sketch ICanBeBuilt Build() MemberDescriptor So, we create a file inside the folder. SubjectSchedule.cs Dtos>Descriptors This class should expose all the details of a member whether he is a or . MemberDescriptor Teacher Student MemberDescriptor namespace FluentApi.Builder.Dtos { public enum MemberRole { Teacher = 1, Student = 2 } } namespace FluentApi.Builder.Dtos.Descriptors { public class MemberDescriptor { public MemberDescriptor(MemberDescriptor other = null) { if (other != null) { Name = other.Name; Age = other.Age; Role = other.Role; } } public string Name { get; set; } public int Age { get; set; } public MemberRole Role { get; set; } public virtual MemberDescriptor Clone() { return new MemberDescriptor(this); } } } What to notice here: The class is exposing the basic info about a member. The more specific info about a or would reside in other two classes for and . MemberDescriptor Teacher Student Teacher Student The class is not immutable and that’s because on every step of the creational process, you would be adding a small detail to the object. So, you don’t have all the details at once. However, you can still choose to make it immutable but you would need to provide more than one constructor that matches your needs for every step. Still we are providing the constructor for cloning purposes as explained before. public MemberDescriptor(MemberDescriptor other = null) And we added a method for an important reason. At some steps on the process, you would be merging from a more specific case to a more generic one. In this kind of cases, your implementations of the interfaces would need to deal with the parent class, not any of its children. And, it would need to clone the entity without knowing it is originally a or . public virtual MemberDescriptor Clone() MemberDescriptor Teacher Student For example, in this merging step: When implementing the interface, it would be expecting an instance of , it can’t be a specific descriptor for a or as it is a common step for both paths. Additionally, you would need at the end to clone the passed in . ICanBeBuilt MemberDescriptor Teacher Student MemberDescriptor TeacherDescriptor using System.Collections.Generic; using System.Linq; namespace FluentApi.Builder.Dtos.Descriptors { public class TeacherDescriptor : MemberDescriptor { public TeacherDescriptor(MemberDescriptor member = null) : base(member) { if (member is TeacherDescriptor teacher) { Subject = teacher.Subject != null ? new Subject(teacher.Subject) : null; Schedules = teacher.Schedules != null ? teacher.Schedules.Clone().ToList() : new List<Schedule>(); } } public Subject Subject { get; set; } public List<Schedule> Schedules { get; set; } = new List<Schedule>(); public override MemberDescriptor Clone() { return new TeacherDescriptor(this); } } } What to notice here: Inside the cloning constructor, we need to check for null properties because as explained before, details are added piece by piece on more than one step. We are also using the extension method for cloning. IEnumerable<Schedule> We defined an override to the method and now we are using our type-specific clone constructor. Clone StudentDescriptor using System.Collections.Generic; using System.Linq; namespace FluentApi.Builder.Dtos.Descriptors { public class StudentDescriptor : MemberDescriptor { public StudentDescriptor(MemberDescriptor member = null) : base(member) { if (member is StudentDescriptor student) { Subjects = student.Subjects != null ? student.Subjects.Clone().ToList() : new List<Subject>(); SubjectsSchedules = student.SubjectsSchedules != null ? student.SubjectsSchedules.Clone().ToList() : new List<SubjectSchedule>(); } } public List<Subject> Subjects { get; set; } = new List<Subject>(); public List<SubjectSchedule> SubjectsSchedules { get; set; } = new List<SubjectSchedule>(); public override MemberDescriptor Clone() { return new StudentDescriptor(this); } } } Following the same concept as in . TeacherDescriptor Interfaces Implementations Now, we move to implementing our interfaces. Let’s define the class implementing the interface. So, create a file, and define the class as follows: MemberBuilder IMemberBuilder 01.MemberBuilder.cs using FluentApi.Builder.Dtos.Descriptors; using FluentApi.Builder.Interfaces; namespace FluentApi.Builder.Implementations { public class MemberBuilder : IMemberBuilder { public IHuman New => new Human(new MemberDescriptor()); } } The property, should return an interface. So, we would now move to implementing the interface but we need to keep in mind something important. We need to keep passing around the partially composed because each step would add some detail to it till it is finally complete. New IHuman IHuman MemberDescriptor On the class, we don’t have any detail to add, however, this is our starting point, so the class should create the initial to start with and then pass it to the next step. MemberBuilder MemberDescriptor Moving on to define the class implementing the interface. So, create a file, and define the class as follows: Human IHuman 02.Human.cs using FluentApi.Builder.Dtos.Descriptors; using FluentApi.Builder.Interfaces; namespace FluentApi.Builder.Implementations { internal class Human : IHuman { private readonly MemberDescriptor m_Descriptor; public Human(MemberDescriptor descriptor) { m_Descriptor = descriptor; } public IHaveNameAndCanHaveAge WithName(string name) { var clone = new MemberDescriptor(m_Descriptor) { Name = name }; return new HaveNameAndCanHaveAge(clone); } public IHaveAgeAndCanHaveName WithAge(int age) { var clone = new MemberDescriptor(m_Descriptor) { Age = age }; return new HaveAgeAndCanHaveName(clone); } } } We defined a constructor which takes in a and saves it to a local read-only variable. MemberDescriptor We also implemented the two methods but what is important to notice here is that before adding any detail to the we first create a clone of it. To create a clone, we can use the cloning constructor or call the method on the class. MemberDescriptor Clone MemberDescriptor Every method would return a different interface, so now we need to move to implementing these interfaces. Moving on to define the class implementing the interface. So, create a file, and define the class as follows: HaveAgeAndCanHaveName IHaveAgeAndCanHaveName 03.HaveAgeAndCanHaveName.cs using FluentApi.Builder.Dtos.Descriptors; using FluentApi.Builder.Interfaces; namespace FluentApi.Builder.Implementations { internal class HaveAgeAndCanHaveName : IHaveAgeAndCanHaveName { private readonly MemberDescriptor m_Descriptor; public HaveAgeAndCanHaveName(MemberDescriptor descriptor) { m_Descriptor = descriptor; } public IHasRole WithName(string name) { var clone = new MemberDescriptor(m_Descriptor) { Name = name }; return new HasRole(clone); } } } Following the same pattern, we created the constructor, implemented the method, created the clone, added the detail, returned the new object passing in the clone to the constructor. Moving on to define the class implementing the interface. So, create a file, and define the class as follows: HaveNameAndCanHaveAge IHaveNameAndCanHaveAge 03.HaveNameAndCanHaveAge.cs using FluentApi.Builder.Dtos.Descriptors; using FluentApi.Builder.Interfaces; namespace FluentApi.Builder.Implementations { internal class HaveNameAndCanHaveAge : IHaveNameAndCanHaveAge { private readonly MemberDescriptor m_Descriptor; public HaveNameAndCanHaveAge(MemberDescriptor descriptor) { m_Descriptor = descriptor; } public IHasRole WithAge(int age) { var clone = new MemberDescriptor(m_Descriptor) { Age = age }; return new HasRole(clone); } } } Following the same pattern, we created the constructor, implemented the method, created the clone, added the detail, returned the new object passing in the clone to the constructor. Moving on to define the class implementing the interface. So, create a file, and define the class as follows: HasRole IHasRole 04.HasRole.cs using FluentApi.Builder.Dtos; using FluentApi.Builder.Dtos.Descriptors; using FluentApi.Builder.Interfaces; namespace FluentApi.Builder.Implementations { internal class HasRole : IHasRole { private readonly MemberDescriptor m_Descriptor; public HasRole(MemberDescriptor descriptor) { m_Descriptor = descriptor; } public IAmTeaching AsTeacher => new AmTeaching(new TeacherDescriptor(m_Descriptor) { Role = MemberRole.Teacher }); public IAmStudying AsStudent => new AmStudying(new StudentDescriptor(m_Descriptor) { Role = MemberRole.Student }); } } Following the same pattern, we created the constructor, implemented the method, created the clone, added the detail, returned the new object passing in the clone to the constructor. Moving on to define the class implementing the interface. So, create a file, and define the class as follows: AmStudying IAmStudying 05.AmStudying.cs using System.Linq; using FluentApi.Builder.Dtos; using FluentApi.Builder.Dtos.Descriptors; using FluentApi.Builder.Interfaces; namespace FluentApi.Builder.Implementations { internal class AmStudying : IAmStudying { private readonly StudentDescriptor m_Descriptor; public AmStudying(StudentDescriptor descriptor) { m_Descriptor = descriptor; } public IHasStudyingSchedule Studying(params Subject[] subjects) { var clone = new StudentDescriptor(m_Descriptor) { Subjects = subjects.AsEnumerable().Clone().ToList() }; return new HasStudyingSchedule(clone); } } } Following the same pattern, we created the constructor, implemented the method, created the clone, added the detail, returned the new object passing in the clone to the constructor. What to notice here is that the constructor is expecting a not a and that’s because at the moment of constructing it is clear. StudentDescriptor MemberDescriptor AmStudying Also, notice that we even cloned the passed in array of using the extension method we created before. This way we make sure that any changes to be applied by the end user to the passed in array of would not affect our builders state. Subject Subject If, for some reason, this is not what you intend to do, then you can change this code by passing in the passed in array as it is. Moving on to define the class implementing the interface. So, create a file, and define the class as follows: AmTeaching IAmTeaching 05.AmTeaching.cs using FluentApi.Builder.Dtos; using FluentApi.Builder.Dtos.Descriptors; using FluentApi.Builder.Interfaces; namespace FluentApi.Builder.Implementations { internal class AmTeaching : IAmTeaching { private readonly TeacherDescriptor m_Descriptor; public AmTeaching(TeacherDescriptor descriptor) { m_Descriptor = descriptor; } public IHasTeachingSchedule Teaching(Subject subject) { var clone = new TeacherDescriptor(m_Descriptor) { Subject = new Subject(subject) }; return new HasTeachingSchedule(clone); } } } Following the same pattern, we created the constructor, implemented the method, created the clone, added the detail, returned the new object passing in the clone to the constructor. What to notice here is that the constructor is expecting a not a and that’s because at the moment of constructing it is clear. TeacherDescriptor MemberDescriptor AmTeaching Also, here we are not passing the same passed in by the end user, we are passing a clone. Subject Moving on to define the class implementing the interface. So, create a file, and define the class as follows: HasStudyingSchedule IHasStudyingSchedule 06.HasStudyingSchedule.cs using System; using System.Linq; using FluentApi.Builder.Dtos; using FluentApi.Builder.Dtos.Descriptors; using FluentApi.Builder.Interfaces; namespace FluentApi.Builder.Implementations { internal class HasStudyingSchedule : IHasStudyingSchedule { private readonly StudentDescriptor m_Descriptor; public HasStudyingSchedule(StudentDescriptor descriptor) { m_Descriptor = descriptor; } public ICanBeBuilt WithSchedule(params SubjectSchedule[] subjectsSchedules) { if (m_Descriptor.Subjects.Any(s => !subjectsSchedules.Select(ss => ss.Subject).Contains(s))) { throw new ArgumentException("Some of the registered subjects are not scheduled."); } if (subjectsSchedules.Select(ss => ss.Subject).Any(s => !m_Descriptor.Subjects.Contains(s))) { throw new ArgumentException("Some of the scheduled subjects are not registered."); } var clone = new StudentDescriptor(m_Descriptor) { SubjectsSchedules = subjectsSchedules.AsEnumerable().Clone().ToList() }; return new CanBeBuilt(clone); } } } Following the same pattern, we created the constructor, implemented the method, created the clone, added the detail, returned the new object passing in the clone to the constructor. What to notice here is that we added some asserts to check if some of the registered subjects are not scheduled or some of the scheduled subjects are not registered. This is just an example and for sure you can add all your business rules as well in every step whenever needed. Moving on to define the class implementing the interface. So, create a file, and define the class as follows: HasTeachingSchedule IHasTeachingSchedule 06.HasTeachingSchedule.cs using System.Linq; using FluentApi.Builder.Dtos; using FluentApi.Builder.Dtos.Descriptors; using FluentApi.Builder.Interfaces; namespace FluentApi.Builder.Implementations { internal class HasTeachingSchedule : IHasTeachingSchedule { private readonly TeacherDescriptor m_Descriptor; public HasTeachingSchedule(TeacherDescriptor descriptor) { m_Descriptor = descriptor; } public ICanBeBuilt WithSchedule(params Schedule[] schedules) { var clone = new TeacherDescriptor(m_Descriptor) { Schedules = schedules.AsEnumerable().Clone().ToList() }; return new CanBeBuilt(clone); } } } Following the same pattern, we created the constructor, implemented the method, created the clone, added the detail, returned the new object passing in the clone to the constructor. Moving on to define the class implementing the interface. So, create a file, and define the class as follows: CanBeBuilt ICanBeBuilt 07.CanBeBuilt.cs using FluentApi.Builder.Dtos.Descriptors; using FluentApi.Builder.Interfaces; namespace FluentApi.Builder.Implementations { internal class CanBeBuilt : ICanBeBuilt { private readonly MemberDescriptor m_Descriptor; public CanBeBuilt(MemberDescriptor descriptor) { m_Descriptor = descriptor; } public MemberDescriptor Build() { return m_Descriptor.Clone(); } } } Following the same pattern, we created the constructor, implemented the method, created the clone, added the detail, returned the new object passing in the clone to the constructor. What to notice here is that the constructor is expecting a as at this point the passed in could be a or a . MemberDescriptor MemberDescriptor TeacherDescriptor StudentDescriptor Also, on the method we are returning a clone of the descriptor but this time we can’t use the cloning constructor as if you use the cloning constructor of the class, you would finally return an instance of , neither a nor a which is not right. Instead, we use the method which would return the right instance at run-time. Build MemberDescriptor MemberDescriptor TeacherDescriptor StudentDescriptor Clone Time For Testing Now, with a simple Console application we can try to run the following code: using System; using FluentApi.Builder.Dtos; using FluentApi.Builder.Implementations; namespace FluentApi { class Program { static void Main(string[] args) { var memberBuilder = new MemberBuilder(); var ahmed = memberBuilder .New .WithName("Ahmed") .WithAge(36) .AsTeacher .Teaching(new Subject("Software Engineering")) .WithSchedule( new Schedule ( new DateTime(2021, 11, 20), new DateTime(2021, 12, 20) ), new Schedule ( new DateTime(2022, 1, 5), new DateTime(2021, 3, 5) )) .Build(); var subjectsToStudy = new Subject[] { new Subject("Software Engineering"), new Subject("Physics") }; var mohamed = memberBuilder .New .WithAge(15) .WithName("Mohamed") .AsStudent .Studying(subjectsToStudy) .WithSchedule ( new SubjectSchedule ( subjectsToStudy[0], new Schedule ( new DateTime(2021, 11, 20), new DateTime(2021, 12, 20) ) ), new SubjectSchedule ( subjectsToStudy[1], new Schedule ( new DateTime(2021, 11, 20), new DateTime(2021, 12, 20) ) ) ) .Build(); Console.ReadLine(); } } } What you can notice here: Our Fluent API is working as it should. May be for this example, you might find it an overkill to create a Fluent API for such simple objects, however, we are using this simple example for demonstration purposes only. Final Words The Builder Design Pattern has some advantages but it also adds complexity. Therefore, you need to use it only when you actually need it. That’s it, hope you found reading this article as interesting as I found writing it. Also Published Here