Both inserts will succeed because when AR was loaded initially and new Appointment was added, business rule was not violated, however in the end AR will be in invalid state.
That's the lucky version. In the unlucky version, you get a lost edit: when the second change is made, the first is overwritten and lost.
You've actually got two orthogonal concerns going here. The first is the lost update problem. We need to ensure that the persistence mechanism doesn't destroy information when we have racing edits.
The usual mechanism here is locking; we ensure that only one process can hold the lock at any given time, and ensure that the process holding the lock maintains the consistency of the data.
Two common patterns
lock.acquire
db.read
(business logic)
db.write
lock.release
Because the read occurs after the acquisition of the lock, we are protected against the situation that some other process sneaks in and changes the data we just read.
db.read
(business logic)
lock.acquire
db.compare
db.write
lock.release
Same basic idea: we ensure that only one process can write to the database at any given time. The difference here is that we explicitly compare the state of the database after we acquired the lock to the state we read originally. In other words, we're performing a compare and swap.
It's fairly common to use a mechanical comparison; for instance, for every write to increment a version number; compare then need only check that the version number after acquiring the lock matches the one used to read the data. This does, of course, assume that the increment of the version happens as part of the same atomic transaction as the changes to the business data.
Completely separate is the modeling question: should a conflicting appointment be rejected, or should it instead be accepted and handled as an escalation.
It would be useful to review Race Conditions Don't Exist.
A microsecond difference in timing shouldn’t make a difference to core business behaviors.
So for something like an appointment calendar, where the appointments describe the state of the Real World[tm] and you have multiple collaborators contributing information to the system, you probably want to make conflict an explicit part of the model (for example, maybe the second appointment goes into a "wait list" if the time slot is already booked).
But notice that switching to a model where you accept the double booking and escalate does not itself fix the lost edit error. You still need the locks to ensure that the first appointment is visible to the second.