Nothing revolutionary there, I admit. But he did a better job than most of defining his terms and offering some best practices.
Mario started by describing his view of architecture as the process of abstracting solutions and experiences into different models. The intent is to identify the various areas of an application which can be their own module. The goal is always to simplify the application design. The example he gave was if a builder has to meet a requirement that a person standing a room should be able to view the outside the preferred design would be to put a window in the wall (simple and meets the need). A bad design would be to introduce a series of gears and supports which would hoist the wall up like a garage door. Sure it meets the need but it is overly complex.
So what, in his mind, is a module? He identifies a module as something which has certain attributes:
- Role - this describes the responsibility it performs in the system
- Seam - the visible or public interface of the module. This is more than just the API, however, which I describe later.
- Body - the hidden design parameters and implementation
- Test Bed - this determines how well the module works in an autonomous way without running the whole system. Based on what I saw it consists of the unit tests and various mock objects required by those unit tests.
The point Mario wanted us to take away from this discussion was that all serious mistakes are made the first day
- The most dangerous assumptions are the unstated ones
- The design must ensure a module has only one reason to change - the Single Responsibility Principle
- We need to group elements that are strongly related to each other while separating elements that are unrelated or have a conflict of interest
As I previously mentioned, Mario's discussion of modules were intended to apply equally to a System, Layer or Class. He defined each as:
- System - An autonomous processing unit which defines a business boundry. Services or applications are the usual examples of a system.
- Layer - the parts of a service or application
- Class - the units of programming which comprise a layer
- List of operations - the API
- Expected behaviors - what it's supposed to do. These become the core of understanding what tests can/should be written for the module. Use examples rather than formal statements (such as UML expressions). It is important to keep in mind we're talking about tests for the module so if the module is a layer the scope and nature of the testing will be different than testing for a class.
- Constraints - logic or requirements that define the prerequisites. Examples of these would be preconditions such as input validation or other guard conditions.
The best practices which Mario offered centered around doing the hard part first. So what's the hard part? Getting the interfaces correct. He believes the greatest leverage in architecting is at the interfaces. So the suggestion is to focus on the Seams to ensure the interactions work properly and clearly. This focus allows the natural boundaries between modules to become self evident. Then the "big ball of mud" can be divided up naturally - much like a log is split along the grain.
Mario advocates starting with the System Seam. For applications this means the UI and for services this would be contract-first development. Ensuring the System Seam is correct is the best way to ensure the user ends up with the experience they desire. Because the system is being written to satisfy the user's need it's the natural place to start.
The process he recommends following for defining the System Seam is decidedly low-tech but aligns with the Agile approach. You start with defining the User Stories/Use Cases/User Scenarios. From there you create what he called low fidelity mock ups - what everyone else would call screen layouts on paper. Nothing is cheaper than drawing on paper or a white board and asking the user, "Is this what you mean?" Once the low fidelity mock ups are solid (after iterative reviews and revisions) you then build high fidelity mock ups - actual screens with just enough behavior implemented to define the user experience but nothing real beneath it.
Once the high fidelity mock up is defined and is agreed upon Mario claims the natural seam between the UI and the business layer will be revealled.
He makes two further recommendations that apply if the user identifies further changes once the high fidelity mock up is started. First, we should welcome those changes. It may like our hard work is being dismissed or put aside but they are an opportunity to get closer to what the user actually wants and needs. Second, any changes should be defined and clarified by restarting with the low fidelity mock ups. For example, don't start moving buttons on a form and compiling - draw a picture of your understanding of the requested change. Remember, paper is cheap.
Part of the meeting got side tracked because Mario was asserting it is foolish to commit to delivering a finished system on a particular date. He believes the software industry needs to get to a place where we are making rational commitments. He believes we should only commit to the next step in the development process. For example, he believes we can only commit to having mock ups by a certain date. Once the mock ups are defined and agreed upon we can begin discussing when the next stage of module delivery might be accomplished. Some of us tried to bring the discussion back to reality - pointing out, for example, that sales folks can't sell screen mock ups - but Mario was insistent the issue is our industry needs to change expectations. He likened it to someone saying, "Here's a bunch of money to cure cancer. When will you have that done?"
I don't think I'll hold my breath for that, though.