The Architect´s Napkin

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

My Links



Post Categories

Flow-Design Cheat Sheet – Part I, Notation

You want to avoid the pitfalls of object oriented design? Then this is the right place to start. Use Flow-Oriented Analysis (FOA) and –Design (FOD or just FD for Flow-Design) to understand a problem domain and design a software solution. Flow-Orientation as described here is related to Flow-Based Programming, Event-Based Programming, Business Process Modelling, and even Event-Driven Architectures.

But even though “thinking in flows” is not new, I found it helpful to deviate from those precursors for several reasons. Some aim at too big systems for the average programmer, some are concerned with only asynchronous processing, some are even not very much concerned with programming at all.

What I was looking for was a design method to help in software projects of any size, be they large or tiny, involing synchronous or asynchronous processing, being local or distributed, running on the web or on the desktop or on a smartphone. That´s why I took ideas from all of the above sources and some additional and came up with Event-Based Components which later got repositioned and renamed to Flow-Design.

In the meantime this has generated some discussion (in the German developer community) and several teams have started to work with Flow-Design. Also I´ve conducted quite some trainings using Flow-Orientation for design. The results are very promising. Developers find it much easier to design software using Flow-Orientation than OOAD-based object orientation.

Since Flow-Orientation is moving fast and is not covered completely by a single source like a book, demand has increased for at least an overview of the current state of its notation. This page is trying to answer this demand by briefly introducing/describing every notational element as well as their translation into C# source code. Take this as a cheat sheet to put next to your whiteboard when designing software.

However, please do not expect any explanation as to the reasons behind Flow-Design elements. Details on why Flow-Design at all and why in this specific way you´ll find in the literature covering the topic. Here´s a resource page on Flow-Design/Event-Based Components, if you´re able to read German.


Connected Functional Units

The basic element of any FOD are functional units (FU):


Think of FUs as some kind of software code block processing data. For the moment forget about classes, methods, “components”, assemblies or whatever. See a FU as an abstract piece of code. Software then consists of just collaborating FUs.

I´m using circles/ellipses to draw FUs. But if you like, use rectangles. Whatever suites your whiteboard needs best.


The purpose of FUs is to process input and produce output. FUs are transformational.


However, FUs are not called and do not call other FUs. There is no dependency between FUs. Data just flows into a FU (input) and out of it (output). From where and where to is of no concern to a FU.


This way FUs can be concatenated in arbitrary ways:



Each FU can accept input from many sources and produce output for many sinks:




Connected FUs form a flow with a start and an end. Data is entering a flow at a source, and it´s leaving it through a sink.


Think of sources and sinks as special FUs which conntect wires to the environment of a network of FUs.


Wiring Details

Data is flowing into/out of FUs through wires. This is to allude to electrical engineering which since long has been working with composable parts.


Wires are attached to FUs usings pins. They are the entry/exit points for the data flowing along the wires. Input-/output pins currently need not be drawn explicitly. This is to keep designing on a whiteboard simple and quick.


Data flowing is of some type, so wires have a type attached to them. And pins have names. If there is only one input pin and output pin on a FU, though, you don´t need to mention them. The default is Process for a single input pin, and Result for a single output pin. But you´re free to give even single pins different names.



There is a shortcut in use to address a certain pin on a destination FU:



The type of the wire is put in parantheses for two reasons. 1. This way a “no-type” wire can be easily denoted, 2. this is a natural way to describe tuples of data.



To describe how much data is flowing, a star can be put next to the wire type:



Nesting – Boards and Parts

If more than 5 to 10 FUs need to be put in a flow a FD starts to become hard to understand. To keep diagrams clutter free they can be nested. You can turn any FU into a flow:


This leads to Flow-Designs with different levels of abstraction. A in the above illustration is a high level functional unit, A.1 and A.2 are lower level functional units.

One of the purposes of Flow-Design is to be able to describe systems on different levels of abstraction and thus make it easier to understand them. Humans use abstraction/decomposition to get a grip on complexity. Flow-Design strives to support this and make levels of abstraction first class citizens for programming.

You can read the above illustration like this: Functional units A.1 and A.2 detail what A is supposed to do. The whole of A´s responsibility is decomposed into smaller responsibilities A.1 and A.2. FU A thus does not do anything itself anymore! All A is responsible for is actually accomplished by the collaboration between A.1 and A.2.


Since A now is not doing anything anymore except containing A.1 and A.2 functional units are devided into two categories: boards and parts.

Boards are just containing other functional units; their sole responsibility is to wire them up. A is a board. Boards thus depend on the functional units nested within them. This dependency is not of a functional nature, though. Boards are not dependent on services provided by nested functional units. They are just concerned with their interface to be able to plug them together.

Parts are the workhorses of flows. They contain the real domain logic. They actually transform input into output. However, they do not depend on other functional units.

Please note the usage of source and sink in boards. They correspond to input-pins and output-pins of the board.


Implicit Dependencies

Nesting functional units leads to a dependency tree. Boards depend on nested functional units, they are the inner nodes of the tree. Parts are independent, they are the leafs:


Even though dependencies are the bane of software development, Flow-Design does not usually draw these dependencies. They are implicitly created by visually nesting functional units. And they are harmless. Boards are so simple in their functionality, they are little affected by changes in functional units they are depending on.

But functional units are implicitly dependent on more than nested functional units. They are also dependent on the data types of the wires attached to them:


This is also natural and thus does not need to be made explicit. And it pertains mainly to parts being dependent. Since boards don´t do anything with regard to a problem domain, they don´t care much about data types. Their infrastructural purpose just needs types of input/output-pins to match.


Explicit Dependencies

You could say, Flow-Orientation is about tackling complexity at its root cause: that´s dependencies. “Natural” dependencies are depicted naturally, i.e. implicitly. And whereever possible dependencies are not even created. Functional units don´t know their collaborators within a flow. This is core to Flow-Orientation. That makes for high composability of functional units.

A part is as independent of other functional units as a motor is from the rest of the car. And a board is as dependend on nested functional units as a motor is on a spark plug or a crank shaft. With Flow-Design software development moves closer to how hardware is constructed.

Implicit dependencies are not enough, though. Sometimes explicit dependencies make designs easier – as counterintuitive this might sound. So FD notation needs a ways to denote explicit dependencies:


Data flows along wires. But data does not flow along dependency relations. Instead dependency relations represent service calls. Functional unit C is depending on/calling services on functional unit S. If you want to be more specific, name the services next to the dependency relation:


Although you should try to stay clear of explicit dependencies, they are fundamentally ok. See them as a way to add another dimension to a flow. Usually the functionality of the independent FU (“Customer repository” above) is orthogonal to the domain of the flow it is referenced by. If you like emphasize this by using different shapes for dependent and independent FUs like above.

Such dependencies can be used to link in resources like databases or shared in-memory state. FUs can not only produce output but also can have side effects.

A common pattern for using such explizit dependencies is to hook a GUI into a flow as the source and/or the sink of data:


Which can be shortened to:


Treat FUs others depend on as boards (with a special non-FD API the dependent part is connected to), but do not embed them in a flow in the diagram they are depended upon.


Attributes of Functional Units

Creation and usage of functional units can be modified with attributes. So far the following have shown to be helpful:

  • Singleton: FUs are by default multitons. FUs in the same of different flows with the same name refer to the same functionality, but to different instances. Think of functional units as objects that get instanciated anew whereever they appear in a design. Sometimes though it´s helpful to reuse the same instance of a functional unit; this is always due to valuable state it holds. Signify this by annotating the FU with a “(S)”.


  • Multiton: FUs on which others depend are singletons by default. This is, because they usually are introduced where shared state comes into play.
    If you want to change them to be a singletons mark them with a “(M)”.


  • Configurable: Some parts need to be configured before the can do they work in a flow. Annotate them with a “(C)” to have them initialized before any data items to be processed by them arrive. Do not assume any order in which FUs are configured. How such configuration is happening is an implementation detail.


  • Entry point: In each design there needs to be a single part where “it all starts”. That´s the entry point for all processing. It´s like Program.Main() in C# programs. Mark the entry point part with an “(E)”. Quite often this will be the GUI part. How the entry point is started is an implementation detail. Just consider it the first FU to start do its job.



Patterns / Standard Parts

If more than a single wire is attached to an output-pin that´s called a split (or fork). The same data is flowing on all of the wires.


Remember: Flow-Designs are synchronous by default. So a split does not mean data is processed in parallel afterwards. Processing still happens synchronously and thus one branch after another. Do not assume any specific order of the processing on the different branches after the split.


It is common to do a split and let only parts of the original data flow on through the branches. This effectively means a map is needed after a split. This map can be implicit or explicit.



Although FUs can have multiple input-pins it is preferrable in most cases to combine input data from different branches using an explicit join:


The default output of a join is a tuple of its input values. The default behavior of a join is to output a value whenever a new input is received. However, to produce its first output a join needs an input for all its input-pins. Other join behaviors can be:

  • reset all inputs after an output
  • only produce output if data arrives on certain input-pins

[Continue with part 2 of the cheat sheet series]

Print | posted on Saturday, March 19, 2011 6:10 PM | Filed Under [ Event-Based Components Software design Software modelling ]



# Deja vu all over again

Looks like you've managed to rediscover "Structured Analysis", popularized in Tom DeMarco's book _Structured Analysis and System Specification_ in 1978.

For a different view of where such things might fit into a system design, you might want to look at Stephen Mellor's _Object Lifecycles: Modeling the World in States_, in which objects have state machines, and each state has a data flow diagram. It's interesting precisely because it avoids nesting, which you seem to embrace.
11/24/2015 2:04 PM | Erick G. Hagstrom

# re: Flow-Design Cheat Sheet – Part I, Notation

"Functional decomposition" and "structured analysis" are different. The point is, that in Flow Design two important principles hold:

* If a functional unit integrates others it must not do anything beside that. This confines logic to the bottom of the hierarchy. (IOSP: integration operation segregation principle)
* Also, functional units communicate only through unidirectional messages and do not know each other. (PoMO: principle of mutual oblivion)

The "rediscovery" is of Alan Kay's notion of object orientation where messaging is core.

As for hierarchies: if you look closely there are no hierarchies of functional units which actually do something, which show behavior. Behavior producing logic is only in operations. Operations are not hierarchically organized.

What sits on top of operations are integrations. But integrations do not (!) contribute to behavior through logic of their own. Integrations are merely for humans to better understand a system. You don't want to deal with 1.000 functional units of any size ;-) Our mental models are limited. We can only hold so much in our minds at the same time. If this capacity is about to get exceeded, we have to abstract, i.e. introduce hierarchies. Try to find your way around in any supermarket without paying attention to hierarchies :-)

11/24/2015 3:11 PM | Ralf Westphal

# re: Flow-Design Cheat Sheet – Part I, Notation

Hi Ralf,

thank you for creating this page and your book. I am an Java Enterprise Architect, currently working as a Scrum Master and Developer. You got my attention through a presentation of one of your fans.

With PoMO I have the notion that messages are broadcasted to services. Then why would I need a split/fork?

I am asking because I am looking for a notation that is stupid simple and easy to understand. You have lots of elements and I fail to find the glue that binds cells with flows.

It will be of great help if you could clarify this.
1/20/2016 12:55 PM | Jelle van Wieringen

# re: Flow-Design Cheat Sheet – Part I, Notation

Hi, Jelle!

PoMO does not necessarily lead to broadcasting. It just means, producer and consumer don't know each other. Whether there is just one consumer or several is beyond the principle.

The split/fork is a means to change what's flowing. If producer P outputs (a,b) tuples, and consumers C1 just works on (a) and consumer C2 just works on (b), then we used to insert a split between P and C1/C2.

Since publication of this article the notation has become simpler, though :-) Here's what I would do today:

P -(a,b)|(a)-> C1;
P -(a,b)|(b)-> C2

The pipe symbole separates what flows out of a producer from what flows into a consumer. Technically this is usually trivial to implement:

var abTuple = P();

If you've more questions, please then me an email. This is my homepage:
1/30/2016 8:33 AM | Ralf Westphal
Post A Comment

Powered by: