Not sure that there is a 'one true way' answer for a design approach that, to be fair, is still evolving. First, DDD and CQRS are not the same thing although the CQRS folks seem to have derived from a DDD-influenced starting point.
There's a lot going on in the DDD mindset and much of it has to do with properly defined boundaries of problems, communication among stakeholders, and interaction between systems, not necessarily a specific implementation in code, so I don't think being too hard-core is a virtue.
You are maybe seeing some of the debate around whether and how domain objects should be changeable, and what function a domain object serves in a system as a whole. CQRS splits a system into read and write pathways, so it's sensible to conclude that you don't actually need read access when you're on the write pathway. Reading then becomes something you do against the events raised by some domain objects and consumed (handled) by others. If you go back a bit in the CQRS history, you'll find arguments that domain objects shouldn't have setters, only getters and a single 'handler' method. The logic here is that only consuming events should result in state change, and that change is entirely handled internally by the domain object.
You show the results of change by treating them as separate artifacts of change, putting them into a separate, flat persistent structure (e.g., a table) and reading it as if you were just reading a report on the current and historical state of the system. For example, you could consume an event by extracting the data you need to read and saving it to a database table that maps closely to a single view (e.g. a screen) of your system.
If you do experiment with this style, be cognizant that other programmers are likely not going to be familiar with this approach, and that there are relatively few (but interesting) scenarios where it's justifiable as a design approach.
For unit testing, there are a couple of approaches that may work for you. The first, and most natural, is to verify the events you expect to see raised by your domain objects are correct. A domain object might raise a generic changed event holding information about the change. You could verify that. You could verify the fact that the event was raised at all, and that other events weren't raised.
Another approach is to use Test Spies that expose readable attributes on your domain object so you can verify state changes. For instance, you can inherit from your domain object, add some accessors to read what would otherwise be encapsulated state and verify that it's correct.
Again, you're confused because these approaches are confusing. If you're looking to adopt some good ideas into your programming, take the juicy bits first. DDD boundaries and making roles explicit are changes to your way of thinking about communicating with your code. CQRS at a minimum suggests that reading data and writing data are segregable operations. That insight leads you to think very clearly about what the role of the data you do need to present is, how much you really need, who's consuming it, how fresh does it need to be, etc... You don't need a full blown Event Sourcing implementation to adopt better encapsulation in your coding. You can start by just focusing on atomic operations within your object, "Tell, Don't Ask" approaches to object interface design, and Inversion of Control through events/handlers rather than strict control within procedural services.
I'd go with Option 3, with the following notes:
- Try and reduce the amount of domain logic that your clients need to know to get the job done. Create services that expose that data in meaningful (to your clients) ways, so that you can request collections of domain objects which fulfil certain criteria, rather than doing that crunching on your client.
- Validation should be considered optional on the client-side as, you can never guarantee that future client implementations are going to be done properly. Therefore, always validate on the server side as if it hadn't been done elsewhere. Of course, clients should be validating on the client-side too.
- Rather than mapping the WCF data back to full domain objects on the client side, consider mapping them to simpler ViewModel-type objects - a slimmed down version of your full domain objects only containing properties appropriate to the client - makes client programming simpler.
The problem you're still faced with is lots of mapping. I guess this price is worth paying (and made easier with a tool such as AutoMapper) because removing the client dependency on your domain model gives you breathing room to change your domain, tweak the mapping, without breaking any client code.
Best Answer
These days, you are likely to see reads (queries) handled differently than writes (commands). In a system with a complicated query, the query itself is unlikely to pass through the domain model (which is primarily responsible for maintaining the consistency of writes).
You are absolutely right that we should render unto SQL that which is SQL. So we'll design a data model optimized around the reads, and a query of that data model will usually take a code path that does not include the domain model (with the possible exception of some input validation -- ensuring that parameters in the query are reasonable).