In my previous article I discussed why projects usually become un-maintainable. I will continue on this article to show how to design a system to limit coupling and thereby decreased the complexity and increase the speed at which software can be delivered.
Let me show you how to go from this.
To this using volatility based design.
In the image below you can see you still have coupling, without coupling you don’t have an application. The secret to having a sustainable project is to manage the coupling. You can see we still have classes and inheritance but they are contained within a service thereby making the system more modular and easier to work on.
The ultimate objective is to have an application that consists mainly out of micro-services where each service will have its own UI, and persistence. See The hidden monolith.
The architect’s job starts with a use case. Someone hands him a document with a bunch of requirements in the form of a user story which usually looks something like this. I got the example of a user story from another website, reference mentioned at the end of the article
As a ____, I want ___, so that ____
It defines the following.
- Who are we building it for, who the user is? — As a <type of user>
- What are we building, what is the intention? — I want <some goal or objective >
- Why are we building it, what value it bring for the user.? — So that <benefit, value>
If the architect is lucky he will receive a use case diagram with it. Similar to the image below. If a diagram is not present the architect will draw if out himself to more easily communicate the concept with others.
Study the requirements.
For a practical example I will use the following story.
As a administrator, I want to be able to import users accounts from a file in csv format through the website interface so that the users can access the existing functionality of the system. For large imports the system must notify the administrator once the import has completed. Users must be notified of their login credentials by email once the administrator has deemed the import successful.
The architect then uses this information and looks at the requirements and ask himself a few questions about the use case.
- Do I need more information?
- What technology?
- Concurrent load – many how concurrent users?
- Volume – how much data?
- Interaction type – does the actor want real time response or can it be delayed?
- Performance consideration
- Will this require scalability with time?
- Logical processes
- Process boundaries
- Identification of requirement volatiles
Until the first question no longer requires information the architect does not continue. The rest of the questions are answered in a architecture document called an artifact and it is specific to this single use case. This document will be used to communicate with the developers that will need to do the implementation.
In the example provided it is clear that.
- User will always be admin and is unlikely to change due to the nature of the task.
- Concurrency will be low as the import is done by the system administrator.
- Volume of data will be high. Could indicate which technology to use as well.
- The interaction type required is delayed. Meaning that the user will not wait for a immediate response. Indicates that a queuing technology can be used here.
- The client through which the import will be done is a website so care should be taken to not impact the performance of the website while a import is in progress. Also indication for a distributed service component requirement to potentially do the processing on another server.
- The source of the import is a file and the format is a csv file.Will need a component / utility to parse the file format.
- The location of the source can either be local or potentially on network drive.
- An assumption can be made that the user is authenticated and authorized prior to doing the task. May need to confirm.
- An assumption can be made that the users are being imported into a database. May need to confirm.
- Communication in email form must be sent to the administrator. Dependency on smtp server.
- Use case hides an additional sub use case relating to marking the import as successful.
- Communication in email form must be sent to the users. Dependency on smtp server. Possible high load on smtp server.
- The communication template itself can potentially change and is an indication of the requirement of a complete sub system relating to the creating and editing of communication templates. For this example I will be ignoring this to limit the complexity of the example.
Volitilies are those things that users are likely to adjust with time that we should encapsulate to limit the change through the system.
- The source of the file is volatile. Can potentially becomes Active directory server or network file server.
- The format of the file is volatile. Can potentially change to json or xml.
- The communication is volatile. Can potentially change to sms or other notification type.
Do the architecture
For this sample I will only show the static view and the call chain.
The static view
Now that the volitilies have been identified the next step is do the static view with the components identified. The static view indicates the layer and each of the component identified for this use case.
Take note that the items that have been identified as volatile are encapsulated in a block, so if they change only a single block will be affected and not the rest of the application.
The blocks can either indicate a Object or a Service as the design can be applied to Layered or Tiered architecture.
The call chain.
The call chain diagram is the one most used by the developers. It gives the static view and shows the interaction between the blocks. The diagram does not indicate sequence as that is a detail design consideration not relevant to the architecture design.
- The dash line indicates that the instruction will be dispatched and that the caller will not wait for a response.
- The solid line indicates that the instruction will be blocking and will wait for the instruction to complete.
Here we can see:
- The website dispatches the instruction to import to the user management block and does not wait for a response.
- The user management block will then interact with the DAO’s and Utilities to complete the use case.
So how is this limiting coupling and complexity?
This design limits the coupling by enforcing the following rules.
- Communication may only be done via a Interface / Contract.
- Contracts must be use case driven and not functional. A single call is made per usecase. There is no orchestration in the client. The client simply requests DoSomething().
- Each block my only communicate one layer down.
- All business logic must reside in the business manager block (yellow block)
- The only block that may interact with more than one block to do the orchestration is the business layer manager.
- The DAOs defined for this use case may not be re-used by other managers.
- Utilities are standalone and may not directly reference any other blocks.
- Any block at any layer may call a utility.
- Each block is assigned to a single developer.
This results in a use case driven design where it allows managers classes to evolve over time without affecting each other. Their volatility and change is contained. Reuse of blocks are limited to utilities. All the complexities are hidden within the manager.
How are the volities contained?
Each volatility is encapsulated into a blocks of responsibility. If a change occurs only 1 block, max 2 may be affected by the change.
- If the source of the import changes how many blocks are affected? 1 – the User Management DAO.
- If the format of the file changes how many blocks are affected? 2 – The User Management DAO and an additional utility for parsing.
- If the notification type changes from Email to SMS how many blocks are affected? 2 The Notification Manager and an additional utility.
This looks like a lot of planning, does it not slow development down?
The answer is no, the architecture and planning must not be seen as part of development. The Architect works separately from the developer, always planning ahead it will not impact the development cycle. By the time the developer gets his instructions all the planning is done and the instructions are clear.
Benefits of this method of development is.
- your projects can be planned.
- It is possible to more accurately estimate time required as you work with isolated blocks of code.
- your development cycles are quicker and more precise.
- resourcing is possible to calculate as there is a limitation of a single resource to a block.
- As time progresses for a project there are no limitations relating to coupling and complexity that will eventually decrease the rate of development.
- Code that has been developed will continue to function independently from the rest of the system regardless of change and age.
- At any point in time will a developer be able to refer to the architecture artifacts to gets an exact layout of the system.