Embedded Value with an Ad Hoc ORM

If we're dealing with an Ad Hoc ORM using the Embedded Value pattern, we need to create a field in the Entity table for each attribute in the Value Object. In this case, two extra columns are needed when persisting a Product Entity — one for the amount of the Value Object, and one for its currency ISO code:

CREATE TABLE `products` (
id INT NOT NULL,
name VARCHAR( 255) NOT NULL,
price_amount INT NOT NULL,
price_currency VARCHAR( 3) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

For persisting the Entity in the database, our Chapter 10, Repositories has to map each of the fields of the Entity and the ones from the Money Value Object.

If you're using an Ad hoc ORM Repository based on DBAL—let's call it DbalProductRepositoryyou must take care of creating the INSERT statement, binding the parameters, and executing the statement:

class DbalProductRepository
extends DbalRepository
implements ProductRepository
{
public function add(Product $aProduct)
{
$sql = 'INSERT INTO products VALUES (?, ?, ?, ?)' ;
$stmt = $this->connection()->prepare($sql);
$stmt->bindValue(1, $aProduct->id());
$stmt->bindValue(2, $aProduct->name());
$stmt->bindValue(3, $aProduct->price()->amount());
$stmt->bindValue(4, $aProduct
->price()->currency()->isoCode());
$stmt->execute();

// ...
}
}

After executing this snippet of code to create a Products Entity and persist it into the database, each column is filled with the desired information:

mysql> select * from products G
*************************** 1. row ***************************
id: 1
name: Domain-Driven Design in PHP
price_amount: 999
price_currency: USD
1 row in set (0.00 sec)

As you can see, you can map your Value Objects and query parameters in an Ad hoc manner in order to persist your Value Objects. However, everything is not as easy as it seems. Let's try to fetch the persisted Product with its associated Money Value Object. A common approach would be to execute a SELECT statement and return a new Entity:

class DbalProductRepository
extends DbalRepository
implements ProductRepository
{
public function productOfId($anId)
{
$sql = 'SELECT * FROM products WHERE id = ?';
$stmt = $this->connection()->prepare($sql);
$stmt->bindValue(1, $anId);
$res = $stmt->execute();
// ...

return new Product(
$row['id'],
$row['name'],
new Money(
$row['price_amount'],
new Currency($row['price_currency'])
)
);
}
}

There are some benefits to this approach. First, you can easily read, step by step, how the persistence and subsequent creations occur. Second, you can perform queries based on any of the attributes of the Value Object. Finally, the space required to persist the Entity is just what is required — no more and no less.

However, using the ad hoc ORM approach has its drawbacks. As explained in the Chapter 6, Domain-Events, Entities (in Aggregate form) should fire an Event in the constructor if your Domain is interested in the Aggregate's creation. If you use the new operator, you'll be firing the Event as many times as the Aggregate is fetched from the database.

This is one of the reasons why Doctrine uses internal proxies and serialize and unserialize methods to reconstitute an object with its attributes in a specific state without using its constructor. An Entity should only be created with the new operator once in its lifetime:

Constructors  
Constructors don't need to include a parameter for each attribute in the object. Think about a blog post. A constructor may need an id and a title; however, internally it can also be setting its status attribute to draft. When publishing the post, a publish method should be called in order to alter its status accordingly and set a published date.

If your intention is still to roll out your own ORM, be ready to solve some fundamental problems such as Events, different constructors, Value Objects, lazy load relations, and so on. That's why we recommend giving Doctrine a try for Domain-Driven Design applications.

Besides, in this instance, you need to create a DbalProduct Entity that extends from the Product Entity and is able to reconstitute the Entity from the database without using the new operator, instead using a static factory method.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset