Hey,

So, yesterday I tried reworking my StreamFlow workflow app into using 
the hexagonal architecture. So far I am extremely happy with the 
results. One of the things I have had big trouble with before is to 
implement the "TellDontAsk" principle. It seemed like no matter what I 
did I had to, in the end, ask for model information in various ways, 
thus showing all the inner details that I had been trying to encapsulate 
with my private mixins etc.

With the hexagonal architecture, where UI can be "at the bottom", and 
considered "output", this problem went away. Let me give you an example. 
In the app there is a search field and a search result view. In a normal 
layered app there would be a UI component that takes the search string 
and sends it to the application layer, and then presents the results. 
The app layer would have a method like this:
SearchResult search(String query);

This is very problematic though: first of all the search field has to 
know about the search result view, so they are coupled. If I then also 
want to update some other part in the UI the search field has to know 
about this too. Also, it is highly likely that once I get the result, I 
have to query the application for other things in order to present the 
result.

With hexagonal architecture this mess goes away. Since the flow is only 
"in->out" rather than "up-down-up", the application layer method becomes:
void search(String query);
The application layer performs the query. When it is done it then simply 
looks up all services that implement SearchObserver, iterates over them, 
and pass the result to them. This can be easily done with a SideEffect 
of the search method, and gives a good example of when to use 
SideEffects. The code is something like this in the SideEffect:
@Service Iterable<SearchObserver> observers;
@This Searcher searcher.
public void search(String query)
{
    for(SearchObserver observer: observers)
    {
       observer.refresh(searcher.searchResult().get());
    }
}
---
Since the app layer uses() the UI layer, one of the observers just 
happens to be the search result view, which presents the results. If 
there had been a status bar it could have also consumed the results and 
showed a message like "Found 14 matches". Or more like, a SearchStatus 
service would have Observed the search results, which would have 
produced the string, which is then in turn sent to StatusObservers, one 
of which happens to be the status bar.

If the search takes a long time, the UI would be in trouble with the 
first method, as it would essentially freeze when calling search. With 
hexagonal architecture the search(string) method can accept the string, 
return immediately, and then spawn off an asynchronous search that only 
when completed notifies the observers. The time between search and 
result can be quite long, but the UI will still be responsive in 
between, without the UI having to do the thread trickery. When consuming 
the results the UI does, however, have to ensure that it is on the Swing 
thread.

In any case, a key point is that the search field *does not have to 
know* how to present the results. All it does is take the string and 
send it to the application for querying. What happens then is up to the 
application and observers of the model that the processing changes. 
Input and output are separated in code, but still both are presented on 
the UI screen.

In this way there *is only TELL*, no ask. All events come from the 
outside, goes to the inside (through app->domain), and then goes out
again. And sometimes the initiator (UI) just happens to be the output too.

This would also simplify testing, as the call to the app and 
introspection of the resulting model using a mock observer is quite easy 
to do.

NEAT!

/Rickard


_______________________________________________
qi4j-dev mailing list
[email protected]
http://lists.ops4j.org/mailman/listinfo/qi4j-dev

Reply via email to