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.
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.