If we build our entire paradigm on a single rule, we can keep it simple and make excellent models.
Being minimalist and being axiomatic means we can derive a set of rules from a single definition.
If we build our entire paradigm over one single rule we can Keep It Simple, Stupid and make excellent models and thus excellent software.
One of the most underrated quality attributes of software is the kind of being predictable.
Being predictable is almost nowhere on the top five design priorities.
We are going to make an exercise on object-oriented software design by stating just one principle:
Each domain object must be represented by a single object in our computable model and vice versa.
The relationship between objects of the model and entities of the real world is 1: 1
We can see the justification of this model in this article:
We will then try to derive design rules and heuristics from that axiom, of course without contradicting it.
We will see that most of the language implementations used in the industry ignore this rule and this causes enormous problems.
Invoking difficulties that arose in the construction of software 3 or 4 decades ago and that they almost no longer exist or are present in a few domains
We keep on making monkey decisions and beating each other without knowing the reason for such behavior.
Monkey and banana experiment
When building models of any kind we want to simulate the conditions that occur in the observed real world so that we can follow each element of interest in our simulation and stimulate it to observe the changes in the same way that they happen in the real world.
Meteorologists make mathematical models to predict and anticipate the behavior of climate and most scientific disciplines are based on these simulations.
With the rise of machine learning, we build black-box models to predict behaviors in real life.
In the domain of software and under the paradigm of objects we will always have one and only one object representing a real-world entity.
Let's try to prove by the absurd what would happen if we did not comply with the principles of being a bijection.
Case 1) We have an object in our computable model to represent more than one real-world entity. For example, many programming languages model algebraic measures using the only scalar magnitude.
Then we can represent 10 meters and 10 inches (two completely different entities in the real world) by a single object (the number 10).
We could add them together obtaining that in our model the number 10 (representing 10 meters) the number 10 (representing 10 inches) is equal to the number 20 (representing who knows what).
Bijection is broken
This generates problems not always captured on time. Because it is a semantic fault, the error usually occurs long after the failure as in the famous case of the Mars Climate Orbiter.
The probe exploded by mixing different units of measurement
Case 2) Our computable model represents the same real-world entity with two objects.
Suppose we have in our observable real world an athlete John Smith who competes in one discipline but who is also a judge in another athletic discipline.
A single person in the real world should be a single object in our computable model. We need to model just the minimum behavior to fulfill our partial simulation.
If we have two different objects (a competitor and a judge) that represent Jane Doe, we will sooner or later have inconsistencies by wanting to assign some responsibility to one of the two and not see it reflected in the other.
Jane Doe Is represented in our model by two different entities
Case 3) A bitcoin wallet can be represented as an anemic object (with some properties regarding address, balance, etc) or by a functional one (with responsibilities such as receiving transactions, writing to a blockchain, etc) but it's clear for someone who is not in software business they are related to the same concept. So the bijection must be held.
To solve these types of problems we must stop seeing entities as data structures with attributes, think of them as objects and understand that they are the same object fulfilling different roles depending on the context in which they are interacting.
Case 4) In most modern object programming languages, a date can be constructed by creating it from its day, month, and year.
We all learned that November 31st, 2020 can be created and that most of the languages will gently return a valid object (probably December 1st, 2020).
But this disguised as a benefit is nothing but error hiding, generating a coupling dependency to the design decision made by the programming language and hiding a sure error in the data load.
The error will raise when running a nightly batch processing these dates far from the root cause violating the Fail Fast principle.
In this article, we define the only axiomatic design rule that we will respect no matter what by justifying the problems that come with not respecting it and laying the foundations for future definitions derived from it.
Part of the objective of this series of articles is to generate spaces for debate and discussion on software design.
We look forward to comments and suggestions on this article.
Part of the ideas in this article was developed together with Hernán Wilkinson and all the members of the Software Engineering Staff on Universidad de Buenos Aires.
This article is published at the same time in Spanish here.