We show how we designed our C#, domain-focused, fluent builders that support nested entities by using lambdas.
In our integration tests we make heavy use of builders designed with Fluent Interface.
Say we have a class Order
with some properties:
A builder with a Fluent Interface lets create instances of Order
with a syntax like:
The builder’s code is something like:
A bit verbose and not so convenient, at least if compared with an ordinary Object Initializer. An Object Initializer gets to the same result with way less code:
Things get more interesting when the entities have other nested entities. Say for example Order
has a property Article
, a separate entity which in turn has got a couple properties:
There are a few options for a Fluent Interface with nested entities. We experimented with 3 of them.
The simplest approach requires the OrderBuilder
to have a method for each of the Article
’s properties:
Sincerely, this is not ideal, as it pollutes the OrderBuilder
with methods, making the reuse of the code for building an Article
nearly impossible.
A better option is to provide Article
with its own builder, and to make it accessible from OrderBuilder
with something like:
With this approach WithArticle()
is supposed to return an ArticleBuilder
, while End()
would return the original OrderBuilder
. In other words, WithArticle()
and End()
respectively begins and ends a nested scope.
This is already better than the previous attempt, but it is not yet ideal, because the implementation gets easily complicated.
For example, End()
must return an instance of OrderBuilder
, but it is not at all obvious how the ArticleBuilder
instance would keep memory of the OrderBuilder
instance . Probably, it has to receive it with the WithArticle()
. Nothing impossible, but it makes the code a bit convoluted.
Also, the scope, highlighted with the indentation
is artificial: it will probably be removed by the IDE with operations like “Reformat code” or the like. Not the best result.
We found a third approach very convenient:
The method HavingArticle()
takes a lambda operating on the ArticleBuilder
. It’s the lambda itself that defines the nested scope. The method HavingArticle
returns the instance of OrderBuilder
, just like all the other methods, making it trivially simple to continue with the fluent syntax. The scope is clearly defined, and the indentation is natively managed by the editor.
Also the implementation is nothing hard:
When OrderBuilder
is asked to the build the Order
instance, it delegates the construction of the nested entity Article
to ArticleBuilder
:
The ArticleBuilder
would be nothing special but an ordinary builder:
As seen, ArticleBuilder
is just another ordinary builder, with a Fluent Interface just like the OrderBuilder
. What’s interesting is that ArticleBuilder
can have another nested builder, enabling the possibility for even deeper nested scopes.
We decided to name the methods that introduce a nested scope with the prefix Having
, rather than with the prefix With
, to help the developer identify the nested entities, but of course this just a matter of tastes.
Find here the complete example.
Over time, using this style of fluent builders, we realized that it would be useful to adapt the resulting syntax to the domain language. That’s the topic of the second part of this post.