---
# System prepended metadata

title: Clean Architecture Facts

---


# So you want to come to coding and act like a big shot

> Tony Mendez: Yeah.
John Chambers: ...without actually doing anything?
Tony Mendez: No.

![a7e6ccdc-1be5-490e-b356-b9f6ea66ae49_text](https://hackmd.io/_uploads/ryDWmgA5T.gif)


## Clean Architecture is wrong

This is going to the world-first explanations on why it is wrong to begin with, how it becomes an anti-pattern, and gets promoted to absurdity without actually refactoring anything. 

## Clean Architecture doesn't need to prove anything

The funny thing is that I have to argue why it doesn't work but Clean Architecture doesn't need to provide any kind of proof or comparisons. "It just works". It is Clean because it says it is.

## Abstraction costs you

Let's start with the iconic dependency rules. Credits to [Clean Coder Blog](https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html)


![CleanArchitecture](https://hackmd.io/_uploads/BkZYEf056.jpg)

Now, can you spot the glaring problems of this design?
 
**It is the assumption that abstraction has no cost.**  

3 arrows from DB to entities mean 3 layers of abstraction so entity can pretend it doesn't know anything specific. When in practice, of course a dev has to know. 

You are not thinking *Oh I have these general rules that apply to general repositories that may be stored in anything.* That is not how business rules work. You need to know specifics like how or where exactly it is stored. You can hide implementation details using basic encapsulation without knowing any of these dependency rules. 

**Abstraction costs you. You lose info.**

Say you use a third party SQL library with API, now in order to carry out these abstraction, you lose the info of it being SQL, so you have to create an abstract interface that translates abstract query to SQL, and that is just to cross one boundary.  

Does Clean Architecture even mention *cost* at all?

See how it describes DB layer
>Frameworks and Drivers.
The outermost layer is generally composed of frameworks and tools such as the Database, the Web Framework, etc. Generally you don’t write much code in this layer other than **glue code** that communicates to the next circle inwards.
This layer is where all the details go. The Web is a detail. The **database is a detail. We keep these things on the outside where they can do little harm.**

I love how he casually mention glue code in something called "Clean  Architecture". You shouldn't have any!

The details of database is usually covered by third party library, it is the abstraction *Gateway* code that will do much harm. The common practice is to show you how easy to write tests for it so it becomes a feature not bug. But you shouldn't need to test it in the first place!

This kind of shit can't scale. Imagine repeating this for every other component in the picture.  

Here's a quiz for you to test your understanding thus far.  See that *flow of control* line at the bottom right?

Use Case Interactor has to know some inner workings of Controller and Presenter to facilitate the control / data flow. That is, you need to know some domain knowledge of outer circles and implement specifically for it in an inner circle. 

>The overriding rule that makes this architecture work is The Dependency Rule. This rule says that source code dependencies can only point inwards. **Nothing in an inner circle can know anything at all about something in an outer circle.**

But all the while you just pretend you don't know, it just so happens that you have this business rule that passes data from one abstract object to another. Then you print it and sell it like the best thing since slice bread. E.g.; 

>Independent of Database. You can swap out Oracle or SQL Server, for Mongo, BigTable, CouchDB, or something else. Your business rules are not bound to the database.

Which are completely worthless. I'm never going to swap out DB in the middle of running business, because if I do, some code changes will be the least of my concerns. In fact I need to sqeeze every benefits out of CouchDB for maximum performance. Again, abstraction is not free, and there's power in specialization. 

Let's summarize what we've learned from this simple test case:

It is not practical. Whoever designed this didn't bother comparing it to existing solutions and think he can work in an abstract vacuum without knowing anything specific. This is not how business rule works. 

## Stupid Over-generalization Layered In Duplication 

Ah yes. The *invaluable* SOLID principles.

I'll be the first person in the world to call SOLID anti-patterns.

Single responsibility is best explained with a joke. 

>What is the single responsibility of a view model?

Answer: to handle everything. 

See the problem? First the word responsibility is vague. It can have any interpretation. Second, if something does only one job, doesn't mean it can do the job clean.


To make things worse, unlike abstract principles like *keep it simple*, it encourages you to create more objects. OOP always wants you to create more objects. More objects != better. There are overheads and interactions. And with more object life cycles to maintain come with more hidden states, and more likely it gets modified since you inject them all the time. 

It actually makes you write dumb, highly coupled (more inter-connected reference type objects) codes that still handle everything. 

The idea of open-closed principle is nice. But how do you extend in Java? Abstracting it to a interface then implement a new class from scratch?

**Abstraction costs you.**

You will see this over and over again, because abstraction is considered key in injection and modularization. 

Implementing from scratch is just brute force. So you need default implementation, which is often in the form of inheritance, which has its own problems. 

This is where language features come in. What if default implementation *is* the interface? And you extend it by provide more default implementation?

The flow is not `class` -> `interface` -> `defaults` -> `inheritance`.

The flow is `defaults`. And extention adds more defaults without inheritance. 

You can't do this in Java. You can in Swift. 

It fits the narrative in Java because you are abstracting everything already. But you can do extension without abstraction.

Liskov substitution principle is downright useless. 

The problems with inheritance is that you inherit too many hidden shits. Any changes from any of the parent classes affect you.

None of these are fixed by this principle. This is such a filler to sell tutorial. 

Oh, and how do you test if this principle is followed? Do you ever check it? 

How does Swift do this? Introducing value type that can't be inherited! Which can be enforced by compiler! 

Interface segregation is complete dog shit. We write library functions, helper functions, short-cuts all the time that other devs can choose to call. Yes you can extend it if you need it, but it doesn't hurt that I extend it for you in case you need it.

The point is that you can't possibly know how people interpret your interface, especially when you are so proud of Clean Architeture that you don't write documents. There will always be functions that nobody called. If something is not called by a person, doesn't mean it won't get called by another person, or the same person at later time. 

Before we discuss the final principle that *solidify* the acronym, let's look at the official example from google.


## This is not clean

This snippet comes from [Android devs](https://developer.android.com/topic/architecture/domain-layer?hl=zh-tw)

```kotlin=
class GetLatestNewsWithAuthorsUseCase(
  private val newsRepository: NewsRepository,
  private val authorsRepository: AuthorsRepository
) { /* ... */ }
```

Of course Google promoted it as *Clean* after simplifying it to 3 layers: data - domain - UI. 

**This is dog shit.** 

Typically `newsRepository` will be some json at url endpoint like `/news`; same with `AuthorRepository`: `/author`


Before dark times, before *clean*, we have this ancient technique called "parameters". E.g.; 

`api.fetch(endpoint)`

so we don't need to create one new type for each endpoint. 

**This is brute force with even more steps.** 

It may seem like generalization, but you can tell it's network fetch just from experience. 

It hard-codes to specific url endpoint but pretends to be abstract repository at the same time. You will never swap out `NewsRepository` to anything else, because again, from experience, how to fetch from back-end is fixed. 

More importantly, remember this?

>Nothing in an inner circle can know anything at all about something in an outer circle.

Here we have a use case that knows nothing about networking! That repository can be anything! But somehow it knows that repository is about `news` and `author`!

As I said, this is not how business rule works. You are just pretending to work in an abstract vacuum. But you know before hand all the required details. 

Now I'm going to do another world-first. I'm going to **compare it to some basic solution.**

```swift=
func getLatestNewsWithAuthor() async -> JSON {
    let json1 = await api.fetch("/news")
    let json2 = await api.fetch("/author")
    // ... processing
    // return some json
}
```

Note that I write in Swift syntax because I'm an iOS dev. 

Do I hard-code `/news` and `/author`? So do you! You even code it in class name!

I can easily refactor them out to static constants while you can't!

I use 0 extra classes, call `fetch` from existing service that is considered to be tested and ready.

If you argue that you can swap `NewsRepository` to `AnotherNewsRepository`, I have news for you.

I can just add an extension to network service so that it becomes
`api.specialFetch("/news")`. Because that's what you are going to do in `AnotherNewsRepository` isn't it? Some changes to how you fetch. 

Again, even I know the implementation details that you are going to  do in a supposed-to-be-abstract setting. Yet you are somehow slower to make changes with all these extra layers. I still use 0 extra classes! And extensions are way easier in Swift!

Still think you don't know anything at all about outer circle?
>Nothing in an inner circle can know anything at all about something in an outer circle.

And even if you don't, you are still slower. 

Do I use any fancy techniques? Just variables and pass parameters. You need to beat this before claiming anything remotely resembling *clean*. 

Oh and your "getter" use case has no explicit return type. 

What does all these mean? It means you are **over-generalizing**.

You know everything from outer circle; You use `class` to do function's job. You don't use `variables` for some reason. Instead, you create a new type for every variable. 

It's not every day that you can watch Google official guide promotes dumb shit brute force. Oh wait...

By the way, people credit Clean Architecture for this ingenious data-domain-UI architecture. Google advertised it as "best practice".

>This guide encompasses best practices and recommended architecture for building robust, high-quality apps.

Rename domain to control and you get the usual MVC. But you never see articles promoting MVC for building robust, high-quality apps.

## You should NOT depend on abstractions

>High-level modules should not depend on low-level modules. Both should depend on abstractions.
Abstractions should not depend on details. Details should depend on abstractions.

We are all familiar with so-called dependency inversion principle. 

This is the key step towards *Stupid Over-generalization Layered In Duplication*.

The devil is always in details. Let me ask you:

* What should be a module?

Is view a module?

Is every caller high level module? 

Is every callee low level module?

* What exactly does depend mean?

If I just want to hard-code some shit, does that count as *depend*?

Rememeber we just beat Google best practice with some hard-coded shit. It doesn't seem like too much a problem if you don't follow this rule.

The ambiguity is then amplified to absurdity. i.e.; you can't have anything hard-coded. Everything *depends* on everything else. 

The result is that you hard-code it somewhere else like class name like Google did there; and we can't create a view without examing its *recursive* dependency hierarchy first.

* Does `NewsRepository` depend on details?

This is Google's usage:
```kotlin=
val news = newsRepository.fetchLatestNews()
```

How is this different than 
```kotlin=
val news = api.fetchLatestNews()
```
`newsRepository` is even more hard-coded than `api`. It depends on a very specific endpoint / data type. 

But somehow we can't just *use* network service. We need some abstraction because details can only depend on abstraction.

No, you just show details can be hidden with basic function API. How `fetchLatestNews` is implemented is hidden, and if you want to change it you need a `AnotherNewsRepository` but you hard-code it to `NewsRepository`! So you end up in the exact same place!

Also, this isn't 2004. We have functions as first-class citzen now, meaning they could be passed around like parameter. It is not risk free, but at the minimum you should mention it. 

Therefore, we have a very strong rule defined by very vague terms that should be applied to every module which is basically every object without room for any other approaches like passing regular parameters or hiding details using function APIs. 

**If your theory is so right, why is your code so wrong?**

If Google can't get it right, what are your odds?

Remember encapsulation? Before dark times? Before *Clean*?

How do you waste so much overhead with this much abstraction just so you can do worse than some basic encapsulation shit taught in programming 101?


## If it doesn't work on small projects, it won't work on large projects


Imagine this sales pitch.

>This architecture can help you build robust high quality apps but it requires much boilerplate. 
But don't worry, it will work on larger projects. 

If your immediate response is not **you are full of shit**, then I got an architecture to sell you.

Now I tell you that its name is Clean Architecture. 

OK. All is good now. If you say it is clean, then it must be clean.

Right?

If you have boilerplate in something called *Clean*, you've already failed. 

---

### There are more facts to come

I'm testing water on the platform. See if I get banned with this level of expression. 

If this gets some visibility on search engine, I'll add more to share the fun. 
















