Blog

  • Home /
  • Blog /
  • Experimenting With Fluent Interfaces in the Domain

Experimenting With Fluent Interfaces in the Domain

March 28, 2009

As you might have guessed from my previous two blog posts, I've been experimenting with fluent interfaces lately. I've been thinking about passing an expression builder to an aggregate root so that the root entity can use the builder to create a particular child entity. This begs for some code.

Suppose I have an aggregate root called Category. A Category can have a list of Items. So in order to add another Item to a Category, the code might look like this.

public class Category : DomainEntity
{
    private readonly ISet<Item> _items;
    public virtual String Name { get; private set; }

    protected Category()
    {
        _items = new HashedSet<CatalogItem>();
    }

    public void Categorize(String productNumber,
                     String itemName, String manufacturer)
    {
        var item = new Item(productNumber, itemName,
                            manufacturer);

        _items.Add(item);
    }
}

The Categorize method adds a new Item to the Category. What I don't like about this method is the number of arguments, even for this simple example. In real life, there can be plenty of arguments that need to be passed to this method. Another thing that somewhat disturbs me is the fact that the Category class is also responsible for creating an Item class.

Lets see what I ended up with using an expression builder. First I created one for building an Item.

public interface IItemBuilder
{
    Item Build();
}

public class ItemBuilder
    : DomainObjectBuilder<Item>,
      IItemBuilder
{
    protected Manufacturer Manufacturer { get; set; }
    protected String Name { get; set; }
    protected Product Product { get; set; }

    public ItemBuilder Named(String name)
    {
        Name = name;
        return this;
    }

    public CatalogItemBuilder ManufacturedBy(
                   Action<ManufacturerBuilder> buildUsing)
    {
        var manufacturerBuilder = new ManufacturerBuilder();
        buildUsing(manufacturerBuilder);
        Manufacturer = manufacturerBuilder.Build();

        return this;
    }

    public CatalogItemBuilder ForProduct(
                        Action<ProductBuilder> buildUsing)
    {
        var productBuilder = new ProductBuilder();
        buildUsing(productBuilder);
        Product = productBuilder.Build();

        return this;
    }

    public override Item Build()
    {
        return new Item(Name, Manufacturer, Product);
    }
}

This is a quite straightforward expression builder where the creation of Manufacturer and Product value objects is handed of to other expression builders, which are not important for this example. Notice the IItemBuilder interface, which can now be used by the Categorize method.

public void Categorize(IItemBuilder itemBuilder)
{
    var item = itemBuilder.Build();
    _items.Add(item);
}

This way I both eliminate the number of arguments I need to pass to the method and the code creating an Item object, which is now moved to the expression builder. Notice that by passing in an instance of IItemBuilder, the domain object is unaware of the actual expression builders themselves which live on top of the domain entities and value objects anyway. Now, in order to remove the need of creating an instance of an ItemBuilder in the layers that use the domain, I added an extra extension method that does that for us.

public static class CatalogCategoryExtensions
{
    public static void Categorize(
        this CatalogCategory catalogCategory,
        Action<CatalogItemBuilder> buildUsing)
    {
        var catalogItemBuilder = new CatalogItemBuilder();
        buildUsing(catalogItemBuilder);
        catalogCategory.CategorizeItem(catalogItemBuilder);
    }
}

This results in the following syntax in the application service (except for the hard-coded values, of course).

category.Categorize(item =>
    item.Named("iPhone")
    .ManufacturedBy(manufacturer =>
        manufacturer.Named("Apple"))
    .ForProduct(product =>
        product.Numbered("AA1687")));

Although this all looks good, I'm not entirely sure of this approach yet but it certainly looks interesting to me. All feedback is more than welcome, off course. I could be totally jumping the shark on this one :-). So let me know what you think.

Profile picture of Jan Van Ryswyck

Jan Van Ryswyck

Thank you for visiting my blog. I’m a professional software developer since Y2K. A blogger since Y2K+5. Curator of the Awesome Talks list. Past organizer of the European Virtual ALT.NET meetings. Thinking and learning about all kinds of technologies since forever.

Comments

About

Thank you for visiting my website. I’m a professional software developer since Y2K. A blogger since Y2K+5. Curator of the Awesome Talks list. Past organizer of the European Virtual ALT.NET meetings. Thinking and learning about all kinds of technologies since forever.

Contact information

(+32) 496 38 00 82

infonull@nullprincipal-itnull.be