cqrs v rohlik.cz
TRANSCRIPT
CQRS v@ProchazkaFilip
Co si povíme- Na čem běží Rohlik.cz- Co je ORM, a proč ho používat- Seznámení s Doctrine 2- Seznámení s ElasticSearch- CQRS
Na čem běžíme
Na čem běžíme: platforma
- PHP-FPM- NGINX- Percona (MySQL)- Redis- RabbitMQ- ElasticSearch
Na čem běžíme: aplikace
- Nette Framework- Doctrine 2 ORM- Symfony komponenty- React.PHP
Na čem běžíme: podpůrné technologie
- NewRelic- Papertrail- Travis CI- GitHub- Slack- Composer- ...
Proč používat ORM
“Nejhloupější” modelyclass OrdersModel{
private $db;function __construct(DbConnection $db) {
$this->db = $db;}
function findAll() {return $this->db->query("SELECT * FROM orders");
}
ORM?
Object-relational mapping is a programming technique for converting data between incompatible type systems in object-oriented programming languages.
via https://en.wikipedia.org/wiki/Object-relational_mapping
Pseudo-ORMclass OrdersModel{
private $db;function __construct(DbConnection $db) {
$this->db = $db;}function findAll() {
return $this->mapResult($this->db->query("SELECT * FROM orders")
);}
Active Record$user = User::create(['name' => 'Tito']);
$user = User::find_by_name('Tito');
$user->name = 'Tito Jr';$user->save();
$user->delete();
SOLID
● Single responsibility principle● Open/closed principle● Liskov substitution principle● Interface segregation principle● Dependency inversion principle
Data Mapper
$user = new User('Pepa');
$em->persist($user);$em->flush();
$user = $em->find(User::class, 1);
Data Mapper- Můžu mít kompletně objektový model- Entity jsou objekty, které se nestarají o persistenci- To že je entita uložena v DB je “detail”
Doctrine 2 ORM
Doctrine: architektura
DBAL
Doctrine ORM
Entities
Services
Facades
Database
EntityManager
Repository
Doctrine: DBAL
- Obálka nad PDO- Abstrakce mezi databázemi- Primitivní datové typy- Schéma- Snadnější DB migrace
Doctrine:
- Persistence entit- Událostní systém (Eventy)- UnitOfWork- Identity Mapa- DQL
Doctrine ORM
Doctrine:
class Product{ /** @var int */ private $id; /** @var string */ private $name;
Entities
Doctrine:
/** @Entity @Table(name="products") **/class Product{ /** @Id @Column(type="integer") @GeneratedValue **/ private $id; /** @Column(type="string") **/ private $name;
Entities
Doctrine: Services
- Logické jednotky a operace v aplikaci- Může být více services na jednu entitu- Může být více entit na jednu service
Doctrine: DQL
SELECT b, e, rFROM Bug bJOIN b.engineer eJOIN b.reporter rORDER BY b.created DESC
Doctrine: Facades
- Zapouzdřují services- Jedna metoda využívá několik service
Doctrine: nevýhody
- Je nutné chápat OOP- Je nutné chápat DataMapper a spol.- Leaky abstraction- Výkon
ElasticSearch
ElasticSearch
- schema-less search engine- umí schéma- škálování za hubičku- REST api
ElasticSearch
- obsahuje index- indexy obsahují typy- typy obsahují dokumenty- dokumenty mají id a fieldy
GET /rohlikcz/product/123
ElasticSearch: (de)normalizace
- app -> databáze = normalizace- méně dat- méně duplikací- horší na dotazování
- databáze -> ES = denormalizace- duplikace, duplikace, duplikace- so fucking fast
ElasticSearchGET /megacorp/employee/_search?q=last_name:Smith{ ... "hits": { "total": 2, "hits": [ { ... "_source": { "first_name": "John", "last_name": "Smith", "age": 25, "about": "I love to go rock climbing", "interests": [ "sports", "music" ] } },
ElasticSearch + Doctrine
- Entity se nemapují 1:1 na typy- Typy mají vlastní serializery- V serializerech můžu pokládat SQL dotazy- Synchronizace běží v RabbitMQ workeru
ElasticSearch + Doctrine
$product = $em->find(Product::class, 1);$product->name = 'Banán';$em->flush();
// volá se automaticky$searchSync->append($product);
ElasticSearch + Doctrine
ElasticSearch + Doctrineclass ProductSerializer{ public function serialize($product)
{ return [ 'id' => $product->id, 'name' => $product->name, ]; }
ElasticSearch + Doctrine$query = (new Query(...))
->andMust(...);
$resultSet = $searchManager->search($query);
$products = $productsMapper->mapResult($resultSet);
ElasticSearch + Doctrinefunction mapResult(ResultSet $resultSet){
$result = [];
foreach ($resultSet->results as $item) {$result[$item->id] =
new ProductDTO($item->id, $item->source);}
return $result;}
ElasticSearch + Doctrineclass ProductDTO{
private $id;private $source;
public function __construct($id, $source){
$this->id = $id;$this->source = $source;
}
ElasticSearch + Doctrineclass ProductDTO{
// ...
public function __get($field){
return $this->source[$field];}
Command Query
Responsibility Segregation
CQS
It states that every method should either be a command that performs an action, or a query that returns data to the caller, but not both.In other words, Asking a question should not change the answer.
via https://en.wikipedia.org/wiki/Command%E2%80%93query_separation
CQRS
Applies the CQS principle by using separate Query and Command objects to retrieve and modify data, respectively.
via https://en.wikipedia.org/wiki/Command%E2%80%93query_separation
CQRS
- lepší škálování- větší komplexita- častěji je snažší použít CRUD
Shrnutí
- ORM je super- S ORM je jednodušší se střelit do nohy- ElasticSearch je super- CQRS je super
Dotazy?
Díky za pozornost!@ProchazkaFilip / @PecemeRohlik