Clean architecture and object oriented principles
A lot has been written about object oriented principles and how they can be applied to write robust and flexible software. According to my feeling, a less has been written about places where object oriented principles are difficult or impossible to apply. Oh, there has been written enough about those places, like how to do apply this or that feature, but often it seems forgotten to note that you will have difficulties to write software there in an object oriented manner.
Read more: Clean architecture and object oriented principlesThere are many such places where basic things like encapsulation become impossible to do. To name a few of them: user interfaces, network interaction, storage access, ORMs. The thing they have in common? They’re all situated at the border of the system. To make this clear, I think it would be good to first explain what I mean by that.
Principles of Clean Architecture
If you want to have an application that is flexible in use, testable and robust, then it is a good idea to decouple the application itself from its dependencies. The technique for this is to put an adaptation layer in between the dependency and the application:
In fact, all recent modern architecture concepts promote this, although the details may differ. Examples of such architectures are Clean architecture, Hexagonal architecture and Ports and Adapters. They all put a lot of effort in separating the core from its dependencies.
In these architecture concepts, the dependencies (depicted by the arrows in the diagram) always point to the application core. This means that the Application has no knowledge about the actual dependencies or even the adaptation layers. The consequence of this is that it becomes possible to exchange the dependencies without touching the application itself. This corresponds also to the SOLID principles.
The Core can be object oriented
From this picture, it follows that the application core is the most ‘pure’ part of the code, in the sense that it is free of any restrictions that the dependencies put on them. You can develop using the pure object oriented principles here, free of distractions. (You can also apply the functional paradigm or anything else, you are free to choose.)
Most dependencies are data oriented
In contrast, most dependencies are about data that goes in and out the application. Take for example a database: it stores data and allows the application to retrieve or change it. Or take a user interface: although it is possible to receive events, what you’re showing is always converted to primitive data like strings and numbers. Ultimately, almost all dependencies are data oriented.
If something is object oriented and well designed, it has encapsulated its data to a large extent. This means that the data that an object holds is not visible from the outside. It is only possible to perform operations on the data using the methods that the class exposes. So access to the data is very much restricted.
If something is data oriented, there is no behavior, just data that is visible for everyone. Reading a text file from disk provides you with a set of characters without any behavior.
Clashing paradigms
This means that data oriented and object oriented are mutually exclusive, something can be one or the other, but not both. This has a profound impact, as it has also the logical conclusion that there is no automatic 1 on 1 conversion from data to objects. Hence also the adaptation layers, where the data from the dependency is turned into objects that the application core can make use of.
This also means that object oriented principles cannot hold in the dependency and adaptation layers. Since the dependency is data oriented, it is all about data structures. The adaptation layer must maintain those structures and create the appropriate objects for them, and also the other way around, create and update data structures based on the objects it receives from the application core.
Language features
Many programming languages that advertise themselves as being object oriented have lots of features that are intended for dealing with the borders of the system (the adaptation and dependency layers). For example:
- They allow reflection so that data structures can be filled automatically from json data
- They support properties on objects that can be written from WPF
- They support events so that objects can receive triggers from the outside world
In my opinion, those features do not belong to the essentials of the object oriented paradigm. The application core can easily do without. In fact, you can go further and remove many more features without touching the essentials. But when approaching the boundary of the software system, those features are often valuable or even necessary.