I've been reading about Domain Driven Design for almost two years and have been cautiously been introducing some concepts in to my daily work or at least making plans for how things I do regularly could be done within a Domain Driven Design.

One conclusion that I've beginning to come to especially in response to reading more about Event Sourcing and Command Query Responsibility Segregation (CQRS) that perhaps domain objects are intended to be used only for write purposes. To be more clear, it seems that what people are subtly suggesting in much of the documentation I've read that the domain objects are responsible for doing domain centric operations/calculations, validation, and then are there mainly to provide a road to persistence through the infrastructure provided within a Repository implementation. Although I do like the fact that this may simplifies the domain model greatly as it cuts out the responsibility of exposing state.

If it is indeed correct that domain objects are mainly to be used as write-only objects then that raises some questions for me that I'm hoping somebody could answer.

  1. How does one perform unit tests on an object that has setters, or methods that modify the state of an object but that provide no outwardly public interface to read the state from such as property getters in C#? Is it okay to expose state solely for the purpose of making that object testable?
  2. How does one show to a user the results of calculations or operations done in the domain without having to persist them and then pull the results from persist store outside of the context of the domain? Is it okay to expose state solely for the purpose of showing results.

Is the rule of thumb that the only property getters (get accessors) should be ones that are also writable in the domain? Or said differently should readonly properties be the only thing to avoid since they are only there for read purposes and thus do not play a a necessary role in the actual domain model?

Related material:

  1. TDD, DDD and Encapsulation

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.

  • 1
    +1 for starting with the jucy bits. In addition: biting off just the CQS (skipping the 'events' part for now) could be a good place to start.
    – cottsak
    Aug 17 '12 at 3:23

Are Domain Objects in Domain Driven Design only supposed to be write-only?

No. CQRS can used alongside DDD.

  • But is the Query part of CQRS all about querying data that is used by the domain model to write changes to the model or can it be used to querying data for the application layer that might say show the values to the user? I'm hearing from some that DDD is all about coordinating changes and should not be used to read from for any other purpose than to coordinate changes to another object within the domain model. The decision one way or the other would mean a radically different model design seeing that exposed data on domain objects would be vary limited if it is only consumed in the domain.
    – jpierson
    Feb 22 '12 at 17:47

Your domain model unit tests should be checking that for each command executed the correct domain events are raised. Your domain commands and captured events can be interrogated for state.

You could also override ToString() on your domain commands and events to provide automatic, human readable reporting of state.

To answer your second question, to display the results of commands you should arrange for domain events to be 'published' to your read-model.

  • Could you elaborate a little bit on whether this still applies when you are not making use of event-sourcing?
    – jpierson
    Dec 17 '11 at 6:45
  • 1
    Without event sourcing perhaps my suggestion will not work; I don't know the ways of your code. For a domain object that ideally does not expose any properties, perhaps you can explicitly implement a test interface that exposes the properties you wish to test. Only your test code will 'know' to cast such domain objects to the test interface.
    – Ed James
    Dec 20 '11 at 11:18
  • Thanks for that suggestion. I feel a little uneasy about the idea of modifying my domain classes specifically for testability but I suppose this may be one of those grey areas still in Domain Driven Design if you want it to be testable. Another thought is that if one were to expose stabilizability and testability through the same interface then at least your only introducing one infrastructure dependency instead of two. What do others think of that?
    – jpierson
    Dec 21 '11 at 17:34

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.