Last active
April 29, 2020 15:23
-
-
Save timoschinkel/a017a6fe5fc668f093874496b11ea505 to your computer and use it in GitHub Desktop.
Writing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
// In the current solution we have three separate interfaces. This allows for a clean separation of functionality: | |
interface QueryExecutorInterface | |
{ | |
public function execute(QueryInterface $query): array; | |
} | |
interface QueryMetadataServiceInterface | |
{ | |
public function getLatestQueryMetaData(): QueryMetadata; | |
} | |
interface QueryTransactionServiceInterface | |
{ | |
public function startTransaction(): QueryTransaction; | |
} | |
// But it also provides us with a side effect when you want to perform actions in a transaction or when you want to | |
// retrieve the last insert identifier. This is caused by the splitting of interfaces. Our writer implements all | |
// three interface, but due to type hinting we need to inject that same writer three times. We rely on our configuration | |
// to ensure that the queries executed within a transaction are actually performed on the same connection as this is not | |
// enforced. Something similar is the case when fetching the meta service from the last query. Given the signature it can | |
// be that all three services are communicating to a different database. | |
class MyRepository | |
{ | |
/** @var QueryExecutorInterface */ | |
private $writer; | |
/** @var QueryTransactionServiceInterface */ | |
private $transactionService; | |
/** @var QueryMetadataServiceInterface */ | |
private $metadataService; | |
public function __construct( | |
QueryExecutorInterface $writer, | |
QueryTransactionServiceInterface $transactionService, | |
QueryMetadataServiceInterface $metadataService | |
) { | |
$this->writer = $writer; | |
$this->transactionService = $transactionService; | |
$this->metadataService = $metadataService; | |
} | |
public function action() | |
{ | |
$transaction = $this->transactionService->startTransaction(); | |
$writer->execute(new MyQuery(/* ... */)); | |
$metadata = $metadataService->getLatestQueryMetaData(); | |
// ... | |
$transaction->commit(); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
// One option to ensure that the meta service and queries are tied to the same connection as the transaction | |
// is to make the transaction more than a value object: | |
interface QueryExecutorInterface | |
{ | |
public function execute(QueryInterface $query): array; | |
} | |
interface QueryMetadataServiceInterface | |
{ | |
public function getLatestQueryMetaData(): QueryMetadata; | |
} | |
interface QueryTransactionServiceInterface | |
{ | |
public function startTransaction(): QueryTransactionInterface; | |
} | |
interface QueryTransactionInterface extends QueryMetadataServiceInterface, QueryExecutorInterface | |
{ | |
public function commit(): void; | |
public function rollback(): void; | |
} | |
// Now everything related to the transaction is actually called on the transaction object itself. This | |
// reduces the amount of dependencies injected into a repository and will ensure that the queries and | |
// meta service are retrieved on the same connection via code. Downside of this is that this solution | |
// is not solving the double dependencies when the need is there for meta service, but not for a transaction. | |
class MyRepository | |
{ | |
/** @var QueryTransactionServiceInterface */ | |
private $transactionService; | |
public function __construct(QueryTransactionServiceInterface $transactionService) | |
{ | |
$this->transactionService = $transactionService; | |
} | |
public function action() | |
{ | |
$transaction = $this->transactionService->startTransaction(); | |
$transaction->execute(new MyQuery(/* ... */)); | |
$metadata = $transaction->getLatestQueryMetaData(); | |
// ... | |
$transaction->commit(); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
// Another solution would be to introduce a new interface that combines the three interface. This | |
// is actually what we implicitly do right now; `MysqlQueryExecutor` already implements all three | |
// interfaces. | |
interface QueryExecutorInterface | |
{ | |
public function execute(QueryInterface $query): array; | |
} | |
interface QueryMetadataServiceInterface | |
{ | |
public function getLatestQueryMetaData(): QueryMetadata; | |
} | |
interface QueryTransactionServiceInterface | |
{ | |
public function startTransaction(): QueryTransaction; | |
} | |
interface WriteQueryExecutorInterface extends QueryExecutorInterface, QueryMetadataServiceInterface, QueryTransactionServiceInterface | |
{} | |
// This way we reduce the dependencies again to one. But instead of moving the logic to the transaction centralization | |
// is ensured by the new interface. | |
class MyRepository | |
{ | |
/** @var WriteQueryExecutorInterface */ | |
private $writer; | |
public function __construct(WriteQueryExecutorInterface $writer) | |
{ | |
$this->writer = $writer; | |
} | |
public function action() | |
{ | |
$transaction = $$writer->startTransaction(); | |
$writer->execute(new MyQuery(/* ... */)); | |
$metadata = $writer->getLatestQueryMetaData(); | |
// ... | |
$transaction->commit(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment