Clean Architecture in KASKUS Chat

Takeaways of Implementing Clean Architecture in KASKUS Chat Android

Around July 2017, On Lee asked me to present a brown bag about Clean Architecture. At that moment, we were developing KASKUS Chat on Android platform. We’ve already had some share-worthy experiences related to Clean Architecture.

However, we didn’t want the brown bag to be just another sharing about Clean Architecture. There are already tons of them on the internet. We wanted the brown bag to be rich with real world examples. So we decided to delay it.

On September 2017, Robert C. Martin (Uncle Bob) released Clean Architecture: A Craftsman’s Guide to Software Structure and Design. The timing couldn’t be more perfect. We read the book voraciously and applied every concepts there as we see fit.

Finally, after a year, we decided to deliver the brown bag as a YouTube video. You can watch it on GDP Labs YouTube channel, or in embedded link below:

Clean Architecture Talk

If you’d like to see the slides, it’s on speakerdeck.

The following is a summary of the talk in case anyone prefers to read it.


Clean Architecture — The Big Names

https://8thlight.com/blog/uncle-bob/2012/08/13/the-clean-architecture.html

Most of you must’ve known about the above article. It’s pretty famous. It was probably the first time Uncle Bob coined the term Clean Architecture. It was released in 2012, so it’s 6 years old by now. However, it was not the first time someone came up with a name for an architectural style.

Before Clean Architecture, there was Hexagonal Architecture by Alistair Cockburn. Some people refer to it as Ports & Adapters. There’s Onion Architecture by Jeffrey Pallermo. There’s also Data-Context-Interface (DCI) and Boundary-Controller-Entity (BCE). And then, finally Uncle Bob came up with Clean Architecture.

Hexagonal and Onion Architectures

Interestingly, all of them — although differ in names and vary in details — have pretty similar characteristics. Those are [1]:

  • Independent of any external agencies, be it frameworks, user interfaces, databases.
  • Testable.

We will not deep dive into each of them in this article. Also, for simplicity we will only use the diagram of Clean Architecture from Uncle Bob as reference.

Software Architecture Patterns

Before we go even further, let’s clear out any potential confusion. There are a lot of diagrams in this world. In this article, we have seen three architecture diagrams so far (Clean, Hexagonal, and Onion). These diagrams focus on dependencies between components (dependency diagram).

On the other hand, we have these software architecture patterns from O’Reilly Software Architecture Conference 2017. These architecture patterns focus on the blocks (block diagram).

Software Architecture Patterns. O’Reilly Software Architecture Conference 2017

We will only discuss diagrams focusing on dependency. Specifically, only the Clean Architecture one.

Soft-ware

Software was invented to be “soft.” It was intended to be a way to easily change the behaviour of machines. If we’d wanted the behaviour of machines to be hard to change, we would have called it hardware [2].

So, every time you find yourself thinking that it’s hard to change your code, ask yourself this: “Am I making a software or a hardware?”

Now, how do we make software soft?

The way you keep software soft is to leave as many options open as possible, for as long as possible. What are the options that we need to leave open? They are the details that don’t matter [3].

Details That Do Not Matter

There are a lot of details (that do not matter): databases, I/O devices, servers, frameworks, drivers, medium (desktop, web, mobile).

Databases. For your architecture, it should not matter if you use MySQL or PostgreSQL.

I/O devices. It does not matter whether you use punch cards, magnetic tapes, hard-disk, or cloud.

Servers. Amazon Web Services, Google Cloud Platform, or even on-premise. It does not matter.

The bottom line is, you should not decide on those technologies at the beginning of development. Those kind of decisions should be able to be deferred until much later.

SOLID

Now, how do we keep our options open?

  • Single Responsibility Principle
  • Open-Close Principle
  • Liskov Substitution Principle
  • Interface Segregation Principle
  • Dependency Inversion Principle

The answer is actually pretty simple, we’ve heard it over and over again. We heard it to the point some of us have gone super-bored hearing this SOLID term. However, worry not my friend, we’ll do our best not to be just-another-article-about-SOLID.

Also, we will only talk about Dependency Inversion Principle and Interface Segregation Principle. We will not talk about the first three.

Dependency Inversion

Why is this principle so important? Let’s start with a simple example so we can grasp the intuition easily.

Intuition (Copy Program)

Copy Program [4]

Let’s say you have a requirement to copy characters from one medium to another. For example, copy characters from punch cards and paste it on magnetic tapes.

Now, take a look at copy program above. Please note that this code is not compile-ready. It’s about sending a message. Not about successfully compiling a code.

Take a look inside getchar function:

return STDIN->read();

Let’s trace it:

  • STDIN is a pointer variable, pointing to a struct named FILE.
  • STDIN is given the address of variable named console.
  • console is a struct variable of type FILE. It is given the open, close, read, write, and seek functions.
  • FILE struct itself contains signatures of five pointer to functions.

Please imagine that we have similar code for putchar function.

Now, this is the interesting part: we can swap STDIN with the address of another FILE struct variable! The way we read & write to punch card is different with that from magnetic tape, or disk, or even AWS cloud. BUT the copy program is still the same! The algorithm is the same.

It’s the same copy program but with different I/O devices.

Why do we bother to make these pointer to functions just for a simple copy program? Because we need the same program, the same algorithm, to run with different devices. Punch cards, magnetic tapes, floppy disks, compact disks, flash drives, and now the cloud.

The source code was written in C. It means the concept of Dependency Inversion has been there since 1960s, even in a non-object oriented programming language. The FILE struct acts as an interface and each I/O devices has their own implementation.

Using that FILE struct, we have made the I/O devices to depend on the copy program and not the other way around.

Copy Program Dependency Diagram

Dependency Inversion

The copy program above is a simple example to give you the idea of a more abstract and higher level concept that is Dependency Inversion.

Source code dependencies vs flow of control — Martin, Robert C. 2017. Clean Architecture — A Craftsman’s Guide to Software Structure and Design. Prentice Hall. Ch. 5. Figure 5.1.

Every program/application/system has these two kind of arrows: the dotted-green one showing the flow of control (or the flow of data in your system) and the red one showing the source code dependency. Usually, both of them are pointing in the same direction.

However, with the dependency inversion principle, we can invert the red arrow (the dependency arrow) every time we want. Everywhere we need it.

Dependency Inversion — Martin, Robert C. 2017. Clean Architecture — A Craftsman’s Guide to Software Structure and Design. Prentice Hall. Ch. 5. Figure 5.2.

We can do the inversion thanks to the power of polymorphism. In Java, you’ll use interface (or abstract class) to do it. In C, you do it by leveraging pointer to functions as described in the copy program above.

That’s the beauty of it! The concept of dependency inversion does NOT only apply to modern programming languages. Certainly not only for object oriented ones. It’s universal!

Benefits

We can invert the dependency direction every time we want, but what are the benefits? Why do we want to do it? For the copy program, we have seen the benefit: we don’t need to recompile the copy program each time we have new I/O devices. So what’s the abstraction of this? What’s the higher level benefits?

The database and the user interface depend on the business rules — Martin, Robert C. 2017. Clean Architecture — A Craftsman’s Guide to Software Structure and Design. Prentice Hall. Ch. 5. Figure 5.3.

With Dependency Inversion principle, suddenly you can develop your business rule independent of any user interfaces, independent of any databases. Remember? UI and databases are details that do not matter. The business rules are the ones that matter. If you can develop them independently, you can also deploy them independently.

Dependency Inversion on Apollo Spacecraft Design

And guess what? The concept of independent developability has also been there since 1960s! In the Apollo spacecraft design!

https://www.hq.nasa.gov/alsj/S66-05120.jpg

Minimize functional interfaces between complex pieces of hardware. In this way, two organizations can work on their own hardware relatively independently.

https://ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/19720005243.pdf

That’s independent developability! It’s there in Apollo spacecraft. Can you imagine? If the dependency inversion principle can be applied on hardware, there’s no reason we can’t apply it on any programming languages.

Library vs Framework

Let’s switch to another topic: Library vs Framework.

Observe this C++ program:

C++ code

Compare it with this ES6 snippet (using ReactJS framework):

ES6 snippet with ReactJS framework

So what’s the difference? What’s the relationship between your code, Library, and Framework? This diagram summarises it nicely.

https://www.programcreek.com/2011/09/what-is-the-difference-between-a-java-library-and-a-framework/

Now, this is the problem with definition. If we refer to the above diagram, it should be obvious that ReactJS is a framework. However, the official website of ReactJS clearly states that it is a JavaScript library.

https://reactjs.org/

We’re not going to discuss about that in this article. We’ll leave it up to you whether you think React is a framework or a library.

So, in a few words, the defining characteristic of a framework is Inversion of Control. The framework calls you, not the other way around. Then, from Inversion of Control, the term evolved to become Dependency Injection. You can check about them more on this and that article by Martin Fowler.

Inversion of Control is a common phenomenon that you come across when extending frameworks.
Indeed it’s often seen as a defining characteristic of a framework.
- Martin Fowler |
https://martinfowler.com/bliki/InversionOfControl.html

As a result I think we need a more specific name for this pattern. Inversion of Control is too generic a term, and thus people find it confusing.
As a result with a lot of discussion with various IoC advocates we settled on the name
Dependency Injection.
-
Martin Fowler | https://martinfowler.com/articles/injection.html

Theory vs Implementation

OK, that’s a lot of theory. It’s now time to share with you about KASKUS Chat implementation. I’d like to remind you that the implementation might not be as idealistic as the theory. When we go from theory to implementation, sometimes we have to be pragmatic.

Idealistic (left) vs Pragmatic (right)

Below is a picture to give you an idea about KASKUS Chat Android dependency diagram before and after we grasp the concept of Clean Architecture.

KASKUS Chat dependency diagram. Before (left) and after (right).

As you can see, it looks like spaghetti, circular dependencies here and there. The picture above was deliberately uploaded in low resolution. The goal is to show you the difference of dependency arrows between them. You don’t need to know the packaging name.

Now, since the very first beginning, we have been using the boilerplate code from android10 GitHub by Fernando Cejas.

https://github.com/android10/Android-CleanArchitecture

If we perceive the above diagram — the left one — as a dependency diagram, the correct arrow direction is B.

The boilerplate code from android10 was inspired by Uncle Bob’s diagram of Clean Architecture, so it should be safe to use it. If we use it, we’ll definitely conform with Clean Architecture, right? Well, it’s wrong. We learned that it’s really dangerous to use something that you don’t really understand yet.

KASKUS Chat Android dependency diagram 1st iteration

This is the first iteration of our dependency diagram. The left one is the diagram we think we have. presentation depends on domain, domain depends on data. The sad thing? It‘s wrong. Even sadder, the reality (the middle one) is even worse. Circular dependencies everywhere.

The correct dependency diagram is the right one. It was also our 2nd iteration. The domain is like the business rules. Presentation is the UI and data is the databases. The dependency arrow should point inward, into the business rules.

KASKUS Chat dependency diagram mapping to Clean Architecture

The above is a more complete view of our dependency diagram, compared to Clean Architecture diagram from Uncle Bob. From the inner most layer. We put entities and uses cases layer into one module (domain module). Then, presentation and data both belong in the same layer. Finally, the outer most layer (the details that don’t matter): Android Fragment, ORMLite library, SMACK framework, all of them are on the outer most layer.

All of them point inward. Domain doesn’t know about presentation and data. Subsequently, it also doesn’t care about Android Fragment, ORMLite library, or SMACK framework.

The Third Benefit

Previously, we know there are two benefits of Clean Architecture: independent deployability and developability. Based on our experience, we’d like to add one more benefit, using clean architecture, we will be able to easily swap/upgrade third party dependencies.

Easily swap/upgrade third party dependencies

This is one real example. In KASKUS Chat, we treat SMACK as a detail that doesn’t matter. We put it on the outer most layer. On domain module (or you can also say domain layer), we have this GroupChat interface. It contains the contract related to groups.

Then, on data layer, we implement that contract using SMACK library. Pay attention to the import lines. On domain layer, it’s only 6 lines. On data layer, it’s 35 lines. It makes sense, our domain layer doesn’t need to know about details, while data layer is where all the details are implemented.

The class from SMACK framework that we use to implement GroupChat is: smackx.muc.MultiUserChat and it is only imported in just a few class. In fact, in this case, it’s only in one class (SmackGroupChat).

One day, we decided to upgrade SMACK from 4.1.6 to 4.1.8. It contains breaking changes. The way it creates group is now different so we have to change the implementation. Had we not use Dependency Inversion principle, the MultiUserChat usage would have been everywhere and it surely would have taken much longer time to upgrade.

Another point of view: let’s say we deploy our system using cloud in AWS. When a cheaper alternative comes up (e.g. GCP), we should be able to swap our deployment system to GCP without touching the business rules. Because no matter where we deploy our system, our business rules stay the same.

Flow of Control

It’s this little little diagram on the bottom right corner of Clean Architecture diagram.

https://8thlight.com/blog/uncle-bob/2012/08/13/the-clean-architecture.htm

The details are quite lengthy. If you want to know more about them, we encourage you to watch the video starting on 18:56. Obviously, there are multiple ways to interpret this diagram. Our way is just one of the many. Your case might need different approach. The rule of thumb is to follow the dependency direction.

SDK

Previously we have discussed KASKUS Chat dependency diagram on 1st and 2nd iteration. Now, this is our 3rd iteration.

KASKUS Chat Android dependency diagram 3rd iteration

The background why we designed it this way was quite simple: we wanted to embed KASKUS Chat into KASKUS Forum app.

Now, as a great software engineer, we have to be able to understand the abstraction. The requirement is not just embedding KASKUS Chat into KASKUS Forum. It’s embedding KASKUS Chat into ANY application.

Moreover, there will be two kinds of application: one, application that will use whatever UI, whatever look and feel we have. Two, application that will use only our business rules because they have their own unique UI. The first type will use sdk-ui. The second type will use sdk.

We decided to separate them because the sdk-ui is quite heavy on storage and we don’t want application that don’t actually need our UI to depend on things they don’t need. Also, we kinda dog-food our own sdk and sdk-ui here. Our KASKUS Chat Android is treated as third party application. Its only concern is logging user in. Everything else is handled by sdk-ui.

To simplify the diagram, we put boundaries: SDK boundary and Core boundary.

KASKUS Chat Android dependency diagram 3rd iteration with boundaries

To simplify it even further, we combine presentation, data, and domain into one block and sdk-ui and sdk into another block.

KASKUS Chat Android dependency diagram 3rd iteration with boundaries. Simplified

It looks simple if we look at it from this point of view. However, to achieve this simplicity, there are a lot of complexities that we hide under the hood.

Well, designing an SDK could be another discussion by itself.

Thou Shalt Not Marry The Framework

…or shall you?

The term marry here means, your business rules depend on a framework, so you put the framework in the inner most layer. Based on Clean Architecture concept, framework is one of many details that doesn’t matter. So we should not marry them, right? Well, previously we’ve explained about being idealistic vs pragmatic. This is the other time for us to be pragmatic.

This is a real case on KASKUS Chat Android. Our domain layer depends on RxJava library. We got married to RxJava.

Marrying RxJava

This is a conscious decision, we decided to marry it. Why? Because making abstraction for RxJava is just not worth it. RxJava itself, in our opinion, is already abstract enough.

This is — again — another example that you should take everything with a grain of salt. Every system is different. It’s our job to decide which trade-off we have to take.

One Interface One Implementation

Is it overkill?

One Interface One Implementation

Some of you might have run into this kind of problem. You find yourself with a lot of blabla-Impl class. It’s quite a lot to the point you ask yourself: “there’s only one concrete class for each interface, why would I need to make the interface then? It’s so much hassle.”

This is one example, pay attention that we have LoginPresenter interface. It’s implemented in LoginPresenterImpl. If you look closely, they both are in the same package: presenter package. So are the others. It happened on KASKUS Chat Android first iteration.

So, is one interface one implementation overkill? The answer is: no it’s not overkill. However, if they’re on the same package, you might want to revisit your dependency diagram.

One Interface One Implementation

This is our second iteration. Interface GroupChat is only implemented in SmackGroupChat. It’s one interface one implementation all over again. But now, they’re in different package. The interface is in domain package, while the implementation is in data package.

If they’re on different package, you have to use interface (or abstract class) no matter what. Because if you put them on the same package, the framework has to be on domain package too. That means, you are marrying the framework unnecessarily.

You use interface (or abstract class) to achieve polymorphism, to be able to invert the dependency. So it will be data depends on domain, not the other way around.

How to Design Interfaces

Based on our experience, there are two approaches: bottom-up or top-down.

Designing Interfaces. Bottom-Up vs Top-Down.

For both approach: we recommend you to read open-source code. You will learn a lot from them. Then, for bottom-up approach: you create the concrete class first (the implementation). Afterwards, you create and adjust the interface. Then you make the unit test.

For top-down approach: you create the interface first based on your requirement. Then you make the unit test (make it fail if it’s necessary). Only then, you implement the concrete class.

For both approaches: you obviously need to do refactoring as necessary.

Interface Segregation Principle

When you already have your interface, you have to think if it could be simplified or not. This is where the fourth principle from SOLID comes in handy: Interface Segregation Principle.

Client should not be forced to depend upon interface that they do not use

This is a real example from KASKUS Chat.

Interface Segregation Principle

Previously (the left side) we have one interface ChatCredential with four contracts. Later, when we consume our own SDK, we realized that third party app does NOT need to care about these two methods: saveChatCredential and isLoggedIn. Third party app should only care about giving username and password (the token), we don’t care how they store it.

So we decided to apply Interface Segregation Principle and we split it into two interfaces. The ChatCredential is defined in SDK, implemented in third party app. While CredentialPref is defined and implemented in SDK. We make it into an interface just so we could invert the dependency direction (third party depends on our SDK).

Keeping up with the latest technologies

We’ll give you two examples. First, we used RxJava 1.0 on our first and second iteration for KASKUS Chat Android.

On October 29 2016, RxJava 2.0 was released. Considering the community will only support RxJava 1.0 until March 31st 2018, we decided to upgrade our RxJava library to 2.0. As you have known, we are married to RxJava. Therefore, the upgrade inherently requires a more elaborated effort.

Second, Android Jetpack. It’s a set of libraries, tools, and architectural guidance to help you build Android app. We haven’t taken a detailed look on this but if you have a plan to create a native Android app, you definitely should check it out.

The bottom line is: you should always keep yourself up-to-date. Software will not break physically, but it will surely deteriorate if we don’t maintain it. Also, as a wise man says: what we learn/decide today will be obsolete tomorrow.

Recapping

Let’s recap what we have discussed.

  1. {Hexagonal, Onion, DCE, BCE, Clean} architecture.
  2. Block vs Dependency Diagram.
  3. Software was invented to be soft.
  4. Dependency Inversion / Inversion of Control / Dependency Injection and its benefits.
  5. Flow of Control.
  6. Library vs Framework.
  7. SDK!
  8. Idealistic vs Pragmatic.
  9. One Interface One Implementation.
  10. Designing interfaces (top-down vs bottom-up).
  11. Interface Segregation Principle.
  12. Keeping up with the latest technologies.

Last but not least, as always, please use common sense. Every system is unique. Every system is different.


If the above story appeals to you, GDP Labs is hiring 1000 great software engineers in five major cities in Indonesia: Bali, Bandung, Jakarta, Surabaya, and Yogyakarta. Apply here.


[1] https://8thlight.com/blog/uncle-bob/2012/08/13/the-clean-architecture.html

[2] Martin, Robert C. 2017. Clean Architecture — A Craftsman’s Guide to Software Structure and Design. Prentice Hall. Ch. 2.

[3] Martin, Robert C. 2017. Clean Architecture — A Craftsman’s Guide to Software Structure and Design. Prentice Hall. Ch. 15.

[4] Martin, Robert C. 2017. Clean Architecture — A Craftsman’s Guide to Software Structure and Design. Prentice Hall. Ch. 15.

gdplabs

Founded in 2012, a software product development-centric organization based in Indonesia.

Thanks to Lutvianes Advendianto, Khandar William, Felix Kurniawan, and Fallon Candra

Timotius Nugroho Chandra

Written by

Software Engineer at GDP Labs & GLAIR

gdplabs

gdplabs

Founded in 2012, a software product development-centric organization based in Indonesia.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade