Flow-Design Cheat Sheet – Part II, Translation

In my previous post I summarized the notation for Flow-Design (FD) diagrams. Now is the time to show you how to translate those diagrams into code. Hopefully you feel how different this is from UML. UML leaves you alone with your sequence diagram or component diagram or activity diagram. They leave it to you how to translate your elaborate design into code. Or maybe UML thinks it´s so easy no further explanations are needed? I don´t know. I just know that, as soon as people stop designing with UML and start coding, things end up to be very different from the design. And that´s bad. That degrades graphical designs to just time waste on paper (or some designer). I even believe that´s the reason why most programmers view textual source code as the only and single source of truth. Design and code usually do not match.

FD is trying to change that. It wants to make true design a first class method in every developers toolchest. For that the first prerequisite is to be able to easily translate any design into code. Mechanically, without thinking. Even a compiler could do it :-) (More of that in some other article.)

Translating to Methods

The first translation I want to show you is for small designs. When you start using FD you should translate your diagrams like this.

Functional units become methods. That´s it. An input-pin becomes a method parameter, an output-pin becomes a return value:

image

The above is a part. But a board can be translated likewise and calls the nested FUs in order:

image

In any case be sure to keep the board method clear of any and all business logic. It should not contain any control structures like if, switch, or a loop. Boards do just one thing: calling nested functional units in proper sequence.

What about multiple input-pins? Try to avoid them. Replace them with a join returning a tuple:

image

What about multiple output-pins? Try to avoid them. Or return a tuple. Or use out-parameters:

image

But as I said, this simple translation is for simple designs only.

Splits and joins are easily done with method translation:

image

All pretty straightforward, isn´t it.

But what about wires, named pins, entry points, explicit dependencies? I suggest you don´t use this kind of translation when your designs need these features.

Translating to methods is for small scale designs like you might do once you´re working on the implementation of a part of a larger design. Or maybe for a code kata you´re doing in your local coding dojo. Instead of doing TDD try doing FD and translate your design into methods. You´ll see that way it´s much easier to work collaboratively on designs, remember them more easily, keep them clean, and lessen the need for refactoring.

Translating to Events

Translating FD diagrams to methods does not scale well. To reap all the benefits of FD you should therefore translate designs to Event-Based Components (EBC) as they were called originally. However this term is now deprecated, since the translation does not produce components in the sense of binary units of code. Nevertheless events enter the stage to hook together functional units.

Functional units

The favored translation results in a class for every functional unit with a method for every input-pin, and an event for every output-pin:

image

The default names for input-pins and output-pins are Process() and Result. You might find that strange since you´re thinking of classes as “things” with many responsibilities. In FD, though, classes can be very small with just one responsibility. And this responsibility is an action.

It´s not unusual to have a functional unit called “Read lines from text file” which then is translated into a class; why then should the class have a methode called ReadLinesFromTextFile()? Process() is sufficient.

If a functional unit has more than a single input-/output-pin the methods/events surely must have different names which are denoted by pin names:

image

Boards and parts do not differ in their basic translation. Both become classes (or interfaces, if you like). However, parts you implement yourself in some creative way. They are the workhorses. They contain the domain logic. Boards on the other hand could be generated – or are implemented by you without much thinking. Leave aside any creativity when implementing boards.

Boards do not contain (much) code in the input-pin methods. What they are doing happens in the constructor. Their sole purpose is to connect the functional units nested within them. Thats why input-pins are just delegating the work to be done. And that´s why all nested FUs are injected into a board using ctor injection:

image

With FD dependencies are (primarily) used to express nesting, i.e. different levels of abstraction. So a board is dependent on its nested functional units.

Within a flow, however, functional units do not (!) depend on one another. FU A does not depend on FU B or vice versa. Also X is independent of any other FUs preceding or following it. That makes testing extremely easy (see below).

Wiring

Wires in their simplest form are just event handler assignments:

image

This also makes it easy to translate split/fork:

image

Board input-/output-pins are treated a bit differently, though. That´s because they are connected to the same kind of pins, input to input, output to output:

image

Join

A join unfortunately is not that simple. You should create a small standard part to accomplish the task – or you use the join class from the ebclang project at CodePlex. ebclang is an effort to provide tools to help with Flow-Design. And ebcpatterns is a sub-project collecting implementations of parts to solve recurring problems.

Here is how you´d translate a join using the Join<T0, T1> class from ebcpatterns:

image

Sets of data items

Events can of course fire any number of times. So there is no need to distinguish between one output packet for an input packet and several output packets. Take as an example a functional unit splitting text lines into words:

image

One word or many… that does not make a difference for the translation of the output-pin to event Action<string>.

But there is a difficulty for any receiver of the output packets. Which word is the last word in a line? If several lines are processed and if it makes a difference to which line a word belongs, then a receiver has no way of associating a word with a line. The only way would be to send a special End-of-Line word as the last word of every line.

Fortunately there is another way of designing this. Just make it clear that output packets consist of several entries:

image

The star after the typename signifies the packet to be a list of data items. And a list is most simply represented by an IEmumerable<>.

Please note: This also works if the list contains millions of entries. Just don´t create an array and pass it along, but use an iterator (yield return) instead.

Explicit dependencies

Explicit dependencies are translated in a very explicit way. Instead of injecting them into the ctor an interface is used:

image

This has to advantages:

  1. Injection does not interfere with any other part of the implementation. It does not force a ctor or an additional parameter to the ctor, it does not require a base class.
  2. Injection is independent of object creation; injection can take place at any time during start-up of a Flow-Oriented application.

Entry Point

The entry point attribute is likewise translated to the implementation of an interface:

image

Configurable

Also making a part configurable is translated to the implementation of an interface:

image

Testing

Testing of functional units is easy:

With board you just do integration tests. Check only if the wiring is correct. Every path through a flow needs to be tested only once. Once you start using tools to generate boards these integration tests are not needed anymore.

With parts do unit tests as usual. Note that you don´t need a mock framework for that anymore because there are no dependencies between parts. Just pay attention to how to check the output:

image

You need to assign any relevant output event handlers before you call an input-pin method of the part. And don´t do the assert in the event handler because the test would go green even if the handler is not called.

Hosting – Putting it all together

Hosting the code created by this translation usually follows a pattern. It runs through a couple of phases:

  1. Build: Create all instances of functional units
  2. Bind: Wire-up the FUs by connecting output- to input-pins
  3. Inject: Inject explicit dependencies on all FUs implementing IDependsOn<T>
  4. Configure: Pass the command line args to all parts implementing IConfigurable
  5. Run: Call the Run() method with the command line args on the sole part implementing IEntryPoint

Build and Bind are put in a sequence to distinguish them; in reality, though, they are intertwined since binding happends also in ctors of boards upon creation.

image

Print | posted on Sunday, March 20, 2011 6:02 PM

Feedback

# re: Flow-Design Cheat Sheet – Part II, Translation

left by qammm at 5/28/2013 5:30 PM Gravatar
You write "Translating FD diagrams to methods does not scale well." and then suggest using a synchronous event based mechanism instead. I don't understand that. Could you please elaborate a bit more on what does not scale well with methods in the synchronous case? Why do I need synchronous events? Couldn't I just make 1 class with a single Process() method for every functional unit (circle in your diagrams)? Wouldn't that be simpler?

Something like that pseudocode in the Process() of the outer flow:
(result1, result2, result3) = new FU1().Process(par1)
result4 = new FU2().Process(result1, result2)
result5 = new FU3.Process(result3, result4)

# re: Flow-Design Cheat Sheet – Part II, Translation

left by Ralf Westphal at 5/28/2013 6:11 PM Gravatar
Using methods for the functional units (circles) is just fine. Use them whenever you can - but be aware that some functional units are not so easy to translate to methods. Then switch to classes.

Example: A functional unit with several inputs which can be used independently.

So the most general translation is to Event-Based Components (EBC) as shown in this article, where procedures represent input ports, and events represent output ports.

Please note: Neglecting that it´s not easy to return multiple results in many languages like you suggested (except by using special types), your approach does not solve the problem of outputing several data items via an output port (streamed output).

# re: Flow-Design Cheat Sheet – Part II, Translation

left by qammm at 5/28/2013 8:06 PM Gravatar
Maybe my pseudocode with returning tuples and destructuring multiple assignment was not so clear. How about the following (now compilable Java)? Wouldn't that be easier?

FU1.Result fu1Result = new FU1().process(args);
FU2.Result fu2Result = new FU2().process(fu1Result.result1, fu1Result.result2);
FU3.Result fu3Result = new FU3().process(fu1Result.result3, fu2Result.result4);

Basically that is the Function Object pattern with multiple values returned in the form of an inner result class (which in addition also can provide better semantic information than tuples with the usual tuple.getFirst(), tuple.getSecond()). The outer flow is basically managing the inner functional units, taking results from them and passing them to the "later" functional units accordingly. I like the idea of FD but I think I would have a hard time selling it when it only could be implemented well with events... :-)

What do you think?

# re: Flow-Design Cheat Sheet – Part II, Translation

left by qammm at 5/28/2013 8:21 PM Gravatar
With streamed output you mean results of the functional units being delivered not completely but incrementally, something like listenable collections where you get notified every time a new element of a result collection arrives? Is that a very relevant use case in today's business applications?

# re: Flow-Design Cheat Sheet – Part II, Translation

left by Ralf Westphal at 5/28/2013 9:47 PM Gravatar
"Streams" are very relevant. You want them at your disposal, even though it´s not the main way to output data.

Interestingly to me Java seems one of the languages making it more difficult to implement flows. C, C++, C#, JavaScript, Ruby, Scala, F#... they all make it quite easy.

Events are not an important language feature. But function pointers of some kind. Otherwise its hard to implement the observer pattern - because that´s what´s needed for "anonymous" outputs.

A function result outputs data anonymously. The function does not know where its output is going. But you don´t want to be limited to functions to translate functional units into.

This kind of anonymous output is one of the most important aspects of flow-design. Use whatever means your language offers you.

# re: Flow-Design Cheat Sheet – Part II, Translation

left by qammm at 5/28/2013 10:44 PM Gravatar
Thanks for your answers. I appreciate them a lot. :-)

I have been programming enterprise web applications in Java for more than 10 years and never came in contact with streams. And I have seen a lot of them. I would say for most web applications that is irrelevant. And I think the majority of programmers out there are building similar web applications without streams. No need for them to use events because they just don't need streaming. I would add that event complexity later only when I need it and where I need it. No need to carry that additional complexity around with me all the time IMHO.

AFAIK streams are really interesting in the Big Data area. But there are different approaches there and streams between processes running on different cores (similar to FBP) only being one of them for distributing computation across multiple cores. AFAIK other approaches exist in that area, too.

You are right: Programming observers in Java gets a bit verbose (boilerplate code) compared with C#'s built-in event support but of course it is doable. FYI: Java already has Method References via Reflection but they are cumbersome and slow. Java 8 will get real Method References but customers being very conservative in upgrading their Java version I expect not being able to use Java 8 for quite some time even after that has been released... :-(

I think the main difference between your approach and my approach is that in your approach the functional unit delivers the results directly to the result sink via events and in my approach the functional unit delivers the result indirectly (real delivery is done to the calling process which then actively forwards the result to the place where they would end up after your event would have delivered them). Of course the function unit itself in both approaches does not know where its results are going to. So when we have an article about how to translate FD into code maybe that is interesting. That is why I was mentioning it. The thing I like most about FD is that it puts the representation of behavior/function again at the core of the systems we build. I hate all that shotgun surgery required in legacy systems because one behavior is scattered all around different classes or hunting inheritence trees up and down to understand how a specific object behaves...

Keep up the good work but don't forget about Average (Java) Joe because there are lots of them. :-)

# re: Flow-Design Cheat Sheet – Part II, Translation

left by Ralf Westphal at 5/29/2013 8:55 AM Gravatar
"Streams of data" are interesting for three reasons: 1. when there´s lot´s of data, 2. when consumption of output should start before its generation has finished, 3. when generation is dependent on feedback from downstream (which is a special case of 2.).

I don´t want to judge who, where, when these cases are relevant. I´m just saying, they exist and need to be dealt with. That´s why functions with a single result as a way to translate functional units are not enough.

Events/continuations are an important means to encode flows. I would not want to use a language making it difficult to implement them.

That doesn´t mean you can´t do flow-design for Java. Here´s a blog devoted to that: http://blog.jebc.de/ (German).

In any case events or not: that´s implementation detail. What´s more important are principles. To me that´s the Integration Operation Segregation Principle (IOSP): A functional unit either integrates other functional units to a larger whole (Integration) or it is an Operation.

Operations contain control structures and expressions, Integrations do not. Integrations depend on other functional units, Operations do not.

To me violating this principle is the cause for much entanglement in today´s code bases. Logic (control structure and expressions) are smeared all over the place, vertically and horizontally in the common class/object hierarchies.

As you said it: putting behavior back at the core of software systems is the purpose behind flow-design. Behavior over data (or functionality over class) - to counter balance the wide spread approach to software design starting with hunting for (candidate) classes.

# re: Flow-Design Cheat Sheet – Part II, Translation

left by Marcus at 4/18/2014 10:11 AM Gravatar
Assuming that the dependency of an adapter is mandatory, why do you favour setter (method injection by means of IDependsOn) over constructor injection? Using constructor injection expresses that the dependency is mandatory. So what is most important reason to not use it? Can you clarify this, please? Thanks.

# re: Flow-Design Cheat Sheet – Part II, Translation

left by Ralf Westphal at 4/18/2014 10:32 AM Gravatar
Maybe you´ve overlooked it, but there are two bullet points stating my reasons:

"This has to advantages:

* Injection does not interfere with any other part of the implementation. It does not force a ctor or an additional parameter to the ctor, it does not require a base class.
* Injection is independent of object creation; injection can take place at any time during start-up of a Flow-Oriented application."

But then... I wrote this quite a few years ago :-) Today I´d use ctor injection, too, most of the times. It´s more lightweight and serves the purpose.

However, there are times when I want to be more explicit and clearly separate a configuration phase with regard to the flow from a construction/build phase. Then I use setter injection.

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