Back

The Clean Architecture

MD Rashid Hussain
MD Rashid Hussain
Dec-2022  -  5 minutes to read

as described by Robert C. Martin (Uncle Bob) Philosophies over the architecture

  • Resilient in the face of changes
  • Maintable with increasing complexity
  • Independent of Frameworks - No direct dependency of external package to the business logic. The architecture does not depend on the existence of some library of feature laden software. This allows you to use such frameworks as tools, rather than having to cram your system into their limited constraints.
  • Testable - The business rules can be tested without the UI, Database, Web Server, or any other external element.
  • Independent of UI - The UI can change easily, without changing the rest of the system. A Web UI could be replaced with a console UI, for example, without changing the business rules.
  • 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.
  • Independent of any external agency - In fact your business rules simply don’t know anything at all about the outside world.

In the centre, we have entities, the primary concepts of your business.

Lets say, you are building an application for an insurance company you might have an entity like a claim or if you're building an entity for some company that sells products. You'll have entities like customer, order, product. These are all what we call entities.

If you're building a social media application you might have an entity like a post, for example or a comment.

Use cases are interactions between entities.

So if we're in our insurance company example you can say that you'd have a use case like filing a claim. Or for our retailer example you might have a use case like customer places an order.

Likewise in a social media setting you might have user posts a tweet.

Adapters are there to isolate our various use cases from the tools that we use so from the frameworks and drivers that we may be using to deliver our application and that's the final circle.

The final circle is all about these frameworks and drivers that we all use to build our applications.

The next thing you'll notice is there are these arrows pointing from the outermost circle down into the innermost circle and they go only in one direction. So those arrows signify the flow of dependency. They say that an outer circle can depend on an inner circle but an inner circle cannot depend on an outer circle, and that rule is the magic that makes this whole architecture hang together and work and deliver all kinds of value.

So if you think about it if the flow of dependencies goes from the outside in that means changes are gonna ripple from the inside out right. So if something changes in a framework or a driver since there's nothing depending on that framework or driver, the change is isolated to that one circle. On the other hand if something changes in an entity well the use cases depend on our entities and the adapters depend on our use cases and the frameworks depend on our adapters. The change has the potential of affecting every layer and so what we do is we put the things that are most likely to change over time on the outside and the things that are less likely to change over time on the inside. And that's what makes this architecture so resilient in the face of change.

Of course you might have noticed there's a problem with all this. If you've got a use case like place order for example customer places order, how is that going to work without the use case being able to access the database.

Because chances are, the order is gonna have to be saved into a database and so you would think that the use case would have to call our adapter and talk to our database which would invert the flow of dependency. The little sub diagram in the bottom right is all about this issue. We have got a controller and a presenter both of these are examples of adapters. And on the right hand side we've got our use case that has been split into three boxes. In the center we have the use case interactor and that's the logic that actually governs the use case. Then on the bottom we've got an input port and on the top an output port. You'll notice there's a tiny upper case "I" in the corner and so that tiny upper case I stands for interface that means that the use case input port is an interface that is satisfied by the controller, and the use case output port is going to be an interface that will be implemented or satisfied by the presenter. All you really need to be able to do is inject your dependencies rather than import them.

Conforming to these simple rules is not hard, and will save you a lot of headaches going forward. By separating the software into layers, and conforming to The Dependency Rule, you will create a system that is intrinsically testable, with all the benefits that implies. When any of the external parts of the system become obsolete, like the database, or the web framework, you can replace those obsolete elements with a minimum of fuss.

For more references, checkout this article by Robert C. Martin (Uncle Bob)