The Architect´s Napkin

Software Architecture on the Back of a Napkin
posts - 69 , comments - 227 , trackbacks - 0

My Links

News

Archives

Post Categories

Image Galleries

Using Event-Based Components for Library Development

Can Event-Based Components (EBC) be used to design libraries? Sure they can. FallenGameR asked a question along this line in response to my previous article. Let me demonstrate this with a simple library scenario:

A function ToDictionary() is to be developed which converts a string like “port=8080;user=bart;password=foo” into a Dictionary<string, string>. The usage should be like this:

var td = new StringToDictionaryConverter();

var dict = td.Convert(“port=8080;user=…”);

or

var dict = new Dictionary<string, string>();

dict.Parse(“port=8080;user=…”);

or

var dict = “port=8080;user=…”.ToDictionary();

Sure, this is trivial functionality and you could write it down in 5 minutes. But for the purpose of showing you how to use EBC for library development it´s sufficient – even though the effort seems to be artificially high.

Please forgive me for neglecting a closer analysis of the requirements. There would be quite some questions to ask, for example, “How to deal with a string like ‘a=1;a=2’?” or “Is ‘a==’ to be considered an error?” Also we could talk about the Ubiquitous Language of this domain ;-) But this time I´ll skip those details and jump right into the design. Here´s my take:

image

The initial trigger for the feature process comes from the API method Convert(). It functions as a facade before the EBC implementation.

Convert to dictionary is a composite activity whose sole purpose is to wire up the real workhorse activities. It hides any EBC implementation details from the surrouding object oriented code. This way the code can look as simple as the high level design at the top of the napkin:

public partial class StringToDictionaryConverter
{
    private readonly Action<string, Action<Dictionary<string, string>>>  Convert_to_dictionary;


    public Dictionary<string, string> Convert(string configuration)
    {
        Dictionary<string, string> dict=null;
        this.Convert_to_dictionary(configuration, result => dict = result);
        return dict;
    }
}

As you can see I decided to translate the Convert to dictionary activity into a method with one input parameter and a delegate to pass any outputs to as a second parameter. Sure, I could have used a function. But to stay flexible – maybe tomorrow I want to add another output to the activity – I´m using this pattern even in such a simple scenario.

The real EBC code is offloaded to a nested part of the partial class. This is how my VS project looks like:

image

I´m using VSCommands to arrange files this way to express different levels of abstractions within a single class. And here´s the code from StringToDictionaryConverter_EBC.cs:

partial class StringToDictionaryConverter
{
    public StringToDictionaryConverter()
    {
        this.Convert_to_dictionary = (configuration, out_dictionary) =>
                {
                    Split_into_assignments(configuration, assignments =>
                    Split_assignments(assignments, keyValuePairs =>
                    Build_dictionary(keyValuePairs, out_dictionary)));
                };
    }


    internal void Split_into_assignments(string configuration, Action<string[]> out_assignments)
    {
        var assignments = configuration.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
        out_assignments(assignments);
    }


    internal void Split_assignments(string[] assigments, Action<KeyValuePair<string, string>[]> out_keyValuePairs)
    {
        var keyValuePairs = assigments.Select(assignment => assignment.Split('='))
                                        .Select(parts => new KeyValuePair<string, string>(parts[0].Trim(), parts[1]))
                                        .ToArray();
        out_keyValuePairs(keyValuePairs);
    }


    internal void Build_dictionary(KeyValuePair<string, string>[] keyValuePairs, Action<Dictionary<string, string>> out_dictionary)
    {
        out_dictionary(keyValuePairs.Aggregate(new Dictionary<string, string>(), (dict, kvp) =>
        {
            dict.Add(kvp.Key, kvp.Value);
            return dict;
        }));
    }
}

Within the ctor the activity methods are wired up to form the flow as designed. Note how readable the wiring code is. The sequence of processing steps is mirrored in code:

  1. Split a whole configuration string like “a=1;b=2” into assignments like “b=2”.
  2. Split assignments like “b=2” into key/value tuples like (“b”, “2”).
  3. Build from key/value tuples like (“b”, “2”) the dictionary.

In addition I implemented a second class offering the conversion functionality through two extension methods as required. See the test cases for usage samples like

[Test]
public void Run_sample_with_extension_method_on_string()
{
    var dict = "port=8080;user=foo;password=bar".ToDictionary();
 
    Assert.That(dict, Is.EqualTo(new Dictionary<string, string>
                                        {
                                            {"port", "8080"},
                                            {"user", "foo"},
                                            {"password", "bar"}
                                        }));
}

You can find the whole code here in my Google Project Mercurial repository.

Summary

I could have done the whole thing without EBC. Sure. And even with EBC I could have done it differently. Why, for example, would I need the ctor to set up some wiring? Couldn´t the Convert() method just contain the call sequence wired up in the ctor? And why not use functions for the activities?

Yes, you could do it like that. But I don´t. I want to constrain the means with which to express EBC designs. There are several reasons for that:

  • If I limit my choices I become more productive and my code becomes more predictable. This makes understanding the code easier.
  • Also a limited set of target patterns for the model-to-code-translation makes it easier to use standard components. That way activities become more composable. This will be the topic of a later article.
  • Having a separate binding step (wire up of activities) is a way of separating concerns. Wiring up activities is different from domain logic.

I value high creativity. I love to have choices. At least fundamentally. Because there are situations where the necessity to choose becomes a burden. There is a time for making decisions and creativity. That´s while implementing atomic EBC activities, the real workhorses of EBC designs.

And there is a time for not-thinking. That´s while translating a design into code. Be creative while modeling, but not while coding the model elements.

Ok, so have I done in using EBC for developing a library? I think it went pretty well. There´s no reason why you couldn´t use EBC for implementing a LINQ provider or building a compression library. You can serve any existing API (defined by a couple of interfaces) with an EBC design.

Print | posted on Thursday, July 29, 2010 2:53 PM | Filed Under [ Event-Based Components ]

Feedback

Gravatar

# re: Using Event-Based Components for Library Development

Nice approach. Uses functional data immutability and piping. Immutability has trouble times when there is some state somewhere and pipeline might have trouble times when there is an exception in the middle of the pipe.

I need some time to play with this. I think there can be some issues with state or error handling. But I can not point them out right away.
7/30/2010 9:33 AM | FallenGameR
Gravatar

# re: Using Event-Based Components for Library Development

EBC are not per se about immutability. You can make the data flowing through a feature process immutable, if you like. But you need not.

Likewise EBC are synchronous by default.

Exception handling is not a problem as I´ll show in a future posting.
7/30/2010 1:05 PM | Ralf Westphal
Post A Comment
Title:
Name:
Email:
Comment:
Verification:
 

Powered by: