Clean Architecture .NET Core (Part 1: Introduction)
What is Clean Architecture?
Does your code has following problems?
- Produce bugs often
- Debugging and/or adding new features is painful
- Cannot write tests without database/ web server
- Other developers cannot understand the intent of the code
- has presentation logic mixed with business logic or business logic mixed in with data access logic
Congratulations! you write bug laden code!, wait.. so there’s no solution for that? Well.. all you have to do is follow best practices. Like SOLID or any other design pattern. They all have the same objective, which is the separation of concerns. They all achieve this separation by dividing the software into layers. Each has at least one layer for business rules, and another for interfaces.
The secret to building a large project that is easy to maintain is this: separating the files or classes into components that can change independently of other components. Let’s illustrate that with a couple of images.
In image (a), if you want to replace the scissors with a knife, what do you have to do? Needless to say that it is a hectic job. In image (b), how do we replace the scissors? We only have to pull the scissors’ string out from under the Post-it notes and add a new string that is tied to a knife. Way easier. The Post-it notes don’t care because the string wasn’t even tied to it.
The architecture represented by the second image was obviously easier to change. The key rule behind Clean Architecture is exactly this, or more technically, the Dependency Rule, which states that source code dependencies can only point inwards. So what does this mean? Take a look.
Nothing in an inner circle can know anything at all about something in an outer circle. In particular, the name of something declared in an outer circle must not be mentioned by the code in the an inner circle. That includes, functions, classes. variables, or any other named software entity. The gist of this is simply that dependencies are encapsulated in each “ring” of the architecture model and these dependencies can only point inward.
Entities : Encapsulate enterprise wide business rules.
An entity is a set of related business rules that are critical to the function of the application. In an object oriented programming language the rules for an entity would be grouped together as methods in a class. Even if there were no application, these rules would still exist.
For example, charging 10% interest on a loan is a rule that a bank might have. This would be true whether the interest was calculated on paper or using a computer. The entities know nothing of the other layers. They don’t depend on anything. That is, they don’t use the names of any other classes or components that are in the outer layers.
Use cases : Contains application specific business rules.
They tell how to automate the system. This determines the behavior of the app. These use cases orchestrate the flow of data to and from the entities, and direct those entities to use their enterprise wide business rules to achieve the goals of the use case. Here’s a sample use case.
Gather Info for New Loan Input: Name, Address, Birthdate, etc.
Output: Same info + credit scoreRules:1.Validate name
2.Validate address, etc.
3.Get credit score
4.If credit score < 500 activate Denial
5.Else create Customer (entity) and activate Loan Estimation
The use cases interact with and depend on the entities, but they know nothing about the layers further out. They don’t care if it’s a web page or an iPhone app. They don’t care if the data is stored in the cloud or in a local SQLite database.
We do, however, expect that changes to the operation of the application will affect the use-cases and therefore the software in this layer. If the details of a use-case change, then some code in this layer will certainly be affected.
Interface Adapters : Convert data from the format most convenient for the use cases and entities.
It is this layer that will wholly contain the MVC architecture of a GUI. The Presenters, Views, and Controllers all belong in here. The models are likely just data structures that are passed from the controllers.
No code inward of this circle should know anything at all about the database. If the database is a SQL database, then all the SQL should be restricted to this layer.
Frameworks and Drivers : Where all the I/O components go.
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. It’s the most volatile layer. Since the things in this layer are so likely to change, they are kept as far away as possible from the more stable domain layers. Because they are kept separate, it’s relatively easy make changes or swap one component for another. (e.g. UI, database, frameworks, devices)
At the lower right of the above diagram (depicted below) is an example of how we cross the circle boundaries.
It shows the Controllers and Presenters communicating with the Use Cases in the next layer. Note the flow of control. It begins in the controller, moves through the use case, and then winds up executing in the presenter. How do we implement this? “Dependency Inversion Principle”.
Dependency Inversion Principle (DIP)
This is the D of SOLID. This means that less stable classes and components should depend on more stable ones, and not the other way around. If a stable class depends on an unstable class, then every time the unstable class changes, it will also affect the stable class. So the direction of dependency needs to be inverted. How is this done? By using an abstract class or hiding the stable class behind an interface.
So instead of having a stable class use the name of a volatile class like this:
You could make an interface that the volatile class implements:
For example, consider that the use case needs to call the presenter. However, this call must not be direct because that would violate The Dependency Rule: No name in an outer circle can be mentioned by an inner circle. So we have the use case call an interface (Shown here as Use Case Output Port) in the inner circle, and have the presenter in the outer circle implement it.
What data crosses the boundaries?
Typically the data that crosses the boundaries is simple data structures. You can use basic structs or simple Data Transfer objects if you like. Or the data can simply be arguments in function calls. Or you can pack it into a hashmap, or construct it into an object. The important thing is that isolated, simple, data structures are passed across the boundaries.
- We don’t want to cheat and pass Entities or Database rows.
- We don’t want the data structures to have any kind of dependency that violates The Dependency Rule.
Essence of The Clean Architecture can be summarized as follows. Classes that might change at the same time and for the same reason should be grouped together into components. The business rule components are more stable and should know nothing about the more volatile infrastructure components, which deal with the UI, database, web, frameworks, and other details. The boundary between component layers is maintained by using interface adapters that translate the data between the layers and keep the dependencies pointing in the direction of the more stable inner components.
In the next article we will try to implement The Clean Architecture with .NET Core.