Matthias NobackDividing responsibilities - Part 2 (22.7.2019, 08:20 UTC)

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)

Link
Voices of the ElePHPantInterview with David Laietta (22.7.2019, 06:03 UTC)
Link
Voices of the ElePHPantInterview with Gary Hockin (20.7.2019, 19:32 UTC)
Link
larry@garfieldtech.comSkipping PHP.CE this year (20.7.2019, 00:01 UTC)
Skipping PHP.CE this year

Being a conference organizer is hard. Like, seriously. Aside from the obvious logistics, and the not-at-all-obvious logistics, you're in a position to create a social gathering, not just a technical one. That comes with a lot of baggage and challenges, many of them often competing and incompatible, that need to be balanced. One in particular is ensuring that the speakers are an eclectic lot that are representative both of the community as it is and as you want it to be. That can take a lot of work.

Earlier this year the organizers of the PHP Central Europe conference (PHP.CE) approached me and asked me to submit sessions for the PHP.CE conference in Dresden this October. I rather enjoy speaking at conferences so I of course did so, and this past week they announced their speaker selections, including me with 2 sessions.

Unfortunately, some fellow speakers pointed out that their speaker selections for this year included zero women. There were numerous speakers with 2 sessions (myself included) or a workshop and a session, but no women at all.

Continue reading this post on SteemIt.

Larry 19 July 2019 - 7:01pm
Link
Voices of the ElePHPantInterview with David Laietta (18.7.2019, 19:32 UTC)
Link
Derick RethansPHP Internals News: Episode 19: Deprecate curly brace syntax (18.7.2019, 08:19 UTC)

PHP Internals News: Episode 19: Deprecate curly brace syntax

In this episode of "PHP Internals News" I chat with Theodore Brown (Twitter, Website, GitHub) about the "Deprecate curly brace syntax for accessing array elements and string offsets" RFC.

The RSS feed for this podcast is https://derickrethans.nl/feed-phpinternalsnews.xml, you can download this episode's MP3 file, and it's available on Spotify and iTunes. There is a dedicated website: https://phpinternals.news

Credits

Music: Chipper Doodle v2 — Kevin MacLeod (incompetech.com) — Creative Commons: By Attribution 3.0

Become a Patron!
Link
Matthias NobackDividing responsibilities - Part 1 (18.7.2019, 07:30 UTC)

I'm happy to share with you an excerpt of 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.

Chapter 7: Dividing responsibilities

We've looked at how objects can be used to retrieve information, or perform tasks. The methods for retrieving information are called query methods, the ones that perform tasks are command methods. Service objects may combine both of these responsibilities. For instance, a repository (like the one in Listing 1) could perform the task of saving an entity to the database, and at the same time it would also be capable of retrieving an entity from the database.

Listing 1. The PurchaseOrderRepository can save and retrieve a PurchaseOrder entity.

interface PurchaseOrderRepository
{
    /**
     * @throws CouldNotSavePurchaseOrder
     */
    public function save(PurchaseOrder purchaseOrder): void;

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

Since saving and retrieving an entity are more or less each other's inverse operations, 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.

Separate write models from read models

As we saw earlier, there are services, and other objects. Some of these other objects can be characterized as Entities, which model a particular domain concept. In doing so, they contain some relevant data, and offer ways to manipulate that data in valid and meaningful ways. Entities can also expose data, allowing clients to retrieve information from them, whether that is exposed internal data (like the date on which an order was placed), or calculated data (like the total amount of the order).

In practice, it turns out that different clients use entities in different ways. Some clients will want to manipulate an entity's data using its command methods, while others just want to retrieve a piece of information from it using its query methods. Nevertheless, all these clients will share the same object, and potentially have access to all the methods, even when they don't need them, or shouldn't even have access to them.

You should never pass an entity that can be modified to a client that isn't allowed to modify it. Even if the client doesn't modify it today, one day it might, and then it will be hard to find out what happened. That's why the first thing you should do to improve the design of an entity, is separate the Write model from the Read model.

We'll find out how to accomplish this by looking at an example of a PurchaseOrder entity (Listing 2). A purchase order represents the fact that a company buys a product from one of its suppliers. Once the product has been received, it's shelved in the company's warehouse. From that moment on the company has this product in stock. We'll use the same example for the remaining part of this chapter and work out different ways to improve it.

Listing 2. The PurchaseOrder entity.

final class PurchaseOrder
{
    private int purchaseOrderId;
    private int productId;
    private int orderedQuantity;
    private bool wasReceived;

    private function __construct()
    {
    }

    public static function place(
        int purchaseOrderId,
        int productId,
        int orderedQuantity
    ): PurchaseOrder {
        /*
         * For brevity, we use primitive type values, while in
         * practice, the use of value objects is recommended.
         */

        purchaseOrder = new self();

        purchaseOrder.productId = productId;
        purchaseOrder.orderedQuantity = orderedQuantity;
        purchaseOrder.wasReceived = false;

        return purchaseOrder;
    }

    public function markAsReceived(): void
    {
        this.wasReceived = true;
    }

    public function purchaseOrderId(): int
    {
        return this.purchaseOrderId;
    }

    public function productId(): int
    {
        return this.productId;
    }

    public function orderedQuantity(): int
    {
        return this.orderedQuantity;
    }

    public function wasReceived(): bool
    {
        return this.wasReceived;
    }
}

In the current implementation,

Truncated by Planet PHP, read more at the original (another 12076 bytes)

Link
Voices of the ElePHPantInterview with David Laietta (17.7.2019, 08:30 UTC)
Link
Evert Pot502 Bad Gateway (16.7.2019, 15:00 UTC)

HTTP is a protocol that is implemented by servers and clients, but there is a third category: proxies.

When a system is acting as a proxy for a different server, and that server is misbehaving or doing something unexpected, the proxy can return 502 Bad Gateway to tell a client that the proxy is working fine, but there was something wrong with the ‘origin’ server instead.

A specific example of this could be a CDN in front on a web server, and the web-server is misconfigured and responding with incorrect HTTP responses.

This status should not be used if the origin server just returned a valid HTTP error itself, because these should generally just be forwarded by the proxy (mostly) unaltered.

Example

HTTP/1.1 502 Bad Gateway
Content-Type text/plain

We made a HTTP request to an origin server, but we got a Gopher response back.

References

Link
Voices of the ElePHPantInterview with Sara Golemon (16.7.2019, 11:30 UTC) Link
LinksRSS 0.92   RDF 1.
Atom Feed   100% Popoon
PHP5 powered   PEAR
ButtonsPlanet PHP   Planet PHP
Planet PHP