Read models

Updated on @September 27, 2023

Reusing the write model

Let’s assume our project already has a Book entity and a BookRepository interface, which are used inside the AddBookToCatalog use case to add a new book to the catalog.

final class Book
{
		public function __construct(
				private readonly BookId $id,
				private int $price,
				// ...
		)

		public function changePrice(int $newPrice): void 
    { 
        $this−>price = $newPrice; 
    }
}

interface BookRepository 
{ 
    public function save(Book $book): void;

    public function ofId(BookId $bookId): ?Book;
}
The Book entity and BookRepository interface.

Now, let’s find out if we can use this Book entity during the order process when we need to know the price of a book. The simplest thing we could do is add a price() method to the entity.

final class OrderBookCommandHandler implements CommandHandler
{
		public function __construct(
				private readonly BookRepository $books,
		) {
		}

		public function __invoke(OrderBookCommand $command): void 
		{ 
		    $book = $this−>books−>ofId( 
		        BookId::fromString($command->bookId()), 
		    );
		    $orderAmount = $command->quantity() * $book->price();
		 
		    // Save the order
				// ...
		}
}
Using the Book entity inside the OrderBook use case.

This solution looks good. However, there are a couple of issues with reusing an existing entity in a different context than creating or updating it:

  1. The existing entity is not designed to retrieve information from. Anywhere we load the Book entity, we do so with the intention to manipulate it and save it. But now we’ve started using Book in the OrderBook use case, where we don’t want to change anything about it at all.
Bibliography