Suppose you have 2 classes, Person and Rabbit. A person can do a number of things to a rabbit, s/he can either feed it, buy it and become its owner, or give it away. A rabbit can have none or at most 1 owner at a time. And if it is not fed for a while, it may die.
Class Person
{
Void Feed(Rabbit r);
Void Buy(Rabbit r);
Void Giveaway(Person p, Rabbit r);
Rabbit[] rabbits;
}
Class Rabbit
{
Bool IsAlive();
Person pwner;
}
There are a couple of observations from the domain model:
- Person and Rabbit can have references to each other
- Any actions on 1 object can also change the state of the other object
- Even if no explicit actions are invoked, there can still be a change of state in the objects (e.g. Rabbit can be starved to death, and that causes it to be removed from the Person.rabbits array)
As DDD is concerned, I think the correct approach is to synchronize all calls that may change the states in the domain model. For instance, if a Person buys a Rabbit, s/he would need to acquire a lock in Person to make a change to the rabbits array AND also another lock in Rabbit to change its owner before releasing the first one. This would prevent a race condition where 2 Persons claim to be the owner of the little Rabbit.
The other approach is to let the database to handle all these synchronizations. Who makes the first call wins, but then the DB needs to have some kind of business logics to figure out if it is a valid transaction (e.g. if a Rabbit already has an owner, it cannot change its owner unless the Person gives it away).
There are both pros/cons in either approach, and I’d expect the “best” solution would be somewhere in-between. How would you do it in real life? What’s your take and experience?
Also, is it a valid concern that there can be another race condition the domain model has committed its change but before it is fully committed in the database?
And for the 3rd observation (i.e. state change due to time factor). How will you do it?