Domain-Driven Design – Are Domain Objects Write-Only?

cqrsdomain-driven-designdomain-modelevent-sourcing

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

Best Answer

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.