Bounded Contexts and Well-Structured Monoliths

We prefer well-structured monoliths over many microservices, except if the project is so big that it warrants a microservice architecture. We're often using this architectural pattern, that's why I am suggesting to ADOPT it.

Note: The following applies to server-side architectures.

How we structure our code

  • We use a Ubiquitous Language in the code base. If the Ubiquitous Language exists only in German (for german clients), we also name classes and properties in German. This is a little strange when reading the code sometimes ("getZulassung()" etc.), but we feel it is the smaller evil compared to ad-hoc translations of a specific business domain language.
  • We use a single Git repository for the whole project (until the point you want to re-use a specific part in another project – that's when we pay the price and extract the component into a separate Git Repository).
  • On the top level, we use different Bounded Contexts for e.g. User Interface, Core Model, Export/Import, ...
    • In Neos/Flow, a "Bounded Context" corresponds to a Package.
    • In Java applications, a "Bounded Context" simply corresponds to the top-level code namespace; e.g. [vendor namespace].[bounded context name]
  • There exists a document (README.md, package-info) which lists all bounded contexts together with a succinct description of why they exist and what their core responsibility is.
  • Inside each Bounded Context, we structure the files according to the Flow/Neos naming conventions (also in Java projects), as we have found this naming convention to be very readable and clear.

Well-Structured Monoliths vs Microservices

Projects we are developing rarely have the size that they, from the start, have to run in a fully distributed, decentralized manner, or that they are so big that more than ten people develop on it. That's why we usually start with a "well-structured monolith", where each bounded context should be as independent as possible. This needs some developer discipline (to not introduce shortcuts there).

If certain parts become bottlenecks and it is needed to scale out, we then might pull out the different bounded contexts into different microservices.

We're thinking of a Well-Structured Monolith basically as a set of microservices packaged together and deployed together in the same process.