Press [s] to read the speaker notes (will open a new window).
Application complexity keeps increasing
Languages / Tools / Design patterns / …
Agile / BDD / DDD / …
« Web applications » over « Websites »
« Business rules » over « Code »
What should we do with these business rules?
We want to …
… understand them ;
… easily express them ;
… reuse them ;
… compose them.
But… how do we do that?
Webreader
« A book supports the web reader if it's an ePub not protected by Adobe DRM »
Repositories "à la Doctrine" ?
class DoctrineBookRepository implements BookRepository
{
public function add(Ebook $book) { }
public function remove(Ebook $book) { }
public function findByEan($ean) { }
public function findByTitle($title) { }
public function findPublished() { }
public function findViewableOnline() { }
public function findNotViewableOnline() { }
public function findPublishedAndViewableOnline() { }
// …
}
What does the Bible say?
“A Repository mediates between the domain and data mapping layers, acting like an in-memory domain object collection.
Client objects construct query specifications declaratively and submit them to Repository for satisfaction.”
format IN :formats_epub AND protection != "adobe drm"
« A book supports the web reader if it's an ePub not protected by Adobe DRM »
Same usage
$rule = 'format IN :formats_epub AND protection != "adobe drm"';
// use the textual rule
$isViewableOnline = $rulerz->satisfies($book, $rule, [
'formats_epub' => ['epub', 'epub 3', 'epub fixed layout'],
]); // bool(true)
Same specification
class SupportsWebReader extends AbstractSpecification
{
public function getRule()
{
return 'format IN :formats_epub AND protection != "adobe drm"';
}
public function getParameters()
{
return [
'formats_epub' => ['epub', 'epub 3', 'epub fixed layout'],
];
}
}
« A book supports the web reader if it's an ePub not protected by Adobe DRM »
Same usage
// build a specification object
$spec = (new SupportsWebReader())
->andX(new AvailableInCountry('FR'))
->andX((new PublisherBlacklisted())->not());
$isViewableOnline = $rulerz->satisfiesSpec($book, $spec); // bool(true)
Usage on a datasource
// our app uses Doctrine to query the database
$queryBuilder = $entityManager
->createQueryBuilder()
->select('book')
->from('Entity\Book', 'book');
// and we want to find the viewable online books
$viewableOnlineBooks = $rulerz->filterSpec($queryBuilder, $spec);
var_dump($viewableOnlineBooks); // array<Entity\Book>
Under the hood
Architecture
A few use cases
Repositories – Before
class DoctrineBookRepository implements BookRepository
{
public function findByEan($ean) { }
public function findByTitle($title) { }
public function findPublished() { }
public function findViewableOnline() { }
public function findNotViewableOnline() { }
public function findPublishedAndViewableOnline() { }
// …
}
Repositories – After
class DoctrineBookRepository implements BookRepository
{
public function matching(Specification $spec)
{
$qb = $this->createQueryBuilder('book');
return $this->rulerz->satisfiesSpec($qb, $spec);
}
}