Dividing responsibilities – Part 2
This is another excerpt from my latest book, which is currently part of Manning's Early Access Program. Take 37% off Object Design Style Guide by entering fccnoback into the discount code box at checkout at manning.com.
Make sure to read part 1 first.
Create read models directly from their data source
Instead of creating a StockReport
model from PurchaseOrderForStock
objects, we could go directly to the source of the data, that is, the database where the application stores its purchase orders. If this is a relational database, there might be a table called purchase_orders
, with columns for purchase_order_id
, product_id
, ordered_quantity
, and was_received
. If that's the case, then StockReportRepository
wouldn't have to load any other object before it could build a StockReport
object; it could make a single SQL query and use it to create the StockReport
, as shown in Listing 11).
Listing 11. StockReportSqlRepository
creates a stock report using plain SQL.
final class StockReportSqlRepository implements StockReportRepository
{
public function getStockReport(): StockReport
{
result = this.connection.execute(
'SELECT ' .
' product_id, ' .
' SUM(ordered_quantity) as quantity_in_stock ' .
'FROM purchase_orders ' .
'WHERE was_received = 1 ' .
'GROUP BY product_id'
);
data = result.fetchAll();
return new StockReport(data);
}
}
Creating read models directly from the write model's data source is usually pretty efficient in terms of runtime performance. It's also an efficient solution in terms of development and maintenance costs. This solution will be less efficient if the write model changes often, or if the raw data can't easily be used as-is, because it needs to be interpreted first.
Build read models from domain events
One disadvantage of creating the StockReport
read model directly from the write model's data is that the application will make the calculations again and again, every time the user requests a stock report. Although the SQL query won't take too long to execute (until the table grows very large), in some cases it'll be necessary to use another approach for creating read models.
First, let's take another look at the result of the SQL query we used in the previous example (Table 1).
Table 1. The result of the SQL query for generating a stock report.
product_id | quantity_in_stock |
---|---|
123 |
10 |
124 |
5 |
What would be another way in which we can come up with the numbers in the second column, that wouldn't involve looking up all the records in the purchase_orders
table and summing their ordered_quantity
values?
What if we could sit next to the user with a piece of paper, and whenever they mark a purchase order as "received", we'd write down the ID of the product and how many items of it were received. The resulting list would look like Table 2:
Table 2. The result if we write down every received product.
product_id | received |
---|---|
123 |
2 |
124 |
4 |
124 |
1 |
123 |
8 |
Now, instead of having multiple rows for the same product, we could also look up the row with the product that was just received, and add the quantity we received to the number that's already in the received
column, as is done in Table 3.
Table 3. The result of combining received quantities per product.
product_id | received |
---|
Truncated by Planet PHP, read more at the original (another 8930 bytes)