The Architect´s Napkin

.NET Software Architecture on the Back of a Napkin

  Home  |   Contact  |   Syndication    |   Login
  44 Posts | 0 Stories | 134 Comments | 0 Trackbacks

News

Archives

Post Categories

Messaging is neutral to the way it´s done. There is nor right or wrong. You think of a message bus, when you hear about communication by messages in software? That´s fine. You think of “pipes and filters” like Steve Bate does, when you hear about messaging? That´s fine, too.

Messaging does not dictate specific means for how to send messages. All´s well, as long as messages are sent one-way and sender and receiver are oblivious of one another (Principle of Mutual Oblivion (PoMO)).

Let me show you what I mean with an example. Here´s a scenario:

A small application is to be written to count the lines of code (LOC) in C# files located in a directory tree.

The user should be able to select the root path of the .cs files. And once the LOC counting has started, the progress should be shown. At the end, the stats on the number of files processed and the total LOC are to be displayed.

To solve this problem, there are at least two parties involved: a UI and some domain logic. They should be communicating via messaging. But how?

Should there be a clear flow like this:

image

The processing gets initiated by some interaction of the user with the UI. Then data flows to the main processing unit, which outputs progress updates and the finally the LOC count stats to the UI. Cause and effect, flow of processing are clearly visible.

Or should the cooperating parties hand over their messages to a bus for delivery:

image

What´s the better way to connect UI and domain logic? Codewise both is easy. Here´s how the flow could get wired up:

// build
var ui = new Form1();
var interactions = new Interactions();

// bind
interactions.Progress += ui.Display_Progress;

ui.OnFolderSelected += path => {
    var stats = interactions.Count_LOC_in_Path(path);
    ui.Display_Stats(stats);
};

// run
Application.Run(ui);

Output could flow through events (ui.OnFolderSelected, interactions.Progress) or be returned from a function (interactions.Count_LOC_in_Path()). Input would flow by calling methods (ui.Display_Progress(), interactions.Count_LOC_in_Path()).

And here´s how the bus could be employed:

// build
var bus = new Bus();
var ui = new Form1(bus);
var interactions = new Interactions(bus);

// bind
bus.Subscribe<string>("PathToAnalyse", interactions.Count_LOC_in_Path);
bus.Subscribe<string>("LOCStats", ui.Display_Stats);
bus.Subscribe<string>("AnalysisProgress", ui.Display_Progress);

// run
Application.Run(ui);

To receive input, functional units would subscribe to certain notifications. The bus then would call the appropriate handlers. Output on the other hand would be published by handing it over to the bus, e.g. bus.Publish(“PathToAnalyse”, @“c:...”);.

Which approach to messaging would you choose? What are the advantages/drawbacks of one or the other?

And how to continue below that level? Because however Count does its work, it´s cerainly more than one concern involved and more than one step to take:

  • Relevant files have to be gathered
  • File contents have to be loaded
  • Lines of code have to be counted
  • Stats have to be compiled
  • Report progress, maybe as each file gets tackled
For that a flow like this comes to my mind:

image

It could be easily implemented, event without wiring-up events and event handlers. Just a sequence of method calls:

class Interactions
{
    public string Count_LOC_in_Path(string path)
    {
        var fsa = new FilesystemAdapter();
        var lc = new LineCounter();

        fsa.Find_CS_files(path, filename => {
            Progress(Path.GetFileName(filename));
            var content = fsa.Load_file(filename);
            lc.Count_LOC_in_file(content);
        });

        return lc.Stats;
    }


    public event Action<string> Progress;
}

Except for fsa.Find_CS_files() which does not return a single value, but produces a stream of filenames as output via a continuation:

public void Find_CS_files(string path, Action<string> OnFilename)
{
    Directory
      .GetFiles(path, "*.cs", SearchOption.AllDirectories)
      .Where(filename => !Path.GetFileName(filename)
                              .StartsWith("Temporary"))
      .ToList()
      .ForEach(OnFilename);
}

Using a continuation keeps the enumeration in the leaf method of the call tree: Find_CS_files(). This way the code follows the Integration Operation Segregation Principle (IOSP). It keeps nodes in a call tree free of logic; logic only resides in leafs. That makes them easier to test and the whole tree easier to understand.

Of course this functionality could also be implemented using a bus to conduct the messages between the cooperating functional units, e.g. the file system adapter and the true domain logic.

image

But the question would be the same as above: What´s the better approach for a messaging scenario? Should a bus or a flow be used?

My personal answer is twofold:

  1. Whether to use a bus or a flow is less important than to use messaging at all. Following the IOSP and the PoMO makes the most difference, I guess. So if Steve is in favour of “pipes and filters” and I like wiring up events or just plain sequences of method calls, should be not point for much dispute.
  2. That said, I´d like to put it this way: understandability over decoupling. Both, understandability and decoupling are important, but since with IOSP and PoMO the decoupling is already high, I think the next issue to tackle should be understandability and not further decoupling. Since flows are easier to understand in terms of “how is input processes to yield output?” I´d say use a flow whenever in doubt. That means on the other hand: use a bus whenever understandability is not hurt.

What does that mean regarding the above example scenario?

I´d connect UI and domain logic using a bus. Their interplay is readily understandable. Or you could argue, UI and domain logic should be as decoupled as possible to make the UI easier to exchange.

For the domain logic, though, I´d prefer a flow. That´s where the action is. That´s the most likely area for changes. So the need for understandability is high.

Also this is where precise coordination is required. Using a bus it´s harder to correlate response processing with requests. Currently the example system is too small to actually feel this. But if it grows, decoupling the file system adapter and the domain logic would lose some of it´s simplicity.

For me a bus thus is more of a tool for distributed scenarios, where correlation overhead does not have such a big impact. But that does not mean I won´t use it in-process. As I said: if understandability is not hurt, use a bus for it´s added decoupling.

posted on Wednesday, August 28, 2013 10:09 AM

Feedback

# re: Bus or Flow? - Messaging done one way or the other 8/29/2013 5:21 PM Kevin
I think your code examples are broken. There is markup embedded that I'm assuming is not meant to be there. "<span class="skimwords-unlinked">" for example.

# re: Bus or Flow? - Messaging done one way or the other 8/29/2013 5:55 PM Ralf Westphal
Strange... I saw what you mean but once I reloaded the page it was gone. Don´t know how to fix it. And seemingly it´s not systematic, because I haven´t seen it at all in Chrome/OSX, but only in Safari/OSX.

# re: Bus or Flow? - Messaging done one way or the other 8/30/2013 12:49 AM James
Nice post. About the formatting, I had the same issue as Kevin in Chrome / Win8. Hit F5, all sorts itself out.


# re: Bus or Flow? - Messaging done one way or the other 9/4/2013 4:15 PM Franz K.
I would make the same decision.
But I still have an extra reason.
Since the UI can be also be separated from the application, for example, in a service, the bus is an advantage. The bus is easier to transfer to a socket or 'named pipe'.

# re: Bus or Flow? - Messaging done one way or the other 9/4/2013 8:04 PM Ralf Westphal
I agree. A bus makes it easier to distribute or introduce async processing.

That said, we should not go overboard by connecting just via a bus to keep all our options for distribution open. That to me would be premature optimization with regard to flexibility. We´d pay a price in terms of understandability.

Post A Comment
Title:
Name:
Email:
Comment:
Verification: