One of the biggest technological advances in this decade is the usage of machines hosted by server giants like Amazon and Google for our businesses, in the form of AWS and Google Cloud Compute. Not only do these companies offer machines, but they also offer specific services such as databases, service to send SMS, online development tools and backup services. These services are collectively referred to as PaaS or Platform-as-a-Service.
Over the last two years, another new concept has rapidly caught, mainly thanks to Amazon’s Lambda. We call this FaaS or Function-as-a-Service, where instead of running an entire software program or a website throughout the day, we simply run a single function, such as sorting a list of million names or converting the format of 50 videos from MPEG to AVI, etc on a remote machine which stays on for only the duration of the time that our function runs and then shuts down. By not keeping machines running all day, maintenance and operational costs go down significantly. This particular way of running machines for a specific short-term purpose and then shutting them down is now termed as ‘serverless’ architecture. Continue reading “Serverless architecture”
In the last article Introduction to clean architecture: Part 1, we saw how clean architecture is a set of principles for designing software such that the purpose of a software program is clear on seeing its code and the details about what tools or libraries are used to make it are buried deeper, out of sight of the person who views it. This is in line with real world products such as buildings and kitchen tools where a user knows what they are seeing rather than how they are made.
In this article, we will see how a very simple program is designed using clean architecture. I am going to present only the blueprint of a program. I won’t use any programming language, staying true to one of the principles of clean architecture, i.e. it doesn’t matter which programming language is used.
The simple program
In our program, we will allow our system to receive a greeting ‘Hi’ from the user while greeting him/her back with a ‘Hello’. That’s all we need to study how to produce a good program blueprint with clean architecture.
In our system, we have a single user who greets our system. Let’s call him/her the greeter. Let’s just use the word ‘system’ to describe our greeting application. We have just one case in our system which we can call, ‘Greet and be greeted back’. Here’s how it will look.
The greeter greets our system.
On receiving the greeting ‘Hi’ (and only ‘Hi’), our system responds with ‘Hello’, which the greeter receives.
Any greeting other than ‘Hi’ will be ignored and the system will simply not respond.
This simple use case has two aspects.
It comprehensively covers every step in the use case covering all inputs and outputs. It distinctly says that only a greeting of ‘Hi’ will be responded to and that other greetings will be ignored without response. No error messages, etc.
The use case also has obvious omissions. The word ‘greet’ is a vague verb which doesn’t say how it’s done. Does the greeter speak to the system and the system speak back. Does the greeter type at a keyboard or use text and instant messaging? Does the system respond on the screen, shoot back an instant message or send an email? As far as a use case is concerned, those are implementation details, the decisions for which can be deferred for much later. In fact, input and ouput systems should be plug-and-play, where one system can be swapped for another without any effect on the program’s core working, which is to be greeted and to greet back.
The EBI system
Once the requirements are clear, we start with the use cases. The use case is the core of the system we are designing and it is converted into a system of parts known as the EBI or Entity-Boundary-Interactor. There are 5 components within the EBI framework. Every use case in the system is converted to an EBI using these five parts.
Interactor (I): The interactor is the object which receives inputs from user, gets work done by entities and returns the output to the user. The interactor sets things in motion like an orchestra director to make the execution of a use case possible. There is exactly one interactor per use case in the system.
Entities (E): The entities contain the data, the validation rules and logic that turns one form of input into another. After receiving input from the user, the interactor uses different entities in the system to achieve the output that is to be sent to the user. Remember that the interactor itself must NEVER directly contain the logic that transforms input into output. In our use case, our interactor uses the services of an entity called GreetingLookup. This entity contains a table of which greeting from the user should be responded to with which greeting. Our lookup table only contains one entry right now, i.e. a greeting of ‘Hi’ should be responded to with ‘Hello’.
Usually, in a system that has been meant to make things easy, automated or online based on a real world system, entities closely resemble the name, properties and functionality of their real world equivalents. E.g. in an accounting system, you’ll have entities like account, balance sheet, ledger, debit and credit. In a shopping system, you’ll have shopping cart, wallet, payment, items and catalogues of items.
Boundaries (B): Many of the specifications in a use case are vague. The use case assumes that it receives input in a certain format regardless of the method of input. Similarly it sends out output in a predetermined format assuming that the system responsible for showing it to the user will format it properly. Sometimes, an interactor or some of the entities will need to use external services to get some things done. The access to such services are in the form of a boundary known as a gateway.
E.g., in our use case, our inputs and outputs may come from several forms such as typed or spoken inputs. The lookup table may seek the services of a database. Databases are an implementation detail that lie outside the scope of the use case and EBI. Why? Because, we may even use something simpler such as an Excel sheet or a CSV file to create a lookup table. Using a database is an implementation choice rather than a necessity.
Request and response model: While not abbreviated in EBI, request and response models are important parts of the system. A request model specifies the form of data that should be sent across the boundaries when requests and responses are sent. In our case, the greeting from the user to the system and vice-versa should be sent in the form of plain English text. This means that if our system works on voice-based inputs and outputs, the voices must be converted to plain English text and back.
With our EBI system complete to take care of the use case, we must realise that ultimately the system will be used by humans and that different people have different preferences for communication. One person may want to speak to the system, while another prefers instant messaging. One person may want to receive the response as an email message, while another may prefer the system to display it on a big flat LCD with decoration.
A controller is an object which takes the input in the form the user gives and converts it into the form required by the request model. If a user speaks to the system, then the controller’s job is to convert the voice to plain English text before passing it on to the interactor.
On the other side is a presenter that receives plain text from the interactor and converts it into a form that can be used by the UI of the system, e.g. a large banner with formatting, a spoken voice output, etc.
Being able to test individual components is a big strength of the clean architecture system. Here are the ways in which the system can be tested.
Use case: Since the use case in the form of EBI is seperated from the user interface, we can test the use case without having to input data manually through keyboards. Testing can be automated by using a tool that can inject data in the form of the request model, i.e. plain text. Likewise the response from the use case can be easily tested since it is plain text. Also individual entities and the interactor can be seperately tested.
Gateway: The gateways such as databases or API calls can be individually tested without having to go through the entire UI and use case. One can tools that use mock data to see if the inputs to and outputs from databases and services on the Internet work correctly.
Controllers and presenters: Without involving the UI and the use case, one can test if controllers are able to convert input data to request model correctly or if presenters are able to convert response model to output data.
Freedom to swap and change components
Interactors: Changes to the interactors are often received well by the entire system. Interactors are usually algorithms and pieces of code that bind the other components together, usually a sequence of steps on what to do. Changes to the steps does not change any functionality in the other components of the system.
Entities: Entities are components that contain a lot of data and rules relevant to the system. Changes to entities will usually lead to corresponding changes in the interactor to comply with the new rules.
Boundaries: Boundaries are agreements between the interactor and external components like controllers, presenters and gateways. A change to the boundary will inevitably change some code in the external components, so that the boundary can be complied.
UI: With a solid use case in place, you can experiment with various forms of UI to see which one is most popular with your users. You can experiment with text, email, chat, voice, banner, etc. The use case and the gateway do not change. However, some changes to the UI can cause a part of the controller and the presenter to change, since these two are directly related to how the UI works.
Controller and presenter: It is rare for the controller or presenter to change in their own rights. A change to the controller or presenter usually means that the UI or the boundary has also changed.
Clean architecture seperates systems such that the functionality is at the core of the system, while everything like user interface, storage and web can be kept at the periphery, where one component can be swapped for another. Hopefully, our example has given you a good idea about how to approach any system with clean architecture in mind.
Let’s say you want live sports, but you are at work. You are not allowed to watch the game on video, but there are several other options. One of them is to visit the event’s official website and use the section for live text commentary.
In text commentary, you will see a running log of the moments of the game. In football, the minute of the game serves as the lead, followed by a brief description of the action, e.g. “57′: Buffon saves a Ronaldo freekick by diving to his left.”. In cricket, you have the over and ball, followed by what happened, e.g. “17.4: Two runs. Kohli drives the ball to long-off, but Watson is in position to prevent the boundary.”
Not only sports, but other events like parliamentary sessions, seminars and board meetings are published the same way, e.g. “10:32 am: Mr. John Doe, Member of Parliament from Acme constituency, takes the mic.”
Let’s take this to the world of technology, with sensors recording the current temperature or an online analytics tool that watches every minute you watch a movie online, shop or read a blog. These tools record a minute-by-minute about what’s going on in the surroundings or what the user is doing on a website. In fact, minute-by-minute is an understatement and it is rather microsecond-by-microsecond. To help record this, the world of technology uses a special type of database called the time-series database.
Continue reading “Follow the trend, use time-series DB”
You probably know that a pressure cooker cannot be opened when it is very hot with plenty of steam built up inside. If you yank at the weight, it will protest with a loud hiss. But there are no problems opening the same cooker either before cooking or after it has completely cooled down. How can the same apparatus behave differently under different conditions for the same procedure: opening the lid? Software engineers will say that the cooker is using the state design pattern. Continue reading “Design patterns: State pattern”
After treating Adil, his doctor prescribes medicines in a complicated dosage. There are two medicinal tablets, one green and one red. The green medicine is to be taken 3 times in the coming week: Monday, Thursday and Saturday and the red one, 4 times: Monday, Wednesday, Friday and Sunday.
As Adil leaves, the doctor hands him two strips of medicine: 7 green tablets and 7 red ones. Adil is confused. He asks, “7 each? But doesn’t it call for 3 of these and 4 of those with a schedule?”. To which, the doctor replies. “How can I be sure that you won’t forget the complicated schedule? That’s why I have given you 7. Have one of each every day.” Adil is aghast. “But… isn’t that over-dosage?” “No, it’s not. 4 of the green tablets are simply mint candies. 3 of the red ones are strawberry candies. Inside the strips, the real tablets are interspersed with identical looking candies as per your dosage schedule. You don’t have to worry. Just habitually have one tablet of each colour every day. Start from the top of each strip.”
Brilliant! The doctor took a complex decision-making process away from Adil and just let him build a simple habit: one green tablet and one red tablet every day. The real tablets will fight against the illness that Adil approached the doctor with. The candies are there to simply … do nothing! In design pattern parlance, the doctor just used the Null Object pattern. Continue reading “Design patterns: Null object”
If you have ever visited a government office, you are probably directed from one counter to another to get tasks done. Why doesn’t the same person do everything? This is because the work is divided into small tasks and each government official is given the responsibility of only one task. Once done, that official will direct you to the next one. You are seeing the chain of responsibility design pattern in action. Continue reading “Design pattern: Chain of responsibility”
Have you ever noticed? If you say “Hi” to Jay, he replies the same. However Jyoti always replies, “What’s up!”. When you get angry and say “Shut up!”, the reactions are different too. Jay is calm, but firm. His reply is, “Hey, watch your word, buddy!”. But Jyoti loses it and says, “Shut up yourself, dumbo!”
What do car rentals and libraries have in common? They temporarily lend resources only as long as someone needs to use them and then take it back so that someone else can use them. In object-oriented programming, such as system is called the ‘object pool’.
Object pooling in real life
Let’s explore the philosophy of a library with regards to a book named ‘The Art of Computer Programming’.
No one needs to read this book 24 hours a day for 7 days a week all his/her life. People need books only for three purposes: learning, reference, review. A person most likely needs a book only for 40 hours in his/her entire life. He/she may need it for 20-25 hours during the learning phase, upto 8-9 hours per day. During reference/review phases, the book is needed for at most half an hour to skim over topics that are forgotten.
Instead of buying his/her own copy of the book, a person might as well borrow the book for the duration needed and return it.
A good library solves exactly this need. They stock up multiple copies of the most popular books and some more. Let’s call our library, the ‘Friend of Readers’.
‘Friends of Readers’ library estimates that they should stock up 5 copies of ‘The Art of Computer Programming’ on their shelves to start with. This is what they will call the ‘initial’ or ‘default’ level. If no one has borrowed this book right now, you will see 5 books on their shelves.
As readers start borrowing this book, the library waits and watches. However, they have set themselves a minimum. At any point of time, readers need to see at least 2 copies of the book on the shelf. So as soon as the number of copies goes down to 1, they will take action. They may either have more copies stored in a basement warehouse or they may get in touch with the publisher. Either way, new copies are arranged. How much should they replenish? Well, the library always strives to revert to their ‘default’ level.
Here is a scenario for clarity. The library has 5 books right now. One person borrows and the number of copies goes down to 4. The library simply waits. As more readers borrow, the number goes down to 3 and 2. The library still waits. Now if one more book is borrowed, the number of copies goes down to 1. The minimum level is breached and the library has to take action. They will arrange for 4 more books, so that they return to their default level of 5.
But about peak season? Perhaps there is an upcoming graduation examination for computer programming majors. Or some leading companies have announced a huge hiring camps. Candidates need to prepare for interviews. In such cases, the library will up the ante and stock more than 5 books. However, a library is a physical space and the number of shelves are limited. They have to take a call and set a ‘maximum’. At no point will they have more than 15 copies of the book in circulation. This include both the borrowed and in-shelf books put together.
As the peak season fades, the library will remove the extra books to free up space and scale back to their default level of 5.
So what exactly is object pooling?
Let’s use the library example to describe an ‘object pool’ system. In the object pool, there is a pool of objects that clients want to use. These objects are called ‘resource objects’. This is similar to the book ‘The Art of Programming’.
A pool manager object overseas the pool. The ‘Friends of Readers’ library is the pool manager. Just like readers do not buy their own copy of the book, the clients objects do not directly create a copy of the resource object. Similar to the readers approaching the library, the client object approaches the pool manager.
The pool manager maintains the default, minimum and maximum levels. The pool manager starts by creating the default level of resource objects. It does not allow the level to fall below the minimum. If the level is breached, the pool manager quickly scales back to the default level. During peak usage, the pool manager has to create more objects, but it never creates more than the maximum. At any point of time, there can be no more resource objects in circulation than the level mandated by the maximum.
Object pooling in software
Database connections take time to set up. Once set up, they consume memory and processor to stay put. In other words, database connections are expensive resources. Hence, in a big application, it does not make sense for every corner of the program to set up its own database connection and maintain it. Instead, there is a database connection pool. The pool manager sets up a fixed number of connections and allows other parts of the application to borrow a connection for use whenever the database needs to be used. There is a default number of connections to start with, a minimum below which the number of unoccupied connections cannot fall and a maximum number of connections to the database, both occupied and unoccupied combined.
On one hand, the effort and time required to set up database connections is taken away from the client and managed by the connection pool. On the other hand, the client doesn’t have to worry about disconnecting when done. The pool takes back the returned connection and gives it to another part of the application that needs it.
Pooling resources and the system of borrowing and lending them is an efficient way to own and use resources. The ones who want to use it do not have to worry about how to get it in the first place. They just need to walk into a place they trust will have it available for them. Also, since the users do not own the resources and keep them permanently, the pool gets back the resources and others can use them. This way, a less number of resources, consuming less space and requiring lower maintenance, can make the world a less crowded place for everyone.
What is common between a washing machine that has just completed washing and your secretary who tells you that she is done creating the annual reports? They are both using the observer pattern.
The observer pattern in real life
Using a washing machine plays out the following story. The user tells the machine to do the laundry. The machine performs the long, chugging task. When done, the machine tells you with a buzz. This is true of other appliances like the microwave oven or a toaster. Your smartphone does the same thing…. only it notifies you too much!
Not only appliances. Delegation of work to people works the same way. Efficient delegates will come back to you after they have done your work for you. Without them coming back, you would never know when the work is done.
Let’s imagine what would happen if your washing machine didn’t ping or if your delegate didn’t call you up to say that she’s done.
Without communication, you’d never know if the work is done or when it will be done. So you have two ways to know about the status of work.
One way is to be there while the work is being done and observe it all the time. No need to tell you how much a waste of time this is. Might as well do the work yourself. This is what happens when you fill your water bottle from an electronic water dispenser. The dispenser takes ages to start. Then the dispenser has such a thin spout that it takes at least 90 whole seconds before a 1-litre water bottle is full. If you have a 2-litre bottle, then God help you. The problem is that 90 to 180 seconds is too long to wait for water to fill, but too short to start any meaningful work. The other problem is that you have to turn off the water flow yourself when the bottle is full. If you don’t, then the bottle will overflow and water will be wasted. Not a good use of resources or your time.
Alternatively, you have to initiate the communication. That means interrupting whatever you are doing to frequently check up on the task’s status. This keeps wasting your time and resources. And you cannot focus on whatever is actually important to you. This happens when pasteurising milk on a gas stove. If you overheat the milk, it will overflow from its vessel and spill over. Hence the need to constantly check. But since it takes nearly 10 minutes for milk to boil, you can work on your important task for at least the first five minutes.
I have seen devices which solve both the problems effectively. I have seen water dispensers which have a dedicated 1-litre button, that dispenses exactly a litre of water and pings when done. You just keep your empty bottle under the dispenser’s spout and go back to your work. Likewise, I have an electric stove that slowly boils milk for 20 minutes at a certain temperature and then turns itself off with a beep. The milk is perfectly boiled at the end and there’s no spillage.
So, what’s the observer pattern?
Let’s use a domestic bread toaster as an example as we describe the observer pattern. Your spouse and you want to eat toast for breakfast. Your spouse puts slices of bread into the slot on the toaster while you switch on the button that starts the toasting process. Your spouse and you are the delegators. The toaster is the observee or delegate. The process of toasting has been delegated to the toaster.
Next, someone wants to know when the toaster is done. This is the Observer. One one hand, you yourself can be the observer, responding to the toaster’s ping. Otherwise you can tell another person, say your parents or children, to attend to the toaster’s ping. This person is the observer. Thus, the delegator may not always be the observer.
The Observee and Observer have a set of events which are agreed upon. This agreement is represented by an interface in object-oriented programming. See this post to learn what interfaces are. In our case, the observer wants to know when the toast is done. Let’s call this the onToastDone method. By terms of the agreement, the Observee (toaster) promises to call the onToastDone method on the observer (toaster sounds a ping) as soon as the toast is done. The Observer promises to receive the call and act upon it (removing the finished toast from the toaster).
Observer pattern in software
It is common knowledge that the speed at which data is read from a hard disk or from the Internet are much slower than the speed of a computer’s processor itself. The observer pattern is used to good effect here.
In almost any modern programming language, you will notice that calls to load files from the hard disk and Internet are based on the observer pattern. Your program will request the hard disk to load a file, but then move on to do the next thing. Internet requests are handled similarly. The hard disk or the network operation will later let the app know that the requested resource is loaded and ready for use. What would happen if the app were to wait for the hard disk or Internet to finish its job before moving on? You would have an ugly, irresponsive app that fails to respond to your clicks and taps. Happily, the observer pattern keeps your app responsive.
Delegation and observation help us focus on tasks that are important to us. They help us move on from time-consuming tasks and start the next one, keeping our day active. Please let me know interesting examples of observer pattern that you have witnessed.