Documentation

Documentation plays a fundamental role in our codebase: every entity --be it a case class, enum or type alias-- mirrors a ubiquitous language concept. To make sure that the code and the ubiquitous language always evolve together we decided that the code should be the only source of truth: every ubiquitous language concept should appear in the code and the code should not use words that do not belong to the ubiquitous language.

This way the code is the ubiquitous language: there are no additional documents describing the ubiquitous language that could go stale and have to be actively kept in sync with the code. Moreover, trying to change the code definitions forces the programmer to think about the ubiquitous language and discuss these changes with the domain experts.
Lastly, knowledge crunching with the domain experts consists in writing simple data structure definitions; in our experience, the domain experts quickly became familiar with the syntax and learned to "ignore" it to focus on the correctness of the definitions we were writing as we spoke.

One of our first meetings to knowledge crunch with the domain experts went like this:

Domain expert: ... a type of cheese is either Ricotta or Caciotta

Meanwhile, we write something like this:

enum CheeseType:
  case Ricotta
  case Caciotta

Domain expert: what is that enum word?

Programmer: it is just a way to say that a cheese type can be any of the following alternatives: so, just like you said, a cheese type is either a Ricotta or a Caciotta

This approach allowed for a tight feedback loop where the experts could look at the code and check that it actually mirrored their understanding of the domain.

Challenges

To use this approach we had to face a couple of challenges:

  1. The code describing the ubiquitous language had to be readable by the domain experts with little help from the programmers; this way it's possible to quickly skim through the code along with the domain experts to make sure it faithfully mirrors the concepts of the domain
  2. There should be a way to automatically generate a textual description of the ubiquitous language starting from the code, this way it is guaranteed to always be in sync with the code

As described in the "development choices section" the first problem was addressed by keeping the modelling of the domain concepts as easy as possible.

As for the second problem, we developed an sbt plugin named ubidoc that, by parsing the documentation generated by unidoc, automatically generates Markdown tables with the desired elements of the ubiquitous language.

Ubidoc

At its core the plugin extracts the scaladoc from classes, case classes, traits, etc. and organizes them into markdown tables as specified in a configuration file. This way we could enrich our description of the bounded contexts with automatically generated tables of the ubiquitous language concepts without the need to manually copy and paste the scaladoc. With this approach we managed to automate a tedious and error-prone task, obtaining an always up-to-date documentation.

⚙️ All the tables in chapter Bounded Contexts are generated by ubidoc

The ubidoc plugin has its own repository and was developed following the same approach as the core project. We paid attention to code quality and enforce the same checks, we adopted a workflow with semantic-release and conventional commits.

Generated byscaladoc
Made with ❤ by Giacomo, Nicolas, Nicolò, Linda
Mode
Back to top
In this article