Very interesting article! However, I don’t agree on everyting.
wherein a class called “Person” is both a domain model and a representation of a database table called “person”
This isn’t necessarily true. When I develop a Domain Model, I do it completely free of any database concerns. I design classes that make sense for the business domain. The model is not a relational model at all.
Then, when I need to persist certain things in database, yes, some of those classes become almost like a table. But not always. Some classes are not persisted, some are persisted as attributes of other tables, some are persisted but without all of their attributes, and so on.
Maybe I’ll use an ORM, maybe i’ll persist using a NoSql Database, the point is that the domain model is not the representation of a database table.
Say your app has four routes, those being: create user, confirm user, login user and delete user (just kidding about that last one, no one deletes anything anymore). In a domain-driven design, you’d put the logic for all four of those things in the user model.
Again, that’s a very basic view of domain driven design. I agree with you that if you do that, the separation of concern is not clear. But that doesn’t mean you should separate behavior and data. That means you have to rethink your design. Putting all the behavior outside of the model is one way to do it, but that’s not the only way. In this example, without thinking too much, create user and delete user belong in the repository for example. that leaves you with only confirm user, but again, no one forces you to put that in the User class if you don’t like the result.
We agree on the problem, but the solution is not that obvious.
What you’re describing is the Command pattern. It’s a great pattern and I agree 100% that it’s a good idea to use it. I use it on the application layer.
In domain-driven design it gets mixed around with code from other operations. Refactoring becomes hard. Classes grow huge.
That’s where I agree with you. Several use cases shouldn’t be mixed in one class.
Each command does one think, it’s usually the perfect fit for a single transaction. What I disagree on is that commands I design use domain objects to perform the job. Commands do the transaction management, they coordinate domain objects actions to perform one use case. They don’t contain business logic besides that.
Otherwise you end up with an anemic domain model and all your business code ends up in your Commands. It looks great in your example, but in more complex use cases it’s very hard to read in my experience. I know you can split commands into several smaller ones, but that doesn’t change the fact that it’s easier to reason on an object if it contains both the data and the behavior. High cohesion is what makes a code easy to read.