Safeguarding the Core Domain

I will keep talking about the core domain where all the rules are strictly enforced and any violation results in an exception. The core domain can be seen as an electric toy car without a cover and all the shields protecting it from the external world.

You will see all the wires are dangling and electric elements sticking out. It is not meant to be exposed to the external world without protection. The whole idea is to minimize any deviation from a predefined and preferably single way to operate. If the rules are violated by you pulling the wires, submerging the car into the water, or applying power to the wrong terminal, the circuit should detect and fail fast with fuses going off. It should break and stop the entire mechanism from going into undocumented conditions. Thus it does not cause damage, hurt, or burn anyone. The cost of a chain of errors is high so it is cheaper to self-destruct. That pretty much applies to software as we model reality.

What if we don’t fail fast in the core domain and allow invalid arguments in a method performing business operations? The consequences are vast and violating business requirements could be the less serious problem. If methods preconditions or object invariants are not properly checked, any bug or malicious call exploiting this deficiency can corrupt or compromise the database, send unplanned emails, shut down production processes, or exhaust server resources. The core domain has no right to fall short of maintaining its invariants and checking methods pre and post-conditions. And it is the only thing it should do, i.e., defend against unauthorized use.

With all that the domain entities are complex enough and it is unfair to ask them for anything else. It should do the most straightforward thing: to fail hard throwing brief info of what is violated and not offer a second chance for corrected input. Like a sculptor making a statue cuts off all unnecessary parts, so does the developer create the domain model (the heart of the system) by limiting all possible states and interactions with users or other systems according to the agreed requirements from stakeholders. That is the best way to tame accidental complexity.

But how to protect our modeled domain from invalid input in a client-friendly way so that the system does not escalate and throw things at you on any hiccup? Clients would be reluctant to play that shooter game and start from the beginning on the first shot. Meet the validation. It is that toy cover or a shield protecting our work from unintended interaction nicely so our device does not blow up on the first mistake. The point is it is not part of the core domain that just throws straightforward errors. The validation allows any input, tries to make sense of it, and decides whether to allow it through or do something else but not throw exceptions or terminate the process. It has many graceful options such as showing a nice message on how to remediate, highlighting your typing in a text box, redirecting you to support chat, or anything else appealing from a usability perspective.

Validation is typically sitting in the Application layer as part of the application facade defending internals from risky interactions. The core domain can be a helper here though. It can provide shared helper methods to check certain conditions in a “functional“ way (i.e. without creating or maintaining a state) or allow for a “specification” pattern. The latter is quite popular as it allows to reuse of many domain checks in a structured way. That generally helps be DRY. The checks are the same but reactions to the errors are different. Domain model immediately throws while validation assumes bad input and tries to suggest a workaround.

Still, I think that is enough for the article and I will toss some examples in the next one.

0
Subscribe to my newsletter

Read articles from Dmitry Dezuk (Dezhurnyuk) directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Dmitry Dezuk (Dezhurnyuk)
Dmitry Dezuk (Dezhurnyuk)

Senior Software Developer with over 10 years of performance-focused .NET development experience. I assist developers in solving architectural challenges and simplifying complex software projects. Writing as Dmitry Dezuk to share everyday productivity tips for developing faster and more reliable software.