Chapter 8. Dividing responsibilities

 

This chapter covers

  • Making a distinction between read and write models
  • Defining separate repositories for read and write models
  • Designing read models for their specific use cases
  • Building up a read model from events or a shared data source

We’ve looked at how objects can be used to retrieve information or perform tasks. The methods for retrieving information are called query methods, and the ones that perform tasks are command methods.

Service objects may combine both of these responsibilities. For instance, a repository (like the one in the following listing) could perform the task of saving an entity to the database and also retrieving an entity from the database.

Listing 8.1. PurchaseOrderRepository can save and retrieve a PurchaseOrder
interface PurchaseOrderRepository
{
    /**
     * @throws CouldNotSavePurchaseOrder
     */
    public function save(PurchaseOrder purchaseOrder): void;

    /**
     * @throws CouldNotFindPurchaseOrder
     */
    public function getById(int purchaseOrderId): PurchaseOrder;
}

Saving and retrieving an entity are more or less each other’s inverse operations, so it’s only natural to let one object have both responsibilities. However, in most other cases, you will find that performing tasks and retrieving information are better off being divided amongst different objects.

8.1. Separate write models from read models

8.2. Create read models that are specific for their use cases

8.3. Create read models directly from their data source

8.4. Build read models from domain events

Summary

Answers to the exercises