This is the hardest part of software design. No wonder there are no clear cut rules on how to do it. You have to be both a domain and implementation expert to get the boundaries right on the first try.
I'm not so convinced that it's something you can get just by better communicating with "the business" either.
This is partly what infuriated me so about the extreme amount of code required to follow DDD patterns - 3-4x the amount of code means 3-4x the cost of that rewrite if you get the boundary wrong.
Doesn't matter if your code has a type named `Aggregate` in it. Matters if you get your consistency boundaries right.
> I'm not so convinced that it's something you can get just by better communicating with "the business" either.
I don't have a good answer for this. I personally try to keep my modules small so that there's not more than a ~week worth of stuff to redo if understanding of business (or business itself) changes. I often fail too.
I tended to find a lot written about the former and very little written about the latter.
In fact I found essentially zero practical or actionable advice about getting the boundaries right beyond just "it's important to get it right". DDD doesnt appear to have a coherent opinion beyond that.
Not in the books nor in blog posts written about the topic.
It reminded me a bit of how scrum had a lot to say about standups (the color of that shed was well defined) but very little to say about refactoring.
Examples:
Are you writing to disk? Printing to the screen? Saving to the database? This is a different "abstraction level" than your data processing, those things should be separated by a boundary.
Are you doing hardcore math to procedurally generate a terrain? It shouldn't be on the same layer you push the triangles to the screen.
It's also about isolation: you want to control the Framebuffers in your 3D renderer? Don't let the Framebuffer class with OpenGL calls ever leak outside the renderer, even though there's encapsulation. Just use a data class to communicate between renderer/non-renderer code.
--
However, the part that is not really discussed on these books is that we should be vigilant not to "over-abstract". Sometimes you have a certain level of abstraction distributed over two or more classes, only to mirror some kind of external structure or mental mode, but in reality you want the code for those things to be together in the same class/method.
One example I run across a lot in 3D renderers is wrapping internal 3D library concepts like "vertex buffer", "context" and "framebuffer" in separate abstract objects, even when they really don't need to be abstract.
For example: "Open GL renderer" will only ever be able to call "OpenGL vertex buffer", "OpenGL context" and "OpenGL framebuffer", while Vulkan will only call the Vulkan equivalents. This means you don't need a "framebuffer" abstraction, you can have it on the same layer as the renderer. You might need a data-only, non-abstract "framebuffer" class to control it from the outside, though.
To be fair, I've never really seen any process/paradigm address this. I've mostly done it based upon gut feel - sometimes abstractions seem like too much of an imposition. Other times it feels critical.
DDD just says "do way too much. all the time".
I imagine one day there will be an abstraction calculus but software engineering aint there yet.
Now imagine you need to encode behaviours for a system where domain experts use terms and jargon you've never heard before. Even worse, users of the same system coming from different departments use the same terms to mean different things. How do you draw the boundaries there? That's what the GP finds disappointing - there is no single guide or reliable process to jump into a new domain and get the boundaries right.
Same with programs.
Why is that a bad thing?
* Try to mininimize the overlap between bounded contexts - loosely coupling domain models.
* Default to "too large" to begin with and institute a pattern for breaking down a bounded context into two smaller contexts.
I tend to find any process that defaults to "have more conversations/interactions" defaults to wheel spinning without some sort of specific plan about what those interactions would entail.
What if this doesn't lead to a more accurate representation of the domain? What if two contexts are just coupled in the business, for good reasons?
> Default to "too large" to begin with and institute a pattern for breaking down a bounded context into two smaller contexts
This is exactly the ambiguous advice that you are rallying against. How do you know when to break down a context?
It's like, building any system involving many actors and actions is hard, that has nothing to do with software. We're just digitizing the same patterns and behaviors that people have used to run companies for hundreds of years.
People want a playbook to be followed to arrive at a "perfect" domain model or architecture. I'm sorry, that sounds pretty farfetched to me. It reminds me of how we first started thinking about computability theory, when David Hilbert proposed that we should be able to devise an algorithm that could decide the truth or falsity of any logical statement (the Entscheidungsproblem). Hilbert was one of the smartest mathematicians to ever live, and he was very confident that this could be done.
Well, Alan Turing, Kurt Gödel, and Alonzo Church (not slouches in their own right) all smashed that idea with various proofs. The truth can often be counterintuitive. I am sorry that the world is complex, I also wish it weren't so.
It's a critical topic that is right at the heart of DDD. I researched up and down on this topic and unlike 'where to use a factory" the DDD community refuses to go into even as much detail on this topic than I just did with my half baked comment.
I am not trying to "complete" DDD here. I think DDD should largely be consigned to the trash heap.
This is a very important question. I will bite and give a guidance because no one is willing to give advice here. If you development team grows over 10 SE. Then you need to split it. So your bounded context has a complete team on it becoming experts in the subject as well as experts in the software implementation. Each team should be able to work independently of other teams.
Is "larger than 10 software engineers" a rule that domain experts would suggest? Would that be in the ubiquitous language?
I ask because only ever heard personal opinions in answer to this question. I researched it heavily and found nothing. As far as I can tell it doesnt.
As far as what the goal of the art is - the goal is to avoid linguistic and semantic ambiguities in the ubiquitous language. There is even a section entitled "Recognizing Splinters Within a Bounded Context" where specific examples are given:
* duplicate concepts * false cognates
If you have truly duplicate concepts across contexts, this is a symptom of the lines not properly being drawn, and perhaps a new, shared context is missing.
False cognates are the bread and butter of bounded contexts though - these occur when two areas of a business use the same exact word for something, but they mean slightly different things _depending on the business context_. The example in the book given is the notion of a "Charge," which customer invoicing and bill payment departments might both use. But each department only cares about certain pieces of data of a charge, so if one "Charge" model were created, it would be more complicated because it had to worry about all the different ways that the teams use it. And even worse, sometimes they are used in _conflicting ways_. That is a semantic collision, creating ambiguity in the model.
This is what a bounded context is meant to address. Each department gets its own model, each with its own version of Charge. The code and data is fit for the specific business purpose it's serving, instead of having a one-size-fits all model that gets the job done, but is more complicated to use in all contexts.
Honestly curious, have you read the book? I still don't think it will give you what you're looking for in terms of a prescriptive formula for "doing DDD right," but there's quite a bit of guidance in there.
It's conceptually similar to answering the question "How do I know where the borders of Germany lie?" by saying "ask the first person you see if they speak German".
It also conflicted with a process I followed, which was to essentially create a team glossary and agree to semantically disambiguate terms which had multiple different meanings (e.g. linux user/website user instead of just user) and even just "ban" the usage of terms which got overloaded too much.
(I discovered that semantic collisions didn't just present problems in code, it often prevented you and your team from having coherent conversations).
This could, of course, then put everything we touched as a team into the same bounded context. Or not...?
>The example in the book given is the notion of a "Charge," which customer invoicing and bill payment departments might both use. But each department only cares about certain pieces of data of a charge
It sounds like they're essentially saying (not explicitly, but via assumption), that your software should follow conway's law.
Nonetheless, this example screams "bug alert" to me, since assumptions made by departments (and, as a consequence software systems) about what they should care about are where the really nasty bugs lie - frequently driven by misunderstanding between departments about terms (e.g. what counts as a user).
The ideas made sense on logically complex code that required frequent refactoring, but the strict separation between all the different layers simply led to a lot of code in most instance. Far too much.
We also couldnt agree on where the limits of the bounded contexts really lay. Most documentation on this issue is a mere handwave saying "you figure it out" or "it'll become clear when you do these exercises with the business" (it didnt), which is odd given how vitally important it is and how damaging it is to bound the wrong things.