What is the difference between

a good and a bad repository?

Hi, I am Arnaud!

Bouchonnois Corp

What is a repository?

«A repository behaves like an collection of unique domain object without taking care about the storage»

Public API

						
							namespace BouchonnoisCorp\Domain\Write;

							interface Repository
							{
								/**
								 * @throws \LogicException
								 */
								public function add(Object $object): void;

								/**
								 * @throws UnknownObject | \LogicException
								 */
								public function get(Identifier $identifier): Object;

								/**
								 * @throws \LogicException
								 */
								public function remove(Identifier $identifier): void;

								/**
								 * @throws \LogicException
								 */
								public function nextIdentity(): Identifier;
							}
						
					

Let's model a Galinette!

Dédé, Business Expert @BouchonnoisCorp

						
							namespace BouchonnoisCorp\Domain\Write;

							/**final*/ class Galinette
							{
								// constructor ...

								public static function born(
									Identifier $identifier,
									Birthday $birthday,
									Gender $gender
								): Galinette {
									return new self($identifier, $birthday, new Name(''), $gender);
								}

							    public function name(Name $name): void
								{
									$this->name = $name;
								}

								public function goToHeaven(): void
								{
									$this->goToHeavenAt = new \DateTimeImmutable('NOW');
								}
							}
						
					

The bad repository

						
							namespace BouchonnoisCorp\Domain\Write;

							interface GalinetteRepository
							{
								public function find(int $id): Galinette;

								public function findGalinetteArray(): array;

								public function findGalinetteForARelease(): Galinette[];

								public function findGalinetteIdentifierForAReleaseAsString(): string[];

								public function findGalinetteNameByIdentifier(int $id): string;

								public function getQueryBuilder(): QueryBuilder;

								// etc ...
							}
						
					

These are not repositories!

These are more SQL query repositories

The good repository

Let's design it!

Gérard, CTO @BouchonnoisCorp

Repository dependencies

						
							namespace BouchonnoisCorp\Infrastructure\Storage\Doctrine;

							use BouchonnoisCorp\Domain\Write\GalinetteRepository;

							final class GalinetteRepository implements GalinetteRepository
							{
								private $entityManager;

								public function __construct(EntityManagerInterface $entityManager)
								{
									$this->entityManager = $entityManager;
								}

								// ...
							}
						
					

Get the next identity

						
							namespace BouchonnoisCorp\Infrastructure\Storage\Doctrine;

							final class GalinetteRepository implements GalinetteRepository
							{
								// ...

								public function nextIdentity(): Identifier
								{
							        try {
										return new Identifier(Uuid::uuid4()->toString());
									} catch (\Exception $e) {
										throw new \LogicException('It is not possible to build the next identity');
									}
								}
							}
						
					

Add a galinette

						
							namespace BouchonnoisCorp\Infrastructure\Storage\Doctrine;

							final class GalinetteRepository implements GalinetteRepository
							{
								// ...

								public function add(Galinette $galinette)
								{
									try {
										$this->entityManager->persist($galinette);
										$this->entityManager->flush($galinette);
									} catch (ORMInvalidArgumentException | ORMException $e) {
										throw new \LogicException('It is not possible to add this galinette');
									}
								}

								// ...
							}
						
					

Retrieve a galinette

						
							namespace BouchonnoisCorp\Infrastructure\Storage\Doctrine;

							final class GalinetteRepository implements GalinetteRepository
							{
								// ...

								public function get(Identifier $identifier): Galinette
								{
									try {
										$galinette = $this->entityManager->find(Galinette::class, $identifier);
									} catch (ORMInvalidArgumentException | ORMException $e) {
										throw new \LogicException('It is not possible to retrieve this galinette');
									} finally {
										if (null === $galinette) {
											throw new Exception\UnknownGalinette(
												(string) $identifier,
												Galinette::class
											);
										}

										return $galinette;
									}
								}

								// ...
							}
						
					

Remove a galinette?

						
							namespace BouchonnoisCorp\Infrastructure\Storage\Doctrine;

							final class ObjectRepository
							{
								// ...

								public function remove(Identifier $identifier)
								{
									try {
										$galinette = $this->entityManager->getReference(
											Object::class,
											(string) $identifier
										);

										$this->entityManager->remove($galinette);
										$this->entityManager->flush($galinette);
									} catch (ORMInvalidArgumentException | ORMException $e) {
										throw new \LogicException('It is not possible to remove this object');
									}
								}

								// ...
							}
						
					

A repository may have extra

finder and count methods

How to manage my SQL queries?

What is a Query function?

«A query function is a class which represents a SQL query.»
						
							namespace BouchonnoisCorp\Infrastructure\Storage\Doctrine\Query;

							final class ReleaseOfGalinettes
							{
								private $connection;

								public function __construct(Connection $connection)
								{
									$this->connection = $connection;
								}

								public function findGalinettes(): array
								{
									$query = 'SELECT g.* FROM galinette
							                  INNER JOIN g.censored_table_name ...'

									$statement = $this->connection->query($query);

									$galinettes = [];
									while ($galinette = $statement->fetch()) {
										$galinettes[] = new ReleasedGalinette(
											$galinette['identifier'],
											$galinette['name'],
											$galinette['birthday']
										);
									}

									return $galinettes;
								}
							}
						
					
						
							namespace BouchonnoisCorp\Domain\Read;

							final class ReleasedGalinette
							{
								private $identifier;
								private $name;
								private $birthday;

								public function __construct(
									string $identifier,
									string $name,
									string $birthday
								) {
									$this->identifier = $identifier;
									$this->name = $name;
									$this->birthday = $birthday;
								}

								public function normalize(): array
								{
									return [
										'id' => $this->identifier,
										'name' => $this->name,
										'birthday' => $this->birthday,
									];
								}
							}
						
					

To conclude!

Code and slides are available on Gitlab!

https://gitlab.com/arnolanglade/bad-or-good-repository

Thank you! Questions?

https://joind.in/talk/afbbd