We have several aggregate roots that have two primary means of identification:
- an integer "key", which is used as a primary key in the database (and is used as a foreign key by referencing aggregates), and internally within the application, and is not accessible by the public web API.
- a string-based "id", which also uniquely identifies the aggregate root and is accessible by the public web API.
There are several reasons for having an integer-based private identifiers and a string-based public identifier - for example, the database performs better (8-byte integers as opposed to variable-length strings) and the public identifiers are difficult to guess.
However, the classes internally reference each other using the integer-based identifiers and if an integer-based identifier is 0, this signifies that the object hasn't yet been stored to the database. This creates a problem, in that entities are not able to reference other aggregate roots until after they have been saved.
How does one get around this problem, or is there a flaw in my understanding of persistence ignorance?
EDIT regarding string-based identifiers
The string-based identifiers are generated by the repository, connected to a PostgreSQL database, which generates the identifier to ensure that it does not clash with anything currently in the database. For example:
class Customer {
public function __construct($customerKey, $customerId, $name) {
$this->customerKey = $customerKey;
$this->customerId = $customerId;
$this->name = $name;
}
}
function test(Repository $repository, UnitOfWork $unitOfWork) {
$customer = new Customer(0, $repository->generateCustomerId(), "John Doe");
// $customer->customerKey == 0
$unitOfWork->saveCustomer($customer);
// $customer->customerKey != 0
}
I assume that the same concept could be used to create an entity with an integer-based key of non-0, and the Unit of Work could use the fact that it doesn't exist in the database as a reason to INSERT rather than UPDATE. The test()
function above would then become:
function test(Repository $repository, UnitOfWork $unitOfWork) {
$customer = new Customer($repository->generateCustomerKey(), $repository->generateCustomerId(), "John Doe");
// $customer->customerKey != 0
$unitOfWork->saveCustomer($customer);
// $customer->customerKey still != 0
}
However, given the above, errors may occur if the Unit of Work does not save the database objects in the correct order. Is the way to get around this to ensure that the Unit of Work saves entities in the correct order?
I hope the above edit clarifies my situation.