Many people have been getting confused over what CQRS is. They look at CQRS as being an architecture; it is not. CQRS is a very simple pattern that enables many opportunities for architecture that may otherwise not exist. -- Greg Young, 2010
CQRS is "just" a pattern, the introduction of two "objects" where previously there was one. That is, in an of itself, a trade off (we're giving up the simplicity of a unified model to exploit other benefits), and one of the benefits is that we can choose different trade offs for writes than we choose for reads.
How are we supposed to efficiently validate business rules?
The usual answer is that we encode into the "write model" all of the checks and compensating actions to ensure that the history of events is internally consistent, which is to say that the history of events satisfies the business invariant.
The read model, with no authority to change anything, doesn't need to validate the rules. Our read models are usefully shaped transformations of the authoritative history.
My problem with this answer is that replaying all the events on every command is a waste of processing resources. why not store that final object in a database and use it to validate business rules?
So what you are really talking about here is caching -- and that's fine, but comes with trade offs.
In a stateful process, reloading the "final object" from disk over and over doesn't make a lot of sense, because you've already got a perfectly viable copy of that object in memory -- having a durable copy of the "final object" is only useful on startup. Furthermore, if you end up shutting down abnormally, it may make a lot more sense to abandon the in memory copy of the object and rebuild it.
You'll also see instances of a sort of hybrid approach, which combines a durable copy of the object from some point in the past along with more recent events.
In a stateless process, like a web server that can be processing many different requests independently, storing the object after each change can make a lot of sense. You do need to be careful about the failure modes that occur when you have multiple processes trying to write to the same resource - if your object representation and your event history happen to share the same coarse grained lock (ex: you are storing both in a relational database), then things get easier -- but of course that introduces additional constraints on your choice of storage.
Well, if we add a database to the read side wouldn't we just end up with duplicate of the read model but now shares the same server and code base as the write side?
Not necessarily -- there's going to be shared semantics (the information has the same meaning), but there's no particular reason that the processes that satisfy queries must be written in the same language as the processes that satisfy commands. For that matter, there's no particular reason that the processes that transform your write models to your read models need to be written in the same language as the other two.
The processes that are accessing the same data model need to have compatible semantics and schema, of course.
Your understanding of the issues of data races will be improved if you read through Pat Helland's Data on the Outside versus Data on the Inside.
Data on the inside is "our" data, the stuff we can lock against modification when we are computing how it should change. In a design where we separate writes and reads, this is the "write side".
Anything else is "data on the outside" - if we can't lock it, then we can't be sure that it is still true, only that it was true at some point in the past. And that in turn becomes a constraint on what we can and cannot do within a model -- our locked data should never conflict with itself, but it may conflict with information that is locked somewhere else. So in those cases we need to be thinking more in the language of detection, mitigation, escalation rather than prevention.
Memories Guesses and Apologies is a good introduction to this topic.