save repository from save

48
Save repository from save

Upload: norbert-orzechowicz

Post on 14-Feb-2017

4.234 views

Category:

Software


2 download

TRANSCRIPT

Page 1: Save Repository From Save

Save repository from save

Page 2: Save Repository From Save

Norbert Orzechowicz @norzechowicz

Page 3: Save Repository From Save

Repository Here I should add some complicated

description of repository pattern. But instead of that…

Page 4: Save Repository From Save

Keep calm and

think about… bookshelf

Page 5: Save Repository From Save

Bookshelf specification

• it contains books

• it contains books from mixed categories

• it allows you to add new books when it’s not full

• it allows you to find and pick a book or books

• it allows you to remove specific book or books from it

Page 6: Save Repository From Save

Bookshelf specification

• it contains books

• it contains books from mixed categories

• it allows you to add new books when it’s not full

• it allows you to find and pick specific books/book

• it allows you to remove specific books/book from it

Page 7: Save Repository From Save

<?php

interface Bookshelf { /** * @param Book $book * @return bool */ public function contains(Book $book);

/** * @param Book $book */ public function add(Book $book);

/** * @param Title $title * @return Book */ public function findBy(Title $title);

/** * @param Book $book */ public function remove(Book $book); }

Page 8: Save Repository From Save

Conclusions

• bookshelf acts as a collection

• bookshelf does not handle book changes

• bookshelf implements repository pattern

Page 9: Save Repository From Save

Repository Mediates between the domain and data mapping layers using a collection-like

interface for accessing domain objects.

Martin Fowler

Page 10: Save Repository From Save

From pure data to entity

Page 11: Save Repository From Save

Database+----+-------------------+------------+----------------------------------+ | id | title | author | description | +----+-------------------+------------+----------------------------------+ | 1 | 50 shades of grey | E.L. James | Fifty Shades of Grey is a... | +----+-------------------+------------+----------------------------------+

Page 12: Save Repository From Save

But first let me introduce you few building blocks

Page 13: Save Repository From Save

Data Access Object<?php

class BookDataAccessObject { public function getByTitle($title); public function saveNew(array $data); }

Page 14: Save Repository From Save

Hydrator <?php

class BookHydrator { /** * @param array $data * @return Book */ public function hydrateBook($data = []); }

Page 15: Save Repository From Save

Converter <?php

class BookConverter { /** * @param Book $book * @return array */ public function toArray(Book $book); }

Page 16: Save Repository From Save

Do you already know where to assemble them?

Page 17: Save Repository From Save

Book Repository<?php

class Bookshelf { /** * @param Title $title * @return Book */ public function findBy(Title $title) { $bookData = $this->dao->getByTitle((string) $title); $book = $this->hydrator->hydrateBook($bookData);

return $book; } }

Page 18: Save Repository From Save

Book Repository<?php

class Bookshelf { /** * @param Book $book */ public function add(Book $book) { $data = $this->converter->toArray($book); $this->dao->saveNew($data); } }

Page 19: Save Repository From Save

What about changes?

Page 20: Save Repository From Save

Well if you don’t ask your bookshelf to handle changes why would you

like to ask repository for that?

Page 21: Save Repository From Save

Example of repository with too many responsibilities

<?php

interface Bookshelf { public function contains(Book $book);

public function add(Book $book);

public function findBy(Title $title);

public function remove(Book $book);

/** * Sometimes also Update/Handle/Persist * @param Book $book */ public function save(Book $book); }

Page 22: Save Repository From Save

Persistence repositories?

Excuses…

Page 23: Save Repository From Save

So how to handle changes?

Page 24: Save Repository From Save

Unit of WorkMaintains a list of objects affected by

a business transaction and coordinates the writing out of changes and the resolution

of concurrency problems.

Martin Fowler

Page 25: Save Repository From Save

Unit of Work<?php

class UnitOfWork { public function watch($entity);

public function remove($entity);

public function commit();

public function rollback(); }

Page 26: Save Repository From Save

UoW expected extension points

• Entity Created

• Entity Updated

• Entity Removed

Page 27: Save Repository From Save

Repository & UoW<?php

class BookRepository { public function add(Book $book) { $this->uow->watch($book); }

public function findBy(Title $title) { $bookData = $this->dao->getByTitle((string) $title); $book = $this->hydrator->hydrateBook($bookData);

$this->uow->watch($book);

return $book; } }

Page 28: Save Repository From Save

Commit?(save changes, create new entities, delete entities)

Page 29: Save Repository From Save

Dummy Update Example<?php

class BookController { public function updateBookDescriptionAction(Request $request, $title) { $book = $this->get('book.repository')->findBy(new Title($title)); $form = $this->createForm(new FormType()); $form->handle($request); if ($form->isValid()) { $book->updateDescription($form->get('description')->getData());

$this->get('unit_of_work')->commit();

return $this->redirect($this->generateUrl('homepage'); }

return $this->render('book/updateDescription.html.twig', [ 'book' => $book, 'form' => $form->createView() ]); } }

Page 30: Save Repository From Save

Dummy Remove Example<?php

class BookController { public function removeBookAction($title) { $book = $this->get('book.repository')->remove(new Title($title));

$this->get('unit_of_work')->commit();

return $this->render('book/updateDescription.html.twig', [ 'book' => $book, 'form' => $form->createView() ]); } }

Page 31: Save Repository From Save

Rollback?

Page 32: Save Repository From Save

In most web applications there is no need for rollback because objects become useless when

response is created.

Page 33: Save Repository From Save

Still wanna see more? (more of abstraction of course)

Page 34: Save Repository From Save

https://github.com/isolate-org

Isolate is a PHP framework that will help you in isolating business logic from persistence layer.

current version: 1.0.0-alpha2

https://twitter.com/isolate_php

Page 35: Save Repository From Save

IsolateThink about it as a registry of persistence contexts

Page 36: Save Repository From Save

Persistence ContextIt’s responsible for opening and closing transactions

Page 37: Save Repository From Save

TransactionIt’s an abstraction over the unit of work (more or less)

Page 38: Save Repository From Save

Repository with Isolate<?php

class BookRepository { public function add(Book $book) { $persistenceContext = $this->get('isolate')->getContext(); $transaction = $persistenceContext->openTransaction(); $transaction->persist($book); }

public function findBy(Title $title) { $bookData = $this->dao->getByTitle((string) $title); $book = $this->hydrator->hydrateBook($bookData);

$persistenceContext = $this->get('isolate')->getContext();

if ($persistenceContext->hasOpenTransaction()) { $transaction = $persistenceContext->openTransaction(); $transaction->persist($book); }

return $book; } }

Page 39: Save Repository From Save

Update action example<?php

class BookController { public function updateBookDescriptionAction(Request $request, $title) { $this->get('isolate')->getContext()->openTransaction();

$book = $this->get('book.repository')->findBy(new Title($title)); $form = $this->createForm(new FormType()); $form->handle($request); if ($form->isValid()) { $book->updateDescription($form->get('description')->getData());

$this->get('isolate')->getContext()->closeTransaction();

return $this->redirect($this->generateUrl('homepage'); }

return $this->render('book/updateDescription.html.twig', [ 'book' => $book, 'form' => $form->createView() ]); } }

Page 40: Save Repository From Save

But there is more code in this example….

Page 41: Save Repository From Save

Refactoring• implement commands and command handlers

(tactician)

• implement middlewares (tactician)

• move isolate transaction management to middlewares

• move business logic into command handlers

Page 42: Save Repository From Save

Transaction middleware <?php

/** * This code is available in Isolate Tactician Bridge */ class TransactionMiddleware implements Middleware { public function execute($command, callable $next) { $context = $this->isolate->getContext(); $transaction = $context->openTransaction();

try { $returnValue = $next($command); $context->closeTransaction();

return $returnValue; } catch (\Exception $e) { $transaction->rollback(); throw $e; } } }

Page 43: Save Repository From Save

Update book command handler

<?php

class UpdateBookHandler { public function handle(UpdateBookCommand $command) { $book = $this->bookshelf->findBy(new Title($command->getTitle()));

if (empty($book)) { throw new BookNotFoundException(); }

$book->updateDescription($command->getDescription()); } }

Page 44: Save Repository From Save

Update book controller <?php

class BookController { public function updateBookAction(Request $request, $title) { $form = $this->createForm(new FormType()); $form->handle($request);

if ($form->isValid()) { $command = new UpdateBookCommand($title, $form->get('description')->getData()); $this->get('command_bus')->handle($command);

return $this->redirect($this->generateUrl('homepage'); }

return $this->render('book/updateDescription.html.twig', [ 'book' => $book, 'form' => $form->createView() ]); } }

Page 45: Save Repository From Save

Isolate extensions

• Tactician Bridge https://github.com/isolate-org/tactician-bridge

• Doctrine Bridge https://github.com/isolate-org/doctrine-bridge

• Symfony Bundle https://github.com/isolate-org/symfony-bundle

Page 46: Save Repository From Save

Advantages • application totally decoupled from storage

• possibility to switch storage for tests

• possibility to replace ORM with ODM, webservice, filesystem or anything else

• possibility to delay a decision about storage type

• possibility to increase performance by replacing auto-generated sql queries with custom, optimized queries

• clean architecture not impacted by persistence layer

Page 47: Save Repository From Save

Disadvantages

• higher entry point for junior developers

• require better understanding of how your storage works

Page 48: Save Repository From Save

Questions?