The following is the report of the second session of the Accounting Adventure.
The time is 27 April 2021 at 9.30 am. It is a beautiful day outside in Berlin: 18 degrees Celsius. Two views have been added to the Zoom screen that connects our software designer's minds. Damir and Jonathan joined the participants of the first session. Six of us today.
In this report, as always, I record the things that impressed me most, problems, solutions and feedback.
Software Design Adventures are serial workshops where I develop real software features. I explain how and why I make certain design decisions or how and why I apply certain practices. If you want to read what happened in episode 1 you can read my report here.
"Where were we?"
I start browsing through the slides of the Google Presentation that acts as a shared board for me and the participants. Each slide represents the content of my mental board at a particular point in the last session.
Browsing through the slides gives me the possibility to remember exactly what I had in mind at a certain moment. Through the board, I can share what is going on in mind with my students.
I start with the slides that represent Marc's requests. Marc is our fictional customer. There is one slide for each request.
To start with, I read the text of the request, where it is located in the business space - the Accounting System - and what output Marc expects me to generate.
The feedback that was received at the end of the fourth and last request confirms that the structure of the business is clear. Perhaps some requirements should be specified: in what format should the dates and amounts appear, with what alignment, etc. We take notes of these requirements in our spreadsheet.
We move on to the slides covering the collaboration diagrams.
The first collaboration diagram shows how the Report Maker selects the categories to be shown in the report and how the various Category objects collect the relevant Transaction objects on which the calculations for the reports will be made.
Collaboration diagrams "want" to be read carefully. I have always been reading them out loud.
After the workshop ended, someone asked: "Francesco, why don't you use a dedicated tool to make diagrams?"
The main reason is that a slide for me is a snapshot of what I have in mind at a certain moment. A slide gives me the freedom to express the content of my mental board: share it, review it, correct it.
Software Design is about dynamics. It is all about developing the behaviour of a software system. Design solutions show how to provide an outcome through a particular dynamic of message exchanges between objects.
Class Diagrams will be useful later on, to summarise what I'have learned from my collaboration diagrams. A kind of bird's eye view that shows me the overall structure of the design: the degree of strength of the dependencies between classes, the design patterns used.
Dynamics can be seen in collaboration diagrams, structure can be seen in class diagrams. That's why I use collaboration diagrams mostly.
From dynamics to structure. My design process proceeds from the particular to the general. From the concrete to the abstract. From objects to classes. From collaboration diagrams to class diagrams.
There's one thing I notice as I’m reading and rereading this diagram with the participants.
This collaboration diagram takes so long to read!
Why?
I realise that in this collaboration diagram there are clearly two different areas. In one area, some objects deal with the categories to be selected and shown in the report, and in another area, other objects collect the transactions to be used to generate the report.
I focus on the first area. The design emerging from the diagram suggests a particular business value that has not yet emerged from Marc's requests. Marc has given me a static list of categories in the order he wants them to be displayed. So far the categories Marc has given me are less than ten, and I can just create a list of them in the order that Marc asked for. But by reading the collaboration diagram I can imagine new requests:
"I would need a summary report with only Aggregate Costs and Aggregate Revenues." "Ah, and a detailed one, with the lowest level categories." "Ah, and one with all categories and subcategories of costs," etc.
I'm not anticipating these possible requests. I just notice that my current design already allows, in a spontaneous way, to handle these possible requests without having to modify anything in that area and in the area where the transactions are, to be used in the report.
That's what the current design is whispering in our ears.
"Just plug in a Strategy object capable of extracting the relevant categories in some way and we will have the automatic selection of categories."
The current design is somehow proposing business changes.
I need to note down this opportunity.
I create a Value Increment in our Use Case Diagram. I name it: "Sorted: Statically." I just want to remind myself and Marc of the "Sorted: Automatic" possible direction.
Then, I duplicate the slide with the collaboration diagram and arrange objects and collaborations so that in one slide I can see the object dynamics related to the Value Increment "Sorted: Statically" and in the second one the dynamics related to the scenario OK of the action Collect Report Transaction of the Use Case Generate Category Balance Report.
I'm not anticipating it. I'm updating the structure of the business. And I'm making sure that my mind can only focus on one design area at a time.
By applying an adaptive development process, these retroactions are common. From the technical side, reveals spontaneous, not intended or not anticipated business opportunities that can be obtained at low cost. Sometimes at zero cost.
Thanks to these opportunities the curve of the cost of change can decrease.
I go back to the slide named "@OK Salary: Generate Category Balance Report." I read aloud the path of messages exchanged among my objects.
I notice an interesting problem. To calculate the total debit of the various transactions collected, I could apply not one but three different design patterns: Visitor, Command, Strategy. Or, better, three variations of these patterns.
What are design pattern variations and why is it important to know them?
Let us imagine that we have to develop an arithmetic calculator capable of performing the sum of two numbers.
Let us limit to only two design patterns: Visitor and Strategy. (Of course we could consider each of the 23 design patterns.) Each of these two design patterns offers a really unique solution to implement the behaviour required by the calculator.
We have a Calculator object and two number objects to be summed: 2 and 3.
The Strategy Pattern will deliver the result by growing the behaviour of the Calculator object from the inside. A Client object will plug a SumCalculationStrategy object in the Calculator. And later, when the Calculator object will be required to provide the result of the operation sum(2, 3) (= 2+3), it will send a message to its current strategy and return its result. Keep in mind this dynamics.
How could we solve the same business request with a Visitor Pattern? A Client object would ask the Calculator to accept the SumVisitor and, if the Calculator object agrees, it will ask the SumVisitor to access itself. The SumVisitor, at that point, can access all the resources of the Calculator, get the addends, work its magic and obtain a number object result (=5) that, by the fact, the Calculator would not even know. The SumVisitor grows the behaviour of the Calculator object from the outside. Keep in mind this dynamics.
From the point of view of object dynamics, the exchange of messages that takes place in these two patterns is not only different - it is unique.
In my mind, when I think of these two patterns, I imagine a space of design solutions and two orthogonal axes: the Strategy Pattern is like axis X and the Visitor Pattern is axis Y. Between them there are an infinite number of pattern variations.
When I think of the 23 patterns described in the Gang of Four's book I see a space with 23 dimensions. Each design pattern represents a design orthogonality: one particular, identifiable, unique way of organising the dynamics between objects in order to achieve the same objective: to obtain valuable behaviour through a collaboration of objects.
In that space, not only we have the canonical versions of those 23 patterns but we also have a high number of variations of those patterns.
How many variations are there for each of these patterns: for example for a Strategy or a Visitor?
Dozens. "Guts" and "Skin" are two simple variations of the Strategy Pattern. Each pattern has their own variations. Even combinations of patterns have their own variations.
In an emerging design perspective, we want to adapt structures to functionalities. It is therefore clear that "listening to the code" or "listening to the objects in our diagrams", in our case, means looking for the pattern variations that best suit that particular context.
Sometimes, not even variations of a certain pattern will be suitable for a certain context but a variation of a combination of patterns will have to be applied!
When we apply a process where we want to "adapt structures to functionality? Well... it will be very uncommon to find ourselves applying a pattern in its "canonical" version. And if that were to happen to me, I would ask myself whether I wasn't actually "imposing that design pattern" on my system.
Imposing a design pattern means "adapting functionality to structure." That, to me, is the biggest mistake you can make in an emerging design process. We deliver clean code, all the principles of good design seem to apply but, inevitably, the complexity of the system increases unnecessarily!
If the complexity increases, it becomes more difficult to implement the next customer requirements. And translated into economic terms, the cost curve of change rises.
The attentive reader might have realised that the most well-known and popular design principles may not be in line with emerging design principles.
The board says: "Scenario @OK of the Use Case Generate Category Balance Report."
We are working on the collaboration diagram of a particular action of this use case: calculating the total credit, debit and balance to be used in the Report.
On the one hand, we have an AccountReport object, but on the other hand, we have an Account object that knows about two Transaction objects (two collected salaries of 1200 euros each). I would like to find an object to pass to the AccountReport object that allows me to obtain the credit total (=2400 euros) the debit total (=0) and the balance (=2400 euros).
Initially, I thought that the dynamics of a Visitor Pattern might work. Then, it seemed to me that perhaps the dynamics of a Command Pattern might work better. I was thinking of a Result Command object (a variation of the Command Pattern in which the command object performs an operation and stores its result in another object). I also thought that such a "result object" is also common in several variations of the Visitor Pattern.
I am attracted to a mix of the dynamics of the two patterns. I prefer that the Account object receives a DebitCalculatorCommand object and immediately executes it by passing itself (typical of the Visitor Pattern). At the same time, I like the idea of using Commands that can perform calculations not on Account objects, but on any amount. In general, it appeals to me that in order to get the various columns of the report (debit, credit, balance) it would be enough to add new objects.
I synchronise my mental board with the slide so that everyone, including myself, can have a clear view. In design, ideas and stories don't matter. What matters are messages and objects.
account.accept(aDebitCalculatorTransactionCommand);
And inside the accept method of the Account class:
aCommand.visit(this);
I highlight visit... and accept to remind me of the possible Visitor Pattern.
"What if instead of receiving this (an Account), Transaction objects were passed to it?" - I'm trying to move towards the hypothesis of Commands unrelated to the Account. They don't need to be related, and if I got to commands like "sum a list of numbers," I could use them quite often.
This issue of passing "this"or resources, reminds me of the dynamics of two variations of the Strategy Pattern: "Guts versus Skin". I took notes highlighting in blue on the Collaboration Diagram the possible "Guts" path in which the Context sends itself to the Strategy.
If I followed the idea of the skin variant of Strategy it would become:
aCommand.runWith(List<Transaction> someTransactions);
You might think that these are 'only' method names. No. Those method names suggest behavioural metaphors: orthogonal design directions.
And what we're looking for here is a particular variation of a design pattern or a combination of design patterns. Something that currently lies between a Visitor, a Command and a Strategy.
"There's also the case where the Account represents a Transaction Composite (Composite Pattern)." This would be a TransactionComponent...
I don't want to consider hierarchies at the moment. I always prefer a linear collaboration of objects if possible. It keeps the design more free to move than aggregations.
I deliberately leave the dynamics of request and counter request typical of the Visitor Pattern in the Skin version of Strategy (runWith(...)) to execute our Command... (variant with Result)
I introduce a new object... the SumResultCommandStrategy. This Command object acts as a Strategy for the DebitCalculatorCommand object but its nature is more that of a Command: it can sum up a List of decimal numbers.
Although it is based on a mix of variations, the design is emerging and now is possible to:
As you may have guessed, I spend a lot of time reading aloud and rereading my diagrams, making small corrections to the names of methods... I highlight objects and messages.
I am working in this way to show participants what is going on in mind while I am designing.
Do I always work this way?
Yes and no.
Usually, I don't have a Google presentation on which to draw Use Case Diagrams and Collaboration Diagrams. I do exactly the same thing but directly in my mind. Developing in the IDE.
Sometimes, when the design challenge is particularly hard, or when I feel tired, and my mental board seems to shrink and fail to hold objects and messages, or when I work in a team, I take my Google presentation and do exactly what we do in this Adventure.
The common aspect is that whether it is objects on a board or whether it is objects in an IDE, I spend a lot of time reading and rereading them, locating scenarios in the business space map, etc.
I'm not surprised now when I hear someone suggesting:
"When do we get to the code?".
The point is that, actually, we have been developing "code" on those slides.
A collaboration diagram is like a musical score for a musician. That score is already music.
Reading a collaboration diagram is the equivalent of a compiler running one statement at a time. With the advantage of being able to keep all objects under control. Instead of being in our IDE, in the trenches of a method and therefore with a limited perspective, we see our objects and messages from above.
Does this change the end result of the design?
Yes, it does.
There are at least two ways to apply emergent design.
The common element between these two approaches is that the objects we choose must be able to reduce the complexity of the system.
What is the most effective one?
The most effective way to let design emerge, to me, is to be able to get a bird’s eye perspective while working in the IDE, still staying in the trenches of a method.
Instead, the most ineffective way is to let design emerge while working in the IDE with a perspective limited by our staying in the trenches of a method.
If you don't have a high-level perspective in your mind, both with respect to the objects involved and with respect to the structure of the business, what kind of design decisions will you make?
The perspective from the trench of a method leads to a design that depends very much on the code written in that method. It seems to be clean, whereas it is actually reducing the adaptability of the system to change.
Mr. IF: "First do the simplest thing, put an IF! Then refactor it to extract method. Then refactor mercilessly: put a Template method to have a clean code... Then refactor relentlessly: Extract similar methods in Strategies."
Exactly, Mr. IF.
One Pomodoro to go, no code written. My participants have given up on seeing any code today.
Despite the fact that I repeated that "the collaboration diagram is programming" during the session, they don't seem to be very convinced.
I put the collaboration diagram on the right and my IDE on the left.
I start "copying" the collaboration diagram into my IDE following the indexes of the messages:
1.1: The salaryResult object of class Map receives the message create...
new Map<String, ...>
1.2 the salaryResult object of class Map receives the message put ("...
And so on.
Everything in the collaboration diagram will be implemented in the code.
We do this until point 1.4.1.4 of the diagram.
Then, I select my starter method, I run it and we get it.
The participants are surprised. They did not expect it. Some ask to see the code.
Legitimate request. Actually, the code is just the result of the decisions we made in our mental board.
On my Google slides, there are four boards dedicated to collaboration diagrams at this time:
Why did we start developing the third collaboration diagram?
Once again, the current design showed us a Value Increment. Another design to business retroaction.
The Account object is a container of Transaction objects. In that collaboration diagram, we use the Account object also as a result object. Some other objects have already collected the Transaction objects to be used for the report and accumulated them into this result Account object.
That said, in another collaboration diagram (number 2) we use Account objects when we import the raw transactions from the bank csv file. Marc told us how to map those raw transactions into accounts like "Books," "Romantic Dinners," etc. Account objects are the leaves of a Composite Pattern where the Composite is represented by a hierarchy of Category objects.
This means that we could use the third collaboration diagram not only to calculate the final Category report but also to calculate a different report based only on Account objects (one or many).
In other terms, we can deliver a report with: "Books," "Romantic Dinners" and "Salaries."
Since we don't "know" how to deal with hierarchies of categories yet, we cannot deliver a report for the aggregations of those accounts: "Costs for Fun," "Costs," "Income," "Profit," etc. We will develop that behaviour in collaboration diagram #2.
This seems to be another spontaneous and zero cost opportunity that allows us to simplify both the technical and the business systems.
What is the Value Increment?
The Generate Category Balance Report increments the new use case Generate Account Balance Report.
Let's update the map of the business domain:
It should be clear why it is important to always have this map in mind.
Looking at it we can consciously decide on which use case and which scenario to work on.
In this case, it is easy to choose to implement the collaboration diagram related to the use case Generate Account Balance Report. It provides value, it is simpler than the original Generate Category Balance Report, and it seems to be the core of the Accounting System domain.
Actually the most important aspect is not the decision I have just made. But the fact that I have made this decision in a conscious way.
I would say it was an intense session.
The feedback is very much the same from the team. They were impressed by:
For me the important points are:
We end the session with this observation of mine:
The next session will take place on 4 May 2021.