In this post, I’d like to talk about a mechanical approach to domain modeling. It sometimes arises when teams start applying Domain-Driven Design (DDD) principles to their projects and when they don’t have enough experience with it yet.
What does it mean to approach domain modeling mechanically?
You might have been noticing the signs of that attitude in the past. Whenever someone discovered a new concept in the project they were working on, they immediately defined several classes for it: an entity for the concept itself, a repository, a factory, and a domain service. They did that even if there was no need for those classes at the moment.
The exact list of such classes may differ and depends on how strongly the team has committed to the use of the tactical patterns described by DDD. There could even be some kind of a guideline making this practice standard across all members of the team.
A logical evolution of this attitude – stage number 2, so to speak, – is to create some small tool on top of this guideline which would help developers adhere to it. That could be a code snippet in Visual Studio or a file template which would auto-generate all four classes given a set of attributes the new concept contains.
I call such attitude mechanical approach to domain modeling. It might seem compelling at first. We are being more productive this way, aren’t we? In practice, however, this is a horrible way to approach Domain-Driven Design.
The problem here is twofold. As I said, all those classes are created at once, before the actual need for them appears in the code base. That is a clear violation of the YAGNI principle.
The second problem with this attitude is that it greatly diminishes the benefits of Domain-Driven Design itself. The sole act of domain modeling is closely related to learning. When we build the model we first and foremost learn the domain we are working in, the code is mostly just an artifact of that learning process. Like laws of physics, the model is not something we create, it is something we discover on our way to understand the problem we are solving.
That’s why all attempts to automate the process of writing code that represents domain knowledge inevitably fail in the long run. At best, the team just perceives those imposed guidelines as noise and don’t pay too much attention to them. At worst, programmers stop thinking about the domain and just try to fit business logic into the building blocks they were given. In any way, such approach hinders learning and slows the team down. The heart of the application – its domain model – becomes bloated with unnecessary elements. The differences between the domain concepts evaporate, they all become faceless.
Any “standard” domain classes structure defined upfront is too rigid. Unless your domain is extremely simple, it’s impossible to fit its concepts into such structure. There always will be some details that don’t quite match, and there always will be exceptional cases you would like to model differently.
Domain modeling is an iterative process. Moreover, it’s the most important part of building software projects. The only way to do it right is to approach it thoughtfully, with full attention to the details.
Practices that encourage approaching domain modeling mechanically
There are several practices that might encourage the mechanical approach to domain modeling.
The first one is use of templates. The guidelines I brought above regarding four classes for each domain concept is an example of it.
But how should you build your domain model, then? The answer is: from scratch. Just create an empty class library and start experimenting. Talk to domain experts and let the knowledge you get from them paint itself in code. Tactical DDD patterns (such as entities, value objects, and so on) are helpful on that way as they allow you to frame that knowledge in a recognizable for other developers way, but don’t let anyone tell you what exact set of those patterns you should use. Every problem is unique in some way or another. Treat it accordingly.
The second practice is code generation. If a team of developers employs auto-generating for domain classes, they are most likely falling into the trap of mechanical approach to domain modeling.
A good example here is Entity Framework’s database-first development where you can define a database structure first and then just auto-generate domain classes after that structure. Such approach feels extremely helpful initially but it completely misses the point of proper domain modeling.
A domain model is much more than just a 1-to-1 image of your database. At the same time, EF database-first development encourages you to treat concepts in your domain layer as a mediocre commodity and doesn’t help with the learning process at all.
Note that I’m not saying that templates and code generation are bad in general. Just that they don’t suit the task of modeling your domain.
Signs of mechanical approach to domain modeling
Here are some signs that may give you a clue your team approaches domain modeling mechanically.
Every database table has its own domain entity. This often happens when programmers employ EF database-first tooling as I described above. With it, it’s easy to begin with the database structure (which itself is not the best way to build a domain model) and then just create a 1-to-1 mapping of it in the application code.
With this attitude, you can often see that even the simplest DB tables – such as those used for representing many-to-many relationships – get their own entities in the domain model. Obviously, this is not the best way to deal with modeling your application. Unless your domain is really simple, there almost never will be a 1-to-1 correspondence between the model and the database structure.
All domain concepts are represented as Entities. This is another corollary of not thinking enough about the domain. Often (although not always), some notions in the application can be modeled as Value Objects instead of Entities. If so, it’s a good idea to do that because value objects are easier to work with and generally should be preferred over entities. Representing all domain concepts as entities often means the team isn’t paying enough attention to modeling the domain.
Excessive use of primitive types. Does your domain model contain the notion of Email? If so, how is it represented? As a string? This is another smell I advocate you avoid. Even if some small concepts can be easily depicted as primitive types, it doesn’t necessarily mean it’s the best way to do that. It is often better to wrap them into a separate type and use that type throughout the domain layer instead. I write about this practice in more detail here.
Domain model is the most important part of any enterprise application, and it should be treated accordingly. The sole act of domain modeling is closely related to learning. And that is something we cannot automate.