Saturday, August 01, 2009

Fluent Domain Methods

In my last post, I talked about a Generic Expression Builder class for easily creating domain entities through expression builders. This generic base class takes away some of the burden while developing these expression builder classes.  Expression builders are generally useful inside the domain when you need to create and set up a complete aggregate root from scratch, not to say that expression builders aren't useful in other parts of an application (e.g. creating message objects, DTO's, etc.).

Another place where a fluent interface can add value is for implementing behavior on a domain entity. Taking the same example from my previous post, where we have a domain class named Document with the name of the author, a title and one or more associated tags. Suppose we have a command that needs to add a new Tag to the existing list of tags for a particular Document. We usually end up with code like this:

public class Document
{
    var IList<Tag> _tags = new List<Tag>();

    ...

    public void AddTag(String name)    
    {
        var newTag = new Tag(name);
        _tags.Add(newTag);
    }
}

The usual mistake I see a lot is to let the AddTag method directly take a Tag object. However, in this case the Document class is the aggregate root where Tag is merely a value object that lives within the boundaries of its aggregate root. This means that the aggregate root is responsible for creating instances of a Tag (also check out this post from Udi Dahan).

Lets make this code a bit more fluent. In my previous post, I provided a separate expression builder for creating new Tag objects. This TagBuilder is already used by the expression builder that creates Document objects.

public interface ITagBuilder
{
    void Named(String name);
}

public class TagBuilder : ITagBuilder
{
    private readonly Action<Tag> _afterBuildAction;

    public TagBuilder(Action<Tag> afterBuildAction)
    {
        _afterBuildAction = afterBuildAction;
    }

    public void Named(String name)
    {
        var tag = new Tag(name);
        _afterBuildAction(tag);
    }
}
Now lets reuse this builder class for our domain method that adds a new Tag for a Document. This is how the AddTag method is implemented:
public ITagBuilder AddTag()
{
    return new TagBuilder(newTag => _tags.Add(newTag));
}

And this is how the calling code looks:

document.AddTag().Named("Science"));

I have to admit that it does involve some overkill for this simple example. However, the code becomes a lot more clear when you have to provide a couple of more arguments to a domain method. Again, not everything is a nail for this shiny hammer. But it does bring some nice, readable code when needed. Here's another example that attaches the data of a file to a Document:

document.Attach()
    .FileWithName("The Universe in a Nutshell.pdf")
    .AndData(new Byte[] { ... });

The fact that we can just reuse an existing expression builder is also a clear advantage.

Till next time

4 comments:

Michael Hart said...

I have to strongly disagree with your characterisation that creating a value object outside of an aggregate root is a "mistake".

Part of the beauty of value objects is that they don't have a lifecycle to worry about, they can be shared, cloned, replaced, etc. This means that they don't need to be subject to the same constraints as entities within aggregate roots.

In your example, what you've done is just replace a more relevant value object (Tag) with a less relevant one (String). IMO this makes the code harder to read and in the long term I think harder to maintain - it also encourages what I would consider to be a bad practice of your aggregates becoming part of your service interfaces, with inputs being passed as is from your service boundaries.

To put it another way, would you really want each aggregate to have their own type for Money or Time?

I'm not saying that there may be cases where you have internal value objects specific to an aggregate. But if you're exposing methods, then it's much cleaner to use the relevant objects on your signatures (plus in this case, it discourages magic strings!), and again, I think it's far too strong and probably misleading to characterise it as a mistake.

Jan Van Ryswyck said...

@Michael: The value object is not internal or specific to the aggregate root. Because it is created by the expression builder, another aggregate can simply reuse it. Aggregates will not need their own type for Money or Time.

Using an expression builder this way, I notice more loose coupling. Not sure why this is harder to maintain?

Michael Hart said...

"The value object is not internal or specific to the aggregate root."

Right... So why did you say this then?

"The usual mistake I see a lot is to let the AddTag method directly take a Tag object. However, in this case the Document class is the aggregate root where Tag is merely a value object that lives within the boundaries of its aggregate root."

This is the paragraph that my comment is directed at.

Jan Van Ryswyck said...

Maybe I should have provided a little more context. For some reason, I never like to specify a value object to a method like you mentioned. What concerns me is that a non-transient reference can be held and that the aggregate is not able to do anything about it.

The advantage of providing a fluent method this way is that the value object or entity is always created by a single expression builder (no SRP violation) and that it can be reused only if necessary (which was definitely not the case for the application where the example code comes from) only when you have to without specifying an instance through the arguments of the method.