Back

Everything belongs to users

Homo Sovieticus's mantra was "Everything belongs to the kolkhoz, everything belongs to me". In the context of this text I want to rephrase it to: Everything belongs to the app, everything belongs to users.

I often hear we can't modularise the app when everything depends on users. I hear it's a mirage, overcomplication in the name of self-indulgence.

I remember that once, back in the old sunny days, trying to visualise the team's problems, I took a piece of paper, drew big dots, labelled them with class names, and drew directional arrows between any two modules referencing each other. That was a spider's web!

Then, I realised life is about striving for a straightforward graph that doesn't look like that web.

Users glue all the pieces together, right?

But what to do with those users?

The problem is we can call whatever user-related a user.

➡️ User belongs to the complaints.

➡️ User belongs to the blood examinations.

➡️ User belongs to the shipment.

➡️ User belongs to the patients' list.

➡️ User belongs to the clinics.

If you see a folder structure in your project like on the Image 1, there is a significant chance that something is firmly off in this project.

Of course, folder structure is not an architecture, but often is an explicit manifestation of it.

Image 1. "We can't separate anything because all features link to the users".

It's a classical ubiquitous language problem.

Without a proper design phase we can't discover subtle differences between actual entities.

Business capabilities

Imagine we have patients registered in our app. Patients can be evaluated according to different test results.

Image 2. Two modules referencing a patient.

We defined two business capabilities - evaluation of test A or B results.

Test A needs an age, and test B needs a patient’s weight and height of.

However, none of these need to be aware of other patients' information. These modules link to a patient by their ID.

The evaluation modules have their own perspectives of what a patient is. And it's not something known from where patients come from.

There's a module that stores patients and contains information that, e.g. lets them log in or get profile details. It doesn't necessarily contain biometric data like age, weight or height. This data may come with test results, be obtained on-demand by a questionnaire, and be purposed only for evaluation.

"Common" is mostly bad

Let's picture creating explicit coupling between patients and the two evaluations having one model of patient and supplying modules with data about patients from one source (that model).

It's presented in the Image 3.

Image 3. Coupling by generalisation.

💡 By the way - patient.entity is hanging, it doesn't belong to any specific module. This situation is called orphan classes, initially described by Neal Ford. I wrote more about that here.

In fact, the two features are fueled by two projections of who the patient is and by what behaviours and properties are represented.

If we don't go with design reasoning and let our brain flow with its natural generalisation mechanism (thank you, evolution!), we will create a Frankenstein. Frankenstein is all needed by the two features.

It is a data and behaviour leak between the features.

I see this situation as breaking Interface Segregation and Single Responsibility rules on the architecture level. In this situation, we caused a domain language leak from one module to another (patients to evaluation A or B).

Also, the other modules don't need to know e.g. emails except the PII (Personal Identifiable Information) module.

Different capabilities, different users

With simple context separation, we increase the overall reliability of the system because we untangled different system capabilities:

➡️ Getting profile details / Logging-in patients
➡️ Evaluating A test results
➡️ Evaluating B test results

Look at the Image 4 that shows the correct design.

Image 4. Each "module"/"slice"/"feature" knows a different patient entity.

Also, look at the Image 5 below how the bad design is.

Image 5. Each "module"/"slice"/"feature" knows a different patient entity.

Each capability understands the patient differently, using the same identifiers to uniquely identify and link patients if needed.

Database also does matter

At the end, it is worth to mention that the coupling can be created also on the database level.

It's tempting to create some relations. Especially if we work with a single database. Then, you may think that it's beneficial to reference a patient from test "A" to patients from the PII module.

That'd make a physical relation between capabilities that can potentially force changes in module A/B because the "base" patient model changed.

You can read more about the database coupling here.

Outro

Adequately designed and modelled systems are nothing like a communist utopia.

Common is not good ☭ 🛑.

In properly designed systems, there are narrowed "sectors" of the overall project's domain knowledge.

Sure, those "sectors" are not entirely separated. It is not possible, and this is not the goal.

The aim is to build an effective development environment in which features can be successfully implemented in a reasonable time - in contrast to the (Distributed) Big Ball of Mud.