SlideShare a Scribd company logo
Symfony Components


and Design Patterns
Symfony World - Symfony components and design patterns
Design patterns?
Symfony World - Symfony components and design patterns
Symfony World - Symfony components and design patterns
Why should you care?
Communication
Communication


Reduced amount of code in one chunk
Communication


Reduced amount of code in one chunk


Increased cohesion and reduced coupling
Communication


Reduced amount of code in one chunk


Increased cohesion and reduced coupling


Extendibility
Communication


Reduced amount of code in one chunk


Increased cohesion and reduced coupling


Extendibility


SOLID
Increase code quality
Can we measure it?
Avg. Logic Lines of Code
Avg. Logic Lines of Code LLoC
Avg. Logic Lines of Code LLoC
Amount of classes
Avg. Logic Lines of Code LLoC
Amount of classes AoC
Avg. Logic Lines of Code LLoC
Amount of classes AoC
Avg. Cyclomatic Complexity
Avg. Logic Lines of Code LLoC
Amount of classes AoC
Avg. Cyclomatic Complexity ACC
Let’s see the code
Sample Project
final class CheckoutController
{
private array $repository = [];
public function cartAction(string $operation, array $options): Cart
{
// Fetch object from repository
if (isset($this->repository[$options['cart_id']])) {
/** @var Cart $cart */
$cart = $this->repository[$options['cart_id']];
} else {
$cart = new Cart($options['cart_id']);
}
// Perform operation
if ('add_to_cart' === $operation) {
$cart->addProduct($options['product'], $options['type'], $options['quantity'], $options['price']);
} elseif ('remove_from_cart' === $operation) {
$cart->removeProduct($options['product']);
} else {
throw new InvalidArgumentException('operation not supported');
}
// Apply promotion
if ($cart->getPrice() >= 1000) {
$cart->applyPercentageDiscount(0.1);
}
// Apply taxation
/** @var CartItem $item */
foreach ($cart->getItems() as $item) {
if ('shirt' === $item->getType()) {
$item->setTaxationPercentage(0.23);
}
if ('book' === $item->getType()) {
$item->setTaxationPercentage(0.08);
}
}
// Save into repository
$this->repository[$options['cart_id']] = $cart;
return $cart;
}
}
final class CheckoutController
{
private array $repository = [];
public function cartAction(string $operation, array $options): Cart
{
// Fetch object from repository
if (isset($this->repository[$options['cart_id']])) {
/** @var Cart $cart */
$cart = $this->repository[$options['cart_id']];
} else {
$cart = new Cart($options['cart_id']);
}
// Perform operation
if ('add_to_cart' === $operation) {
$cart->addProduct($options['product'], $options['type'], $options['quantity'], $options['price']);
} elseif ('remove_from_cart' === $operation) {
$cart->removeProduct($options['product']);
} else {
throw new InvalidArgumentException('operation not supported');
}
// Apply promotion
if ($cart->getPrice() >= 1000) {
$cart->applyPercentageDiscount(0.1);
}
// Apply taxation
/** @var CartItem $item */
foreach ($cart->getItems() as $item) {
if ('shirt' === $item->getType()) {
$item->setTaxationPercentage(0.23);
}
if ('book' === $item->getType()) {
$item->setTaxationPercentage(0.08);
}
}
// Save into repository
$this->repository[$options['cart_id']] = $cart;
return $cart;
}
{
// Fetch object from repository
if (isset($this->repository[$options['cart_id']])) {
/** @var Cart $cart */
$cart = $this->repository[$options['cart_id']];
} else {
$cart = new Cart($options['cart_id']);
}
// Perform operation
if ('add_to_cart' === $operation) {
$cart->addProduct(
$options[‘product'],
$options[‘type'],
$options[‘quantity'],
$options[‘price']
);
} elseif ('remove_from_cart' === $operation) {
$cart->removeProduct($options['product']);
} else {
throw new InvalidArgumentException('operation not supported');
}
// Apply promotion
if ($cart->getPrice() >= 1000) {
$cart->applyPercentageDiscount(0.1);
}
// Apply taxation
/** @var CartItem $item */
foreach ($cart->getItems() as $item) {
if ('shirt' === $item->getType()) {
$item->setTaxationPercentage(0.23);
}
if ('book' === $item->getType()) {
$item->setTaxationPercentage(0.08);
}
}
// Save into repository
final class CheckoutController
{
private array $repository = [];
public function cartAction(string $operation, array $options): Cart
{
// Fetch object from repository
if (isset($this->repository[$options['cart_id']])) {
/** @var Cart $cart */
$cart = $this->repository[$options['cart_id']];
} else {
$cart = new Cart($options['cart_id']);
}
// Perform operation
if ('add_to_cart' === $operation) {
$cart->addProduct($options['product'], $options['type'], $options['quantity'], $options['price']);
} elseif ('remove_from_cart' === $operation) {
$cart->removeProduct($options['product']);
} else {
throw new InvalidArgumentException('operation not supported');
}
// Apply promotion
if ($cart->getPrice() >= 1000) {
$cart->applyPercentageDiscount(0.1);
}
// Apply taxation
/** @var CartItem $item */
foreach ($cart->getItems() as $item) {
if ('shirt' === $item->getType()) {
$item->setTaxationPercentage(0.23);
}
if ('book' === $item->getType()) {
$item->setTaxationPercentage(0.08);
}
}
// Save into repository
$this->repository[$options['cart_id']] = $cart;
return $cart;
}
}
private array $repository = [];
public function cartAction(string $operation, array $options): Cart
{
// Fetch object from repository
if (isset($this->repository[$options['cart_id']])) {
/** @var Cart $cart */
$cart = $this->repository[$options['cart_id']];
} else {
$cart = new Cart($options['cart_id']);
}
// Perform operation
if ('add_to_cart' === $operation) {
$cart->addProduct($options['product'], $options['type'], $options['quantity'], $options['price']);
} elseif ('remove_from_cart' === $operation) {
$cart->removeProduct($options['product']);
} else {
throw new InvalidArgumentException('operation not supported');
}
// Apply promotion
if ($cart->getPrice() >= 1000) {
$cart->applyPercentageDiscount(0.1);
}
// Apply taxation
/** @var CartItem $item */
foreach ($cart->getItems() as $item) {
if ('shirt' === $item->getType()) {
$item->setTaxationPercentage(0.23);
}
if ('book' === $item->getType()) {
$item->setTaxationPercentage(0.08);
}
}
// Save into repository
$this->repository[$options['cart_id']] = $cart;
final class CheckoutController
{
private array $repository = [];
public function cartAction(string $operation, array $options): Cart
{
// Fetch object from repository
if (isset($this->repository[$options['cart_id']])) {
/** @var Cart $cart */
$cart = $this->repository[$options['cart_id']];
} else {
$cart = new Cart($options['cart_id']);
}
// Perform operation
if ('add_to_cart' === $operation) {
$cart->addProduct($options['product'], $options['type'], $options['quantity'], $options['price']);
} elseif ('remove_from_cart' === $operation) {
$cart->removeProduct($options['product']);
} else {
throw new InvalidArgumentException('operation not supported');
}
// Apply promotion
if ($cart->getPrice() >= 1000) {
$cart->applyPercentageDiscount(0.1);
}
// Apply taxation
/** @var CartItem $item */
foreach ($cart->getItems() as $item) {
if ('shirt' === $item->getType()) {
$item->setTaxationPercentage(0.23);
}
if ('book' === $item->getType()) {
$item->setTaxationPercentage(0.08);
}
}
// Save into repository
$this->repository[$options['cart_id']] = $cart;
return $cart;
}
}
final class CheckoutController
{
private array $repository = [];
public function cartAction(string $operation, array $options): Cart
{
// Fetch object from repository
if (isset($this->repository[$options['cart_id']])) {
/** @var Cart $cart */
$cart = $this->repository[$options['cart_id']];
} else {
$cart = new Cart($options['cart_id']);
}
// Perform operation
if ('add_to_cart' === $operation) {
$cart->addProduct($options['product'], $options['type'], $options['quantity'], $options['price']);
} elseif ('remove_from_cart' === $operation) {
$cart->removeProduct($options['product']);
} else {
throw new InvalidArgumentException('operation not supported');
}
// Apply promotion
if ($cart->getPrice() >= 1000) {
$cart->applyPercentageDiscount(0.1);
}
// Apply taxation
/** @var CartItem $item */
foreach ($cart->getItems() as $item) {
if ('shirt' === $item->getType()) {
$item->setTaxationPercentage(0.23);
}
if ('book' === $item->getType()) {
$item->setTaxationPercentage(0.08);
}
}
// Save into repository
$this->repository[$options['cart_id']] = $cart;
return $cart;
}
}
Avg. Logic Lines of Code 40
Amount of classes 1
Avg. Cyclomatic Complexity 8
LLoC AoC ACC
Sample project 40 1 8
Single responsibility principle


Open close principle


Liskov substitution principle


Interface segregation principle


Dependency inversion principle
Single responsibility principle


Open close principle


Liskov substitution principle


Interface segregation principle


Dependency inversion principle
Composition
Preparation
final class CheckoutController
{
private array $repository = [];
public function cartAction(string $operation, array $options): Cart
{
// Fetch object from repository
if (isset($this->repository[$options['cart_id']])) {
/** @var Cart $cart */
$cart = $this->repository[$options['cart_id']];
} else {
$cart = new Cart($options['cart_id']);
}
// Perform operation
if ('add_to_cart' === $operation) {
$cart->addProduct($options['product'], $options['type'], $options['quantity'], $options['price']);
} elseif ('remove_from_cart' === $operation) {
$cart->removeProduct($options['product']);
} else {
throw new InvalidArgumentException('operation not supported');
}
// Apply promotion
if ($cart->getPrice() >= 1000) {
$cart->applyPercentageDiscount(0.1);
}
// Apply taxation
/** @var CartItem $item */
foreach ($cart->getItems() as $item) {
if ('shirt' === $item->getType()) {
$item->setTaxationPercentage(0.23);
}
if ('book' === $item->getType()) {
$item->setTaxationPercentage(0.08);
}
}
// Save into repository
$this->repository[$options['cart_id']] = $cart;
return $cart;
}
}
$cart = new Cart($options['cart_id']);
}
// Perform operation
if ('add_to_cart' === $operation) {
$cart->addProduct($options['product'], $options['type'], $options['quantity'], $options['price']);
} elseif ('remove_from_cart' === $operation) {
$cart->removeProduct($options['product']);
} else {
throw new InvalidArgumentException('operation not supported');
}
// Apply promotion
if ($cart->getPrice() >= 1000) {
$cart->applyPercentageDiscount(0.1);
}
// Apply taxation
/** @var CartItem $item */
foreach ($cart->getItems() as $item) {
if ('shirt' === $item->getType()) {
$item->setTaxationPercentage(0.23);
}
if ('book' === $item->getType()) {
$item->setTaxationPercentage(0.08);
}
}
// Save into repository
$this->repository[$options['cart_id']] = $cart;
return $cart;
}
}
private array $repository = [];
public function cartAction(string $operation, array $options): Cart
{
// Fetch object from repository
if (isset($this->repository[$options['cart_id']])) {
/** @var Cart $cart */
$cart = $this->repository[$options['cart_id']];
} else {
$cart = new Cart($options['cart_id']);
}
// Perform operation
if ('add_to_cart' === $operation) {
$cart->addProduct($options['product'], $options['type'], $options['quantity'], $options['price']);
} elseif ('remove_from_cart' === $operation) {
$cart->removeProduct($options['product']);
} else {
throw new InvalidArgumentException('operation not supported');
}
// Apply promotion
$this->applyPromotion($cart);
// Apply taxation
$this->applyTaxation($cart);
// Save into repository
$this->repository[$options['cart_id']] = $cart;
return $cart;
}
}
private function applyTaxation(Cart $cart): void
{
/** @var CartItem $item */
foreach ($cart->getItems() as $item) {
if ('shirt' === $item->getType()) {
$item->setTaxationPercentage(0.23);
}
if ('book' === $item->getType()) {
$item->setTaxationPercentage(0.08);
}
}
}
private function applyPromotion(Cart $cart): void
{
if ($cart->getPrice() >= 1000) {
$cart->applyPercentageDiscount(0.1);
}
}
private array $repository = [];
public function cartAction(string $operation, array $options): Cart
{
// Fetch object from repository
if (isset($this->repository[$options['cart_id']])) {
/** @var Cart $cart */
$cart = $this->repository[$options['cart_id']];
} else {
$cart = new Cart($options['cart_id']);
}
// Perform operation
if ('add_to_cart' === $operation) {
$cart->addProduct($options['product'], $options['type'], $options['quantity'], $options['price']);
} elseif ('remove_from_cart' === $operation) {
$cart->removeProduct($options['product']);
} else {
throw new InvalidArgumentException('operation not supported');
}
// Apply promotion
$this->applyPromotion($cart);
// Apply taxation
$this->applyTaxation($cart);
// Save into repository
$this->repository[$options['cart_id']] = $cart;
return $cart;
}
}
final class CheckoutController
{
private array $repository = [];
public function cartAction(string $operation, array $options): Cart
{
// Fetch object from repository
if (isset($this->repository[$options['cart_id']])) {
/** @var Cart $cart */
$cart = $this->repository[$options['cart_id']];
} else {
$cart = new Cart($options['cart_id']);
}
// Perform operation
if ('add_to_cart' === $operation) {
$cart->addProduct($options['product'], $options['type'], $options['quantity'], $options['price']);
} elseif ('remove_from_cart' === $operation) {
$cart->removeProduct($options['product']);
} else {
throw new InvalidArgumentException('operation not supported');
}
$this->processOrder($cart);
// Save into repository
$this->repository[$options['cart_id']] = $cart;
return $cart;
}
}
private function processOrder(Cart $cart): void
{
// Apply promotion
$this->applyPromotion($cart);
// Apply taxation
$this->applyTaxation($cart);
}
final class CheckoutController
{
private array $repository = [];
public function cartAction(string $operation, array $options): Cart
{
// Fetch object from repository
if (isset($this->repository[$options['cart_id']])) {
/** @var Cart $cart */
$cart = $this->repository[$options['cart_id']];
} else {
$cart = new Cart($options['cart_id']);
}
// Perform operation
if ('add_to_cart' === $operation) {
$cart->addProduct($options['product'], $options['type'], $options['quantity'], $options['price']);
} elseif ('remove_from_cart' === $operation) {
$cart->removeProduct($options['product']);
} else {
throw new InvalidArgumentException('operation not supported');
}
$this->processOrder($cart);
// Save into repository
$this->repository[$options['cart_id']] = $cart;
return $cart;
}
private function applyTaxation(Cart $cart): void
{
/** @var CartItem $item */
foreach ($cart->getItems() as $item) {
if ('shirt' === $item->getType()) {
$item->setTaxationPercentage(0.23);
}
if ('book' === $item->getType()) {
$item->setTaxationPercentage(0.08);
}
}
}
private function applyPromotion(Cart $cart): void
{
if ($cart->getPrice() >= 1000) {
$cart->applyPercentageDiscount(0.1);
}
}
/**
* @param Cart $cart
*/
private function processOrder(Cart $cart): void
{
// Apply promotion
$this->applyPromotion($cart);
// Apply taxation
$this->applyTaxation($cart);
}
}
final class CheckoutController
{
private array $repository = [];
public function cartAction(string $operation, array $options): Cart
{
// Fetch object from repository
if (isset($this->repository[$options['cart_id']])) {
/** @var Cart $cart */
$cart = $this->repository[$options['cart_id']];
} else {
$cart = new Cart($options['cart_id']);
}
// Perform operation
if ('add_to_cart' === $operation) {
$cart->addProduct($options['product'], $options['type'], $options['quantity'], $options['price']);
} elseif ('remove_from_cart' === $operation) {
$cart->removeProduct($options['product']);
} else {
throw new InvalidArgumentException('operation not supported');
}
$this->processOrder($cart);
// Save into repository
$this->repository[$options['cart_id']] = $cart;
return $cart;
}
}
final class CheckoutController
{
private array $repository = [];
public function cartAction(string $operation, array $options): Cart
{
// Fetch object from repository
if (isset($this->repository[$options['cart_id']])) {
/** @var Cart $cart */
$cart = $this->repository[$options['cart_id']];
} else {
$cart = new Cart($options['cart_id']);
}
// Perform operation
if ('add_to_cart' === $operation) {
$cart->addProduct($options['product'], $options['type'], $options['quantity'], $options['price']);
} elseif ('remove_from_cart' === $operation) {
$cart->removeProduct($options['product']);
} else {
throw new InvalidArgumentException('operation not supported');
}
$this->processOrder($cart);
// Save into repository
$this->repository[$options['cart_id']] = $cart;
return $cart;
}
}
Extraction
final class CheckoutController
{
private array $repository = [];
public function cartAction(string $operation, array $options): Cart
{
// Fetch object from repository
if (isset($this->repository[$options['cart_id']])) {
/** @var Cart $cart */
$cart = $this->repository[$options['cart_id']];
} else {
$cart = new Cart($options['cart_id']);
}
// Perform operation
if ('add_to_cart' === $operation) {
$cart->addProduct($options['product'], $options['type'], $options['quantity'], $options['price']);
} elseif ('remove_from_cart' === $operation) {
$cart->removeProduct($options['product']);
} else {
throw new InvalidArgumentException('operation not supported');
}
$this->orderProcessor->processOrder($cart);
// Save into repository
$this->repository[$options['cart_id']] = $cart;
return $cart;
}
}
final class CompositeOrderProcessor implements OrderProcessor
{
public function processOrder(Cart $cart): void
{
// Apply promotion
$this->applyPromotion($cart);
// Apply taxation
$this->applyTaxation($cart);
}
private function applyTaxation(Cart $cart): void
{
/** @var CartItem $item */
foreach ($cart->getItems() as $item) {
if ('shirt' === $item->getType()) {
$item->setTaxationPercentage(0.23);
}
if ('book' === $item->getType()) {
$item->setTaxationPercentage(0.08);
}
}
}
private function applyPromotion(Cart $cart): void
{
if ($cart->getPrice() >= 1000) {
$cart->applyPercentageDiscount(0.1);
}
}
}
LLoC AoC ACC
Sample project 40 1 8
Extracted processor 61 2 4.5
Symfony World - Symfony components and design patterns
final class CompositeOrderProcessor implements OrderProcessor
{
public function processOrder(Cart $cart): void
{
// Apply promotion
$this->applyPromotion($cart);
// Apply taxation
$this->applyTaxation($cart);
}
private function applyTaxation(Cart $cart): void
{
/** @var CartItem $item */
foreach ($cart->getItems() as $item) {
if ('shirt' === $item->getType()) {
$item->setTaxationPercentage(0.23);
}
if ('book' === $item->getType()) {
$item->setTaxationPercentage(0.08);
}
}
}
private function applyPromotion(Cart $cart): void
{
if ($cart->getPrice() >= 1000) {
$cart->applyPercentageDiscount(0.1);
}
}
}
final class CompositeOrderProcessor implements OrderProcessor
{
public function processOrder(Cart $cart): void
{
// Apply promotion
$this->applyPromotion($cart);
// Apply taxation
$this->applyTaxation($cart);
}
private function applyTaxation(Cart $cart): void
{
/** @var CartItem $item */
foreach ($cart->getItems() as $item) {
if ('shirt' === $item->getType()) {
$item->setTaxationPercentage(0.23);
}
if ('book' === $item->getType()) {
$item->setTaxationPercentage(0.08);
}
}
}
private function applyPromotion(Cart $cart): void
{
if ($cart->getPrice() >= 1000) {
$cart->applyPercentageDiscount(0.1);
}
}
}
final class OrderTaxationProcessor implements OrderProcessor
{
public function processOrder(Cart $cart): void
{
/** @var CartItem $item */
foreach ($cart->getItems() as $item) {
if ('shirt' === $item->getType()) {
$item->setTaxationPercentage(0.23);
}
if ('book' === $item->getType()) {
$item->setTaxationPercentage(0.08);
}
}
}
}
final class CompositeOrderProcessor implements OrderProcessor
{
public function processOrder(Cart $cart): void
{
// Apply promotion
$this->applyPromotion($cart);
// Apply taxation
$this->applyTaxation($cart);
}
private function applyPromotion(Cart $cart): void
{
if ($cart->getPrice() >= 1000) {
$cart->applyPercentageDiscount(0.1);
}
}
}
final class OrderPromotionProcessor implements OrderProcessor
{
public function processOrder(Cart $cart): void
{
if ($cart->getPrice() >= 1000) {
$cart->applyPercentageDiscount(0.1);
}
}
}
final class CompositeOrderProcessor implements OrderProcessor
{
private OrderPromotionProcessor $orderPromotionProcessor;
private OrderTaxationProcessor $orderTaxationProcessor;
public function __construct(
OrderPromotionProcessor $orderPromotionProcessor,
OrderTaxationProcessor $orderTaxationProcessor
) {
$this->orderPromotionProcessor = $orderPromotionProcessor;
$this->orderTaxationProcessor = $orderTaxationProcessor;
}
public function processOrder(Cart $cart): void
{
// Apply promotion
$this->orderPromotionProcessor->processOrder($cart);
// Apply taxation
$this->orderTaxationProcessor->processOrder($cart);
}
}
LLoC AoC ACC
Sample project 40 1 8
Extracted processor 61 2 4.5


Separated processors 76 4 2.75
Symfony World - Symfony components and design patterns
final class CompositeOrderProcessor implements OrderProcessor
{
private OrderPromotionProcessor $orderPromotionProcessor;
private OrderTaxationProcessor $orderTaxationProcessor;
public function __construct(
OrderPromotionProcessor $orderPromotionProcessor,
OrderTaxationProcessor $orderTaxationProcessor
) {
$this->orderPromotionProcessor = $orderPromotionProcessor;
$this->orderTaxationProcessor = $orderTaxationProcessor;
}
public function processOrder(Cart $cart): void
{
// Apply promotion
$this->orderPromotionProcessor->processOrder($cart);
// Apply taxation
$this->orderTaxationProcessor->processOrder($cart);
}
}
final class CompositeOrderProcessor implements OrderProcessor
{
private iterable $orderProcessors;
public function __construct(
OrderPromotionProcessor $orderPromotionProcessor,
OrderTaxationProcessor $orderTaxationProcessor
) {
$this->orderProcessors = [
$orderPromotionProcessor,
$orderTaxationProcessor
];
}
public function processOrder(Cart $cart): void
{
foreach ($this->orderProcessors as $orderProcessor) {
$orderProcessor->processOrder($cart);
}
}
}
final class CompositeOrderProcessor implements OrderProcessor
{
private iterable $orderProcessors;
public function __construct(
OrderPromotionProcessor $orderPromotionProcessor,
OrderTaxationProcessor $orderTaxationProcessor
) {
$this->orderProcessors = [
$orderPromotionProcessor,
$orderTaxationProcessor
];
}
public function processOrder(Cart $cart): void
{
foreach ($this->orderProcessors as $orderProcessor) {
$orderProcessor->processOrder($cart);
}
}
}
final class CompositeOrderProcessor implements OrderProcessor
{
private iterable $orderProcessors;
public function __construct(
OrderPromotionProcessor $orderPromotionProcessor,
OrderTaxationProcessor $orderTaxationProcessor
) {
$this->orderProcessors = [
$orderPromotionProcessor,
$orderTaxationProcessor
];
}
public function processOrder(Cart $cart): void
{
foreach ($this->orderProcessors as $orderProcessor) {
$orderProcessor->processOrder($cart);
}
}
}
final class CompositeOrderProcessor implements OrderProcessor
{
private iterable $orderProcessors;
public function __construct(
OrderPromotionProcessor $orderPromotionProcessor,
OrderTaxationProcessor $orderTaxationProcessor
) {
$this->orderProcessors = [
$orderPromotionProcessor,
$orderTaxationProcessor
];
}
public function processOrder(Cart $cart): void
{
foreach ($this->orderProcessors as $orderProcessor) {
$orderProcessor->processOrder($cart);
}
}
}
final class CompositeOrderProcessor implements OrderProcessor
{
private iterable $orderProcessors;
public function __construct(iterable $orderProcessors)
{
$this->orderProcessors = $orderProcessors;
}
public function processOrder(Cart $cart): void
{
foreach ($this->orderProcessors as $orderProcessor) {
$orderProcessor->processOrder($cart);
}
}
}
final class CompositeOrderProcessor implements OrderProcessor
{
private iterable $orderProcessors;
public function __construct(iterable $orderProcessors)
{
$this->orderProcessors = $orderProcessors;
}
public function processOrder(Cart $cart): void
{
foreach ($this->orderProcessors as $orderProcessor) {
$orderProcessor->processOrder($cart);
}
}
}
SymfonyDesignPatternsProcessorOrderProcessor:
class: 'SymfonyDesignPatternsProcessorCompositeOrderProcessor'
arguments:
- [
'@SymfonyDesignPatternsProcessorOrderPromotionProcessor',
'@SymfonyDesignPatternsProcessorOrderTaxationProcessor'
]
Can Symfony help us with it?
SymfonyDesignPatternsProcessorOrderProcessor:
class: 'SymfonyDesignPatternsProcessorCompositeOrderProcessor'
arguments:
- !tagged_iterator app.order_processor
SymfonyDesignPatternsProcessorOrderProcessor:
class: 'SymfonyDesignPatternsProcessorCompositeOrderProcessor'
arguments:
- !tagged_iterator app.order_processor
SymfonyDesignPatternsProcessorOrderPromotionProcessor:
tags:
- app.order_processor
SymfonyDesignPatternsProcessorOrderTaxationProcessor:
tags:
- app.order_processor
LLoC AoC ACC
Sample project 40 1 8
Extracted processor 61 2 4.5


Separated processors 76 4 2.75
Composite iteration 73 4 3
Single responsibility principle


Open close principle


Liskov substitution principle


Interface segregation principle


Dependency inversion principle
LLoC AoC ACC
Sample project 40 1 8
Composite iteration 73 4 3
Strategy
final class OrderTaxationProcessor implements OrderProcessor
{
public function processOrder(Cart $cart): void
{
/** @var CartItem $item */
foreach ($cart->getItems() as $item) {
if ('shirt' === $item->getType()) {
$item->setTaxationPercentage(0.23);
}
if ('book' === $item->getType()) {
$item->setTaxationPercentage(0.08);
}
}
}
}
final class OrderTaxationProcessor implements OrderProcessor
{
public function processOrder(Cart $cart): void
{
/** @var CartItem $item */
foreach ($cart->getItems() as $item) {
if ('shirt' === $item->getType()) {
$item->setTaxationPercentage(0.23);
}
if ('book' === $item->getType()) {
$item->setTaxationPercentage(0.08);
}
}
}
}
final class OrderTaxationProcessor implements OrderProcessor
{
private TaxationStrategyDelegator $taxationStrategy;
public function __construct(TaxationStrategyDelegator $taxationStrategy)
{
$this->taxationStrategy = $taxationStrategy;
}
public function processOrder(Cart $cart): void
{
/** @var CartItem $item */
foreach ($cart->getItems() as $item) {
$this->taxationStrategy->processTaxation($item);
}
}
}
final class OrderTaxationProcessor implements OrderProcessor
{
private TaxationStrategyDelegator $taxationStrategy;
public function __construct(TaxationStrategyDelegator $taxationStrategy)
{
$this->taxationStrategy = $taxationStrategy;
}
public function processOrder(Cart $cart): void
{
/** @var CartItem $item */
foreach ($cart->getItems() as $item) {
$this->taxationStrategy->processTaxation($item);
}
}
}
final class TaxationStrategyDelegator implements TaxationStrategy
{
public function processTaxation(CartItem $item): void
{
if ('shirt' === $item->getType()) {
$item->setTaxationPercentage(0.23);
}
if ('book' === $item->getType()) {
$item->setTaxationPercentage(0.08);
}
}
}
final class TaxationStrategyDelegator implements TaxationStrategy
{
public function processTaxation(CartItem $item): void
{
if ('shirt' === $item->getType()) {
$item->setTaxationPercentage(0.23);
}
if ('book' === $item->getType()) {
$item->setTaxationPercentage(0.08);
}
}
}
final class TaxationStrategyDelegator implements TaxationStrategy
{
private ShirtTaxationStrategy $shirtTaxationStrategy;
private BookTaxationStrategy $bookTaxationStrategy;
public function __construct(
ShirtTaxationStrategy $shirtTaxationStrategy,
BookTaxationStrategy $bookTaxationStrategy
) {
$this->shirtTaxationStrategy = $shirtTaxationStrategy;
$this->bookTaxationStrategy = $bookTaxationStrategy;
}
public function processTaxation(CartItem $item): void
{
if ('shirt' === $item->getType()) {
$this->shirtTaxationStrategy->processTaxation($item);
}
if ('book' === $item->getType()) {
$this->bookTaxationStrategy->processTaxation($item);
}
}
}
final class TaxationStrategyDelegator implements TaxationStrategy
{
private ShirtTaxationStrategy $shirtTaxationStrategy;
private BookTaxationStrategy $bookTaxationStrategy;
public function __construct(
ShirtTaxationStrategy $shirtTaxationStrategy,
BookTaxationStrategy $bookTaxationStrategy
) {
$this->shirtTaxationStrategy = $shirtTaxationStrategy;
$this->bookTaxationStrategy = $bookTaxationStrategy;
}
public function processTaxation(CartItem $item): void
{
if ('shirt' === $item->getType()) {
$this->shirtTaxationStrategy->processTaxation($item);
}
if ('book' === $item->getType()) {
$this->bookTaxationStrategy->processTaxation($item);
}
}
}
Symfony World - Symfony components and design patterns
final class TaxationStrategyDelegator implements TaxationStrategy
{
private ShirtTaxationStrategy $shirtTaxationStrategy;
private BookTaxationStrategy $bookTaxationStrategy;
public function __construct(
ShirtTaxationStrategy $shirtTaxationStrategy,
BookTaxationStrategy $bookTaxationStrategy
) {
$this->shirtTaxationStrategy = $shirtTaxationStrategy;
$this->bookTaxationStrategy = $bookTaxationStrategy;
}
public function processTaxation(CartItem $item): void
{
if ('shirt' === $item->getType()) {
$this->shirtTaxationStrategy->processTaxation($item);
}
if ('book' === $item->getType()) {
$this->bookTaxationStrategy->processTaxation($item);
}
}
}
final class TaxationStrategyDelegator implements TaxationStrategy
{
private ShirtTaxationStrategy $shirtTaxationStrategy;
private BookTaxationStrategy $bookTaxationStrategy;
public function __construct(
ShirtTaxationStrategy $shirtTaxationStrategy,
BookTaxationStrategy $bookTaxationStrategy
) {
$this->shirtTaxationStrategy = $shirtTaxationStrategy;
$this->bookTaxationStrategy = $bookTaxationStrategy;
}
public function processTaxation(CartItem $item): void
{
if ('shirt' === $item->getType()) {
$this->shirtTaxationStrategy->processTaxation($item);
}
if ('book' === $item->getType()) {
$this->bookTaxationStrategy->processTaxation($item);
}
}
}
final class TaxationStrategyDelegator implements TaxationStrategy
{
private array $indexedTaxationStrategies;
public function __construct(
ShirtTaxationStrategy $shirtTaxationStrategy,
BookTaxationStrategy $bookTaxationStrategy
) {
$this->indexedTaxationStrategies = [
'shirt' => $shirtTaxationStrategy,
'book' => $bookTaxationStrategy,
];
}
public function processTaxation(CartItem $item): void
{
$this->indexedTaxationStrategies[$item->getType()]->processTaxation($item);
}
}
final class TaxationStrategyDelegator implements TaxationStrategy
{
private array $indexedTaxationStrategies;
public function __construct(
ShirtTaxationStrategy $shirtTaxationStrategy,
BookTaxationStrategy $bookTaxationStrategy
) {
$this->indexedTaxationStrategies = [
'shirt' => $shirtTaxationStrategy,
'book' => $bookTaxationStrategy,
];
}
public function processTaxation(CartItem $item): void
{
$this->indexedTaxationStrategies[$item->getType()]->processTaxation($item);
}
}
Can Symfony help us with it?
final class TaxationStrategyDelegator implements TaxationStrategy
{
private array $indexedTaxationStrategies;
public function __construct(
ShirtTaxationStrategy $shirtTaxationStrategy,
BookTaxationStrategy $bookTaxationStrategy
) {
$this->indexedTaxationStrategies = [
'shirt' => $shirtTaxationStrategy,
'book' => $bookTaxationStrategy,
];
}
public function processTaxation(CartItem $item): void
{
$this->indexedTaxationStrategies[$item->getType()]->processTaxation($item);
}
}
final class TaxationStrategyDelegator implements TaxationStrategy
{
private array $indexedTaxationStrategies;
public function __construct(
ShirtTaxationStrategy $shirtTaxationStrategy,
BookTaxationStrategy $bookTaxationStrategy
) {
$this->indexedTaxationStrategies = [
'shirt' => $shirtTaxationStrategy,
'book' => $bookTaxationStrategy,
];
}
public function processTaxation(CartItem $item): void
{
$this->indexedTaxationStrategies[$item->getType()]->processTaxation($item);
}
}
final class TaxationStrategyDelegator implements TaxationStrategy
{
private ServiceLocator $taxationStrategies;
public function __construct(ServiceLocator $taxationStrategies)
{
$this->taxationStrategies = $taxationStrategies;
}
public function processTaxation(CartItem $item): void
{
$this->taxationStrategies->get($item->getType())->processTaxation($item);
}
}
final class TaxationStrategyDelegator implements TaxationStrategy
{
private ServiceLocator $taxationStrategies;
public function __construct(ServiceLocator $taxationStrategies)
{
$this->taxationStrategies = $taxationStrategies;
}
public function processTaxation(CartItem $item): void
{
$this->taxationStrategies->get($item->getType())->processTaxation($item);
}
}
SymfonyDesignPatternsTaxationStrategiesTaxationStrategy:
class: 'SymfonyDesignPatternsTaxationStrategiesTaxationStrategyDelegator'
arguments:
- !tagged_locator { tag: 'app.taxation_strategies', index_by: 'type' }
SymfonyDesignPatternsTaxationStrategiesTaxationStrategy:
class: 'SymfonyDesignPatternsTaxationStrategiesTaxationStrategyDelegator'
arguments:
- !tagged_locator { tag: 'app.taxation_strategies', index_by: 'type' }
SymfonyDesignPatternsTaxationStrategiesBookTaxationStrategy:
tags:
- { name: 'app.taxation_strategies', type: 'book' }
SymfonyDesignPatternsTaxationStrategiesShirtTaxationStrategy:
tags:
- { name: 'app.taxation_strategies', type: 'shirt' }
LLoC AoC ACC
Sample project 40 1 8
Composite iteration 73 4 3
Strategy iteration 102 7 1.86
Single responsibility principle


Open close principle


Liskov substitution principle


Interface segregation principle


Dependency inversion principle
Time for small refactoring
final class CheckoutController
{
private array $repository = [];
private OrderProcessor $orderProcessor;
public function __construct(OrderProcessor $orderProcessor)
{
$this->orderProcessor = $orderProcessor;
}
public function cartAction(string $operation, array $options): Cart
{
// Fetch object from repository
if (isset($this->repository[$options['cart_id']])) {
/** @var Cart $cart */
$cart = $this->repository[$options['cart_id']];
} else {
$cart = new Cart($options['cart_id']);
}
// Perform operation
if ('add_to_cart' === $operation) {
$cart->addProduct($options['product'], $options['type'], $options['quantity'], $options['price']);
} elseif ('remove_from_cart' === $operation) {
$cart->removeProduct($options['product']);
} else {
throw new InvalidArgumentException('operation not supported');
}
$this->orderProcessor->processOrder($cart);
// Save into repository
$this->repository[$options['cart_id']] = $cart;
return $cart;
}
}
final class CheckoutController
{
private array $repository = [];
private OrderProcessor $orderProcessor;
public function __construct(OrderProcessor $orderProcessor)
{
$this->orderProcessor = $orderProcessor;
}
public function cartAction(string $operation, array $options): Cart
{
// Fetch object from repository
if (isset($this->repository[$options['cart_id']])) {
/** @var Cart $cart */
$cart = $this->repository[$options['cart_id']];
} else {
$cart = new Cart($options['cart_id']);
}
// Perform operation
if ('add_to_cart' === $operation) {
$cart->addProduct($options['product'], $options['type'], $options['quantity'], $options['price']);
} elseif ('remove_from_cart' === $operation) {
$cart->removeProduct($options['product']);
} else {
throw new InvalidArgumentException('operation not supported');
}
$this->orderProcessor->processOrder($cart);
// Save into repository
$this->repository[$options['cart_id']] = $cart;
return $cart;
}
}
final class CheckoutController
{
private OrderProcessor $orderProcessor;
private CartRepository $cartRepository;
public function __construct(OrderProcessor $orderProcessor, CartRepository $cartRepository)
{
$this->orderProcessor = $orderProcessor;
$this->cartRepository = $cartRepository;
}
public function cartAction(string $operation, array $options): Cart
{
// Fetch object from repository
$cart = $this->cartRepository->getCart($options['cart_id']);
// Perform operation
if ('add_to_cart' === $operation) {
$cart->addProduct($options['product'], $options['type'], $options['quantity'], $options['price']);
} elseif ('remove_from_cart' === $operation) {
$cart->removeProduct($options['product']);
} else {
throw new InvalidArgumentException('operation not supported');
}
$this->orderProcessor->processOrder($cart);
// Save into repository
$this->cartRepository->save($cart);
return $cart;
}
}
final class CheckoutController
{
private OrderProcessor $orderProcessor;
private CartRepository $cartRepository;
public function __construct(OrderProcessor $orderProcessor, CartRepository $cartRepository)
{
$this->orderProcessor = $orderProcessor;
$this->cartRepository = $cartRepository;
}
public function cartAction(string $operation, array $options): Cart
{
// Fetch object from repository
$cart = $this->cartRepository->getCart($options['cart_id']);
// Perform operation
if ('add_to_cart' === $operation) {
$cart->addProduct($options['product'], $options['type'], $options['quantity'], $options['price']);
} elseif ('remove_from_cart' === $operation) {
$cart->removeProduct($options['product']);
} else {
throw new InvalidArgumentException('operation not supported');
}
$this->orderProcessor->processOrder($cart);
// Save into repository
$this->cartRepository->save($cart);
return $cart;
}
}
final class InMemoryCartRepository implements CartRepository
{
private array $repository = [];
public function getCart(string $cart_id): Cart
{
if (isset($this->repository[$cart_id])) {
/** @var Cart $cart */
$cart = $this->repository[$cart_id];
} else {
$cart = new Cart($cart_id);
}
return $cart;
}
}
public function getCart(string $cart_id): Cart
{
if (!isset($this->repository[$cart_id])) {
$this->repository[$cart_id] = new Cart($cart_id);
}
/** @var Cart $cart */
return $this->repository[$cart_id];
}
LLoC AoC ACC
Sample project 40 1 8
Composite iteration 73 4 3
Strategy iteration 102 7 1.86
Repository extraction 125 8 1.75
Command
final class CheckoutController
{
private array $repository = [];
private OrderProcessor $orderProcessor;
public function __construct(OrderProcessor $orderProcessor)
{
$this->orderProcessor = $orderProcessor;
}
public function cartAction(string $operation, array $options): Cart
{
// Fetch object from repository
$cart = $this->cartRepository->getCart($options[‘cart_id']);
// Perform operation
if ('add_to_cart' === $operation) {
$cart->addProduct($options['product'], $options['type'], $options['quantity'], $options['price']);
} elseif ('remove_from_cart' === $operation) {
$cart->removeProduct($options['product']);
} else {
throw new InvalidArgumentException('operation not supported');
}
$this->orderProcessor->processOrder($cart);
// Save into repository
$this->repository[$options['cart_id']] = $cart;
return $cart;
}
}
final class CheckoutController
{
private array $repository = [];
private OrderProcessor $orderProcessor;
public function __construct(OrderProcessor $orderProcessor)
{
$this->orderProcessor = $orderProcessor;
}
public function cartAction(string $operation, array $options): Cart
{
// Fetch object from repository
$cart = $this->cartRepository->getCart($options[‘cart_id']);
// Perform operation
if ('add_to_cart' === $operation) {
$cart->addProduct($options['product'], $options['type'], $options['quantity'], $options['price']);
} elseif ('remove_from_cart' === $operation) {
$cart->removeProduct($options['product']);
} else {
throw new InvalidArgumentException('operation not supported');
}
$this->orderProcessor->processOrder($cart);
// Save into repository
$this->repository[$options['cart_id']] = $cart;
return $cart;
}
}
public function cartAction(string $operation, array $options): Cart
{
// Fetch object from repository
$cart = $this->cartRepository->getCart($options['cart_id']);
// Perform operation
if ('add_to_cart' === $operation) {
$this->messageBus->dispatch(new AddToCart(
$options['cart_id'],
$options['product'],
$options['type'],
$options['quantity'],
$options['price']
));
} elseif ('remove_from_cart' === $operation) {
$this->messageBus->dispatch(new RemoveFromCart(
$options['cart_id'],
$options['product'],
));
} else {
throw new InvalidArgumentException('operation not supported');
}
$this->orderProcessor->processOrder($cart);
// Save into repository
$this->cartRepository->save($cart);
return $cart;
}
public function cartAction(string $operation, array $options): Cart
{
// Fetch object from repository
$cart = $this->cartRepository->getCart($options['cart_id']);
// Perform operation
if ('add_to_cart' === $operation) {
$this->messageBus->dispatch(new AddToCart(
$options['cart_id'],
$options['product'],
$options['type'],
$options['quantity'],
$options['price']
));
} elseif ('remove_from_cart' === $operation) {
$this->messageBus->dispatch(new RemoveFromCart(
$options['cart_id'],
$options['product'],
));
} else {
throw new InvalidArgumentException('operation not supported');
}
$this->orderProcessor->processOrder($cart);
// Save into repository
$this->cartRepository->save($cart);
return $cart;
}
final class AddToCart
{
private string $cartId;
private string $product;
private string $type;
private int $quantity;
private int $price;
public function __construct(...)
{
$this->cartId = $cartId;
$this->product = $product;
$this->type = $type;
$this->quantity = $quantity;
$this->price = $price;
}
}
final class AddToCartHandler implements MessageHandlerInterface
{
private CartRepository $cartRepository;
public function __construct(CartRepository $cartRepository)
{
$this->cartRepository = $cartRepository;
}
public function __invoke(AddToCart $addToCart)
{
$cart = $this->cartRepository->getCart($addToCart->getCartId());
$cart->addProduct(
$addToCart->getProduct(),
$addToCart->getType(),
$addToCart->getQuantity(),
$addToCart->getPrice()
);
$this->cartRepository->save($cart);
}
}
public function cartAction(string $operation, array $options): Cart
{
// Fetch object from repository
$cart = $this->cartRepository->getCart($options['cart_id']);
// Perform operation
if ('add_to_cart' === $operation) {
$this->messageBus->dispatch(new AddToCart(
$options['cart_id'],
$options['product'],
$options['type'],
$options['quantity'],
$options['price']
));
} elseif ('remove_from_cart' === $operation) {
$this->messageBus->dispatch(new RemoveFromCart(
$options['cart_id'],
$options['product'],
));
} else {
throw new InvalidArgumentException('operation not supported');
}
$this->orderProcessor->processOrder($cart);
// Save into repository
$this->cartRepository->save($cart);
return $cart;
}
public function cartAction(string $operation, array $options): Cart
{
// Fetch object from repository
$cart = $this->cartRepository->getCart($options['cart_id']);
// Perform operation
if ('add_to_cart' === $operation) {
$this->messageBus->dispatch(new AddToCart(
$options['cart_id'],
$options['product'],
$options['type'],
$options['quantity'],
$options['price']
));
} elseif ('remove_from_cart' === $operation) {
$this->messageBus->dispatch(new RemoveFromCart(
$options['cart_id'],
$options['product'],
));
} else {
throw new InvalidArgumentException('operation not supported');
}
$this->orderProcessor->processOrder($cart);
// Save into repository
$this->cartRepository->save($cart);
return $cart;
}
public function cartAction(CartIdAwareCommand $operation): Cart
{
// Fetch object from repository
$cart = $this->cartRepository->getCart($operation->getCartId());
// Perform operation
$this->messageBus->dispatch($operation);
$this->orderProcessor->processOrder($cart);
// Save into repository
$this->cartRepository->save($cart);
return $cart;
}
public function cartAction(CartIdAwareCommand $operation): Cart
{
// Fetch object from repository
$cart = $this->cartRepository->getCart($operation->getCartId());
// Perform operation
$this->messageBus->dispatch($operation);
$this->orderProcessor->processOrder($cart);
// Save into repository
$this->cartRepository->save($cart);
return $cart;
}
LLoC AoC ACC
Sample project 40 1 8
Composite iteration 73 4 3
Strategy iteration 102 7 1.86
Repository extraction 125 8 1.75
Command iteration 209 12 1.33
Decorator
final class AddToCartHandler implements MessageHandlerInterface
{
private CartRepository $cartRepository;
public function __construct(CartRepository $cartRepository)
{
$this->cartRepository = $cartRepository;
}
public function __invoke(AddToCart $addToCart)
{
$cart = $this->cartRepository->getCart($addToCart->getCartId());
$cart->addProduct(
$addToCart->getProduct(),
$addToCart->getType(),
$addToCart->getQuantity(),
$addToCart->getPrice()
);
$this->cartRepository->save($cart);
}
}
final class AddToCartHandler implements MessageHandlerInterface
{
private CartRepository $cartRepository;
public function __construct(CartRepository $cartRepository)
{
$this->cartRepository = $cartRepository;
}
public function __invoke(AddToCart $addToCart)
{
$cart = $this->cartRepository->getCart($addToCart->getCartId());
$cart->addProduct(
$addToCart->getProduct(),
$addToCart->getType(),
$addToCart->getQuantity(),
$addToCart->getPrice()
);
$this->cartRepository->save($cart);
}
}
final class AddToCartHandler implements MessageHandlerInterface
{
private CartRepository $cartRepository;
public function __construct(CartRepository $cartRepository)
{
$this->cartRepository = $cartRepository;
}
public function __invoke(AddToCart $addToCart): Cart
{
$cart = $this->cartRepository->getCart($addToCart->getCartId());
$cart->addProduct(
$addToCart->getProduct(),
$addToCart->getType(),
$addToCart->getQuantity(),
$addToCart->getPrice()
);
return $cart;
}
}
final class AddToCartHandler implements MessageHandlerInterface
{
private CartRepository $cartRepository;
public function __construct(CartRepository $cartRepository)
{
$this->cartRepository = $cartRepository;
}
public function __invoke(AddToCart $addToCart): Cart
{
$cart = $this->cartRepository->getCart($addToCart->getCartId());
$cart->addProduct(
$addToCart->getProduct(),
$addToCart->getType(),
$addToCart->getQuantity(),
$addToCart->getPrice()
);
return $cart;
}
}
final class TransactionDecorator
{
private MessageHandlerInterface $messageHandler;
private CartRepository $cartRepository;
public function __construct(
MessageHandlerInterface $messageHandler,
CartRepository $cartRepository
) {
$this->messageHandler = $messageHandler;
$this->cartRepository = $cartRepository;
}
public function __invoke(CartIdAwareCommand $cartIdAwareCommand)
{
// start transaction
$cart = ($this->messageHandler)($cartIdAwareCommand);
$this->cartRepository->save($cart);
}
}
Can Symfony help us with it?
SymfonyDesignPatternsDecoratorTransactionDecorator:
decorates: SymfonyDesignPatternsCommandHandlerAddToCartHandler
arguments:
- '@.inner'
LLoC AoC ACC
Sample project 40 1 8
Composite iteration 73 4 3
Strategy iteration 102 7 1.86
Repository extraction 125 8 1.75
Command iteration 209 12 1.33
Decorator iteration 216 13 1.31
Take offs?
Trade offs!
LLoC AoC ACC
Sample project 40 1 8
Composite iteration 73 4 3
Strategy iteration 102 7 1.86
Repository extraction 125 8 1.75
Command iteration 209 12 1.33
Decorator iteration 216 13 1.31
LLoC AoC ACC
Sample project 40 1 8
Composite iteration 73 4 3
Strategy iteration 102 7 1.86
Repository extraction 125 8 1.75
Command iteration 209 12 1.33
Decorator iteration 216 13 1.31
LLoC AoC ACC
Sample project 40 1 8
Composite iteration 73 4 3
Strategy iteration 102 7 1.86
Repository extraction 125 8 1.75
Command iteration 209 12 1.33
Decorator iteration 216 13 1.31
LLoC AoC ACC
Sample project 40 1 8
Composite iteration 73 4 3
Strategy iteration 102 7 1.86
Repository extraction 125 8 1.75
Command iteration 209 12 1.33
Decorator iteration 216 13 1.31
Increased complexity
Increased complexity


Increased testability
Increased complexity


Increased testability


Possibility for maintenance cost reduction
Symfony may help you a lot
@Sylius
Thank you!
@lukaszchrusciel

More Related Content

What's hot (20)

Ansible - Hands on Training
Ansible - Hands on TrainingAnsible - Hands on Training
Ansible - Hands on Training
Mehmet Ali Aydın
 
ルータコンフィグのGit管理のススメ 〜Git管理以外を自動化してみた〜
ルータコンフィグのGit管理のススメ 〜Git管理以外を自動化してみた〜ルータコンフィグのGit管理のススメ 〜Git管理以外を自動化してみた〜
ルータコンフィグのGit管理のススメ 〜Git管理以外を自動化してみた〜
Taiji Tsuchiya
 
Ansibleで始めるインフラ構築自動化
Ansibleで始めるインフラ構築自動化Ansibleで始めるインフラ構築自動化
Ansibleで始めるインフラ構築自動化
dcubeio
 
アイデンティティ管理の基礎~Fim adfsアーキテクチャ
アイデンティティ管理の基礎~Fim adfsアーキテクチャアイデンティティ管理の基礎~Fim adfsアーキテクチャ
アイデンティティ管理の基礎~Fim adfsアーキテクチャ
Naohiro Fujie
 
AWS における Microservices Architecture と DevOps を推進する組織と人とツール
AWS における Microservices Architecture と DevOps を推進する組織と人とツールAWS における Microservices Architecture と DevOps を推進する組織と人とツール
AWS における Microservices Architecture と DevOps を推進する組織と人とツール
Amazon Web Services Japan
 
Ansible presentation
Ansible presentationAnsible presentation
Ansible presentation
John Lynch
 
Amazon ECS AWS Fargate あるとき~ ないとき~ (トレノケ雲の会 mod2)
Amazon ECS AWS Fargate あるとき~ ないとき~ (トレノケ雲の会 mod2)Amazon ECS AWS Fargate あるとき~ ないとき~ (トレノケ雲の会 mod2)
Amazon ECS AWS Fargate あるとき~ ないとき~ (トレノケ雲の会 mod2)
Trainocate Japan, Ltd.
 
CMDBuildを中心とした運用管理自動化基盤OpenPIEの事例紹介
CMDBuildを中心とした運用管理自動化基盤OpenPIEの事例紹介CMDBuildを中心とした運用管理自動化基盤OpenPIEの事例紹介
CMDBuildを中心とした運用管理自動化基盤OpenPIEの事例紹介
OSSラボ株式会社
 
Azureの上におとりを置いて、世界中から攻撃される様子を観察した話
Azureの上におとりを置いて、世界中から攻撃される様子を観察した話Azureの上におとりを置いて、世界中から攻撃される様子を観察した話
Azureの上におとりを置いて、世界中から攻撃される様子を観察した話
Ryuki Yoshimatsu
 
Kubernetes Introduction
Kubernetes IntroductionKubernetes Introduction
Kubernetes Introduction
Red Hat Developers
 
도메인 주도 설계의 본질
도메인 주도 설계의 본질도메인 주도 설계의 본질
도메인 주도 설계의 본질
Young-Ho Cho
 
AWS Black Belt Online Seminar 2016 HPC分野でのAWS活用
AWS Black Belt Online Seminar 2016 HPC分野でのAWS活用AWS Black Belt Online Seminar 2016 HPC分野でのAWS活用
AWS Black Belt Online Seminar 2016 HPC分野でのAWS活用
Amazon Web Services Japan
 
Ansible Introduction
Ansible Introduction Ansible Introduction
Ansible Introduction
Robert Reiz
 
webエンジニアのためのはじめてのredis
webエンジニアのためのはじめてのrediswebエンジニアのためのはじめてのredis
webエンジニアのためのはじめてのredis
nasa9084
 
What is Docker
What is DockerWhat is Docker
What is Docker
Pavel Klimiankou
 
[SC03] Active Directory の DR 対策~天災/人災/サイバー攻撃、その時あなたの IT 基盤は利用継続できますか?
[SC03] Active Directory の DR 対策~天災/人災/サイバー攻撃、その時あなたの IT 基盤は利用継続できますか? [SC03] Active Directory の DR 対策~天災/人災/サイバー攻撃、その時あなたの IT 基盤は利用継続できますか?
[SC03] Active Directory の DR 対策~天災/人災/サイバー攻撃、その時あなたの IT 基盤は利用継続できますか?
de:code 2017
 
AWS Black Belt Techシリーズ Amazon SNS / Amazon SQS
AWS Black Belt Techシリーズ Amazon SNS / Amazon SQSAWS Black Belt Techシリーズ Amazon SNS / Amazon SQS
AWS Black Belt Techシリーズ Amazon SNS / Amazon SQS
Amazon Web Services Japan
 
Introduction to docker
Introduction to dockerIntroduction to docker
Introduction to docker
Frederik Mogensen
 
20190319 AWS Black Belt Online Seminar Amazon FSx for Windows Server
20190319 AWS Black Belt Online Seminar Amazon FSx for Windows Server20190319 AWS Black Belt Online Seminar Amazon FSx for Windows Server
20190319 AWS Black Belt Online Seminar Amazon FSx for Windows Server
Amazon Web Services Japan
 
20191002 AWS Black Belt Online Seminar Amazon EC2 Auto Scaling and AWS Auto S...
20191002 AWS Black Belt Online Seminar Amazon EC2 Auto Scaling and AWS Auto S...20191002 AWS Black Belt Online Seminar Amazon EC2 Auto Scaling and AWS Auto S...
20191002 AWS Black Belt Online Seminar Amazon EC2 Auto Scaling and AWS Auto S...
Amazon Web Services Japan
 
ルータコンフィグのGit管理のススメ 〜Git管理以外を自動化してみた〜
ルータコンフィグのGit管理のススメ 〜Git管理以外を自動化してみた〜ルータコンフィグのGit管理のススメ 〜Git管理以外を自動化してみた〜
ルータコンフィグのGit管理のススメ 〜Git管理以外を自動化してみた〜
Taiji Tsuchiya
 
Ansibleで始めるインフラ構築自動化
Ansibleで始めるインフラ構築自動化Ansibleで始めるインフラ構築自動化
Ansibleで始めるインフラ構築自動化
dcubeio
 
アイデンティティ管理の基礎~Fim adfsアーキテクチャ
アイデンティティ管理の基礎~Fim adfsアーキテクチャアイデンティティ管理の基礎~Fim adfsアーキテクチャ
アイデンティティ管理の基礎~Fim adfsアーキテクチャ
Naohiro Fujie
 
AWS における Microservices Architecture と DevOps を推進する組織と人とツール
AWS における Microservices Architecture と DevOps を推進する組織と人とツールAWS における Microservices Architecture と DevOps を推進する組織と人とツール
AWS における Microservices Architecture と DevOps を推進する組織と人とツール
Amazon Web Services Japan
 
Ansible presentation
Ansible presentationAnsible presentation
Ansible presentation
John Lynch
 
Amazon ECS AWS Fargate あるとき~ ないとき~ (トレノケ雲の会 mod2)
Amazon ECS AWS Fargate あるとき~ ないとき~ (トレノケ雲の会 mod2)Amazon ECS AWS Fargate あるとき~ ないとき~ (トレノケ雲の会 mod2)
Amazon ECS AWS Fargate あるとき~ ないとき~ (トレノケ雲の会 mod2)
Trainocate Japan, Ltd.
 
CMDBuildを中心とした運用管理自動化基盤OpenPIEの事例紹介
CMDBuildを中心とした運用管理自動化基盤OpenPIEの事例紹介CMDBuildを中心とした運用管理自動化基盤OpenPIEの事例紹介
CMDBuildを中心とした運用管理自動化基盤OpenPIEの事例紹介
OSSラボ株式会社
 
Azureの上におとりを置いて、世界中から攻撃される様子を観察した話
Azureの上におとりを置いて、世界中から攻撃される様子を観察した話Azureの上におとりを置いて、世界中から攻撃される様子を観察した話
Azureの上におとりを置いて、世界中から攻撃される様子を観察した話
Ryuki Yoshimatsu
 
도메인 주도 설계의 본질
도메인 주도 설계의 본질도메인 주도 설계의 본질
도메인 주도 설계의 본질
Young-Ho Cho
 
AWS Black Belt Online Seminar 2016 HPC分野でのAWS活用
AWS Black Belt Online Seminar 2016 HPC分野でのAWS活用AWS Black Belt Online Seminar 2016 HPC分野でのAWS活用
AWS Black Belt Online Seminar 2016 HPC分野でのAWS活用
Amazon Web Services Japan
 
Ansible Introduction
Ansible Introduction Ansible Introduction
Ansible Introduction
Robert Reiz
 
webエンジニアのためのはじめてのredis
webエンジニアのためのはじめてのrediswebエンジニアのためのはじめてのredis
webエンジニアのためのはじめてのredis
nasa9084
 
[SC03] Active Directory の DR 対策~天災/人災/サイバー攻撃、その時あなたの IT 基盤は利用継続できますか?
[SC03] Active Directory の DR 対策~天災/人災/サイバー攻撃、その時あなたの IT 基盤は利用継続できますか? [SC03] Active Directory の DR 対策~天災/人災/サイバー攻撃、その時あなたの IT 基盤は利用継続できますか?
[SC03] Active Directory の DR 対策~天災/人災/サイバー攻撃、その時あなたの IT 基盤は利用継続できますか?
de:code 2017
 
AWS Black Belt Techシリーズ Amazon SNS / Amazon SQS
AWS Black Belt Techシリーズ Amazon SNS / Amazon SQSAWS Black Belt Techシリーズ Amazon SNS / Amazon SQS
AWS Black Belt Techシリーズ Amazon SNS / Amazon SQS
Amazon Web Services Japan
 
20190319 AWS Black Belt Online Seminar Amazon FSx for Windows Server
20190319 AWS Black Belt Online Seminar Amazon FSx for Windows Server20190319 AWS Black Belt Online Seminar Amazon FSx for Windows Server
20190319 AWS Black Belt Online Seminar Amazon FSx for Windows Server
Amazon Web Services Japan
 
20191002 AWS Black Belt Online Seminar Amazon EC2 Auto Scaling and AWS Auto S...
20191002 AWS Black Belt Online Seminar Amazon EC2 Auto Scaling and AWS Auto S...20191002 AWS Black Belt Online Seminar Amazon EC2 Auto Scaling and AWS Auto S...
20191002 AWS Black Belt Online Seminar Amazon EC2 Auto Scaling and AWS Auto S...
Amazon Web Services Japan
 

Similar to Symfony World - Symfony components and design patterns (20)

Event sourcing w PHP (by Piotr Kacała)
Event sourcing w PHP (by Piotr Kacała)Event sourcing w PHP (by Piotr Kacała)
Event sourcing w PHP (by Piotr Kacała)
GOG.com dev team
 
購物車程式架構簡介
購物車程式架構簡介購物車程式架構簡介
購物車程式架構簡介
Jace Ju
 
Symfony2 Building on Alpha / Beta technology
Symfony2 Building on Alpha / Beta technologySymfony2 Building on Alpha / Beta technology
Symfony2 Building on Alpha / Beta technology
Daniel Knell
 
Chaining and function composition with lodash / underscore
Chaining and function composition with lodash / underscoreChaining and function composition with lodash / underscore
Chaining and function composition with lodash / underscore
Nicolas Carlo
 
12.9 Program Online shopping cart (continued) (C++)This program e.pdf
12.9 Program Online shopping cart (continued) (C++)This program e.pdf12.9 Program Online shopping cart (continued) (C++)This program e.pdf
12.9 Program Online shopping cart (continued) (C++)This program e.pdf
fasttracksunglass
 
PhpUnit - The most unknown Parts
PhpUnit - The most unknown PartsPhpUnit - The most unknown Parts
PhpUnit - The most unknown Parts
Bastian Feder
 
[PHPCon 2023] “Kto to pisał?!... a, to ja.”, czyli sposoby żeby znienawidzić ...
[PHPCon 2023] “Kto to pisał?!... a, to ja.”, czyli sposoby żeby znienawidzić ...[PHPCon 2023] “Kto to pisał?!... a, to ja.”, czyli sposoby żeby znienawidzić ...
[PHPCon 2023] “Kto to pisał?!... a, to ja.”, czyli sposoby żeby znienawidzić ...
Mateusz Zalewski
 
Meet Elcodi, the flexible e-commerce components built on Symfony2
Meet Elcodi, the flexible e-commerce components built on Symfony2Meet Elcodi, the flexible e-commerce components built on Symfony2
Meet Elcodi, the flexible e-commerce components built on Symfony2
Aldo Chiecchia
 
Unittests für Dummies
Unittests für DummiesUnittests für Dummies
Unittests für Dummies
Lars Jankowfsky
 
Getting the Most Out of jQuery Widgets
Getting the Most Out of jQuery WidgetsGetting the Most Out of jQuery Widgets
Getting the Most Out of jQuery Widgets
velveeta_512
 
laravel tricks in 50minutes
laravel tricks in 50minuteslaravel tricks in 50minutes
laravel tricks in 50minutes
Barang CK
 
50 Laravel Tricks in 50 Minutes
50 Laravel Tricks in 50 Minutes50 Laravel Tricks in 50 Minutes
50 Laravel Tricks in 50 Minutes
Azim Kurt
 
Framework Project
Framework  ProjectFramework  Project
Framework Project
Mauro_Sist
 
Adding Dependency Injection to Legacy Applications
Adding Dependency Injection to Legacy ApplicationsAdding Dependency Injection to Legacy Applications
Adding Dependency Injection to Legacy Applications
Sam Hennessy
 
Zero to SOLID
Zero to SOLIDZero to SOLID
Zero to SOLID
Vic Metcalfe
 
Be RESTful (Symfony Camp 2008)
Be RESTful (Symfony Camp 2008)Be RESTful (Symfony Camp 2008)
Be RESTful (Symfony Camp 2008)
Fabien Potencier
 
Clean Javascript
Clean JavascriptClean Javascript
Clean Javascript
Ryunosuke SATO
 
Doctrine For Beginners
Doctrine For BeginnersDoctrine For Beginners
Doctrine For Beginners
Jonathan Wage
 
Migrare da symfony 1 a Symfony2
 Migrare da symfony 1 a Symfony2  Migrare da symfony 1 a Symfony2
Migrare da symfony 1 a Symfony2
Massimiliano Arione
 
Chaining et composition de fonctions avec lodash / underscore
Chaining et composition de fonctions avec lodash / underscoreChaining et composition de fonctions avec lodash / underscore
Chaining et composition de fonctions avec lodash / underscore
Nicolas Carlo
 
Event sourcing w PHP (by Piotr Kacała)
Event sourcing w PHP (by Piotr Kacała)Event sourcing w PHP (by Piotr Kacała)
Event sourcing w PHP (by Piotr Kacała)
GOG.com dev team
 
購物車程式架構簡介
購物車程式架構簡介購物車程式架構簡介
購物車程式架構簡介
Jace Ju
 
Symfony2 Building on Alpha / Beta technology
Symfony2 Building on Alpha / Beta technologySymfony2 Building on Alpha / Beta technology
Symfony2 Building on Alpha / Beta technology
Daniel Knell
 
Chaining and function composition with lodash / underscore
Chaining and function composition with lodash / underscoreChaining and function composition with lodash / underscore
Chaining and function composition with lodash / underscore
Nicolas Carlo
 
12.9 Program Online shopping cart (continued) (C++)This program e.pdf
12.9 Program Online shopping cart (continued) (C++)This program e.pdf12.9 Program Online shopping cart (continued) (C++)This program e.pdf
12.9 Program Online shopping cart (continued) (C++)This program e.pdf
fasttracksunglass
 
PhpUnit - The most unknown Parts
PhpUnit - The most unknown PartsPhpUnit - The most unknown Parts
PhpUnit - The most unknown Parts
Bastian Feder
 
[PHPCon 2023] “Kto to pisał?!... a, to ja.”, czyli sposoby żeby znienawidzić ...
[PHPCon 2023] “Kto to pisał?!... a, to ja.”, czyli sposoby żeby znienawidzić ...[PHPCon 2023] “Kto to pisał?!... a, to ja.”, czyli sposoby żeby znienawidzić ...
[PHPCon 2023] “Kto to pisał?!... a, to ja.”, czyli sposoby żeby znienawidzić ...
Mateusz Zalewski
 
Meet Elcodi, the flexible e-commerce components built on Symfony2
Meet Elcodi, the flexible e-commerce components built on Symfony2Meet Elcodi, the flexible e-commerce components built on Symfony2
Meet Elcodi, the flexible e-commerce components built on Symfony2
Aldo Chiecchia
 
Getting the Most Out of jQuery Widgets
Getting the Most Out of jQuery WidgetsGetting the Most Out of jQuery Widgets
Getting the Most Out of jQuery Widgets
velveeta_512
 
laravel tricks in 50minutes
laravel tricks in 50minuteslaravel tricks in 50minutes
laravel tricks in 50minutes
Barang CK
 
50 Laravel Tricks in 50 Minutes
50 Laravel Tricks in 50 Minutes50 Laravel Tricks in 50 Minutes
50 Laravel Tricks in 50 Minutes
Azim Kurt
 
Framework Project
Framework  ProjectFramework  Project
Framework Project
Mauro_Sist
 
Adding Dependency Injection to Legacy Applications
Adding Dependency Injection to Legacy ApplicationsAdding Dependency Injection to Legacy Applications
Adding Dependency Injection to Legacy Applications
Sam Hennessy
 
Be RESTful (Symfony Camp 2008)
Be RESTful (Symfony Camp 2008)Be RESTful (Symfony Camp 2008)
Be RESTful (Symfony Camp 2008)
Fabien Potencier
 
Doctrine For Beginners
Doctrine For BeginnersDoctrine For Beginners
Doctrine For Beginners
Jonathan Wage
 
Chaining et composition de fonctions avec lodash / underscore
Chaining et composition de fonctions avec lodash / underscoreChaining et composition de fonctions avec lodash / underscore
Chaining et composition de fonctions avec lodash / underscore
Nicolas Carlo
 
Ad

More from Łukasz Chruściel (20)

2024 PHPCon - Symfony background processing
2024 PHPCon - Symfony background processing2024 PHPCon - Symfony background processing
2024 PHPCon - Symfony background processing
Łukasz Chruściel
 
Wprowadzenie do fundamentów Sylius 2.0
Wprowadzenie do fundamentów  Sylius  2.0Wprowadzenie do fundamentów  Sylius  2.0
Wprowadzenie do fundamentów Sylius 2.0
Łukasz Chruściel
 
APIP 2024 - All the Challenges of Sylius Migration to API Platform 3.pdf [REU...
APIP 2024 - All the Challenges of Sylius Migration to API Platform 3.pdf [REU...APIP 2024 - All the Challenges of Sylius Migration to API Platform 3.pdf [REU...
APIP 2024 - All the Challenges of Sylius Migration to API Platform 3.pdf [REU...
Łukasz Chruściel
 
APIP 2024 - All the Challenges of Sylius Migration to API Platform 3.pdf
APIP 2024 - All the Challenges of Sylius Migration to API Platform 3.pdfAPIP 2024 - All the Challenges of Sylius Migration to API Platform 3.pdf
APIP 2024 - All the Challenges of Sylius Migration to API Platform 3.pdf
Łukasz Chruściel
 
Need for Speed: Removing speed bumps from your Symfony projects ⚡️
Need for Speed: Removing speed bumps from your Symfony projects ⚡️Need for Speed: Removing speed bumps from your Symfony projects ⚡️
Need for Speed: Removing speed bumps from your Symfony projects ⚡️
Łukasz Chruściel
 
2024 eCommerceDays Toulouse - Sylius 2.0.pdf
2024 eCommerceDays Toulouse - Sylius 2.0.pdf2024 eCommerceDays Toulouse - Sylius 2.0.pdf
2024 eCommerceDays Toulouse - Sylius 2.0.pdf
Łukasz Chruściel
 
Unveiling the Future: Sylius 2.0 New Features
Unveiling the Future: Sylius 2.0 New FeaturesUnveiling the Future: Sylius 2.0 New Features
Unveiling the Future: Sylius 2.0 New Features
Łukasz Chruściel
 
ConFoo 2024 - Need for Speed: Removing speed bumps in API Projects
ConFoo 2024  - Need for Speed: Removing speed bumps in API ProjectsConFoo 2024  - Need for Speed: Removing speed bumps in API Projects
ConFoo 2024 - Need for Speed: Removing speed bumps in API Projects
Łukasz Chruściel
 
ConFoo 2024 - Sylius 2.0, top-notch eCommerce for customizable solution
ConFoo 2024 - Sylius 2.0, top-notch eCommerce for customizable solutionConFoo 2024 - Sylius 2.0, top-notch eCommerce for customizable solution
ConFoo 2024 - Sylius 2.0, top-notch eCommerce for customizable solution
Łukasz Chruściel
 
SyliusCon - Typical pitfalls of Sylius development.pdf
SyliusCon - Typical pitfalls of Sylius development.pdfSyliusCon - Typical pitfalls of Sylius development.pdf
SyliusCon - Typical pitfalls of Sylius development.pdf
Łukasz Chruściel
 
Need for Speed: Removing speed bumps in API Projects
Need for Speed: Removing speed bumps in API ProjectsNeed for Speed: Removing speed bumps in API Projects
Need for Speed: Removing speed bumps in API Projects
Łukasz Chruściel
 
SymfonyLive Online 2023 - Is SOLID dead? .pdf
SymfonyLive Online 2023 - Is SOLID dead? .pdfSymfonyLive Online 2023 - Is SOLID dead? .pdf
SymfonyLive Online 2023 - Is SOLID dead? .pdf
Łukasz Chruściel
 
Worldwide Software Architecture Summit'23 - BDD and why most of us do it wron...
Worldwide Software Architecture Summit'23 - BDD and why most of us do it wron...Worldwide Software Architecture Summit'23 - BDD and why most of us do it wron...
Worldwide Software Architecture Summit'23 - BDD and why most of us do it wron...
Łukasz Chruściel
 
4Developers - Rozterki i decyzje.pdf
4Developers - Rozterki i decyzje.pdf4Developers - Rozterki i decyzje.pdf
4Developers - Rozterki i decyzje.pdf
Łukasz Chruściel
 
4Developers - Sylius CRUD generation revisited.pdf
4Developers - Sylius CRUD generation revisited.pdf4Developers - Sylius CRUD generation revisited.pdf
4Developers - Sylius CRUD generation revisited.pdf
Łukasz Chruściel
 
BoilingFrogs - Rozterki i decyzje. Czego się nauczyliśmy projektując API Syliusa
BoilingFrogs - Rozterki i decyzje. Czego się nauczyliśmy projektując API SyliusaBoilingFrogs - Rozterki i decyzje. Czego się nauczyliśmy projektując API Syliusa
BoilingFrogs - Rozterki i decyzje. Czego się nauczyliśmy projektując API Syliusa
Łukasz Chruściel
 
What we've learned designing new Sylius API
What we've learned designing new Sylius APIWhat we've learned designing new Sylius API
What we've learned designing new Sylius API
Łukasz Chruściel
 
How to optimize background processes.pdf
How to optimize background processes.pdfHow to optimize background processes.pdf
How to optimize background processes.pdf
Łukasz Chruściel
 
SymfonyCon - Dilemmas and decisions..pdf
SymfonyCon - Dilemmas and decisions..pdfSymfonyCon - Dilemmas and decisions..pdf
SymfonyCon - Dilemmas and decisions..pdf
Łukasz Chruściel
 
How to optimize background processes - when Sylius meets Blackfire
How to optimize background processes - when Sylius meets BlackfireHow to optimize background processes - when Sylius meets Blackfire
How to optimize background processes - when Sylius meets Blackfire
Łukasz Chruściel
 
2024 PHPCon - Symfony background processing
2024 PHPCon - Symfony background processing2024 PHPCon - Symfony background processing
2024 PHPCon - Symfony background processing
Łukasz Chruściel
 
Wprowadzenie do fundamentów Sylius 2.0
Wprowadzenie do fundamentów  Sylius  2.0Wprowadzenie do fundamentów  Sylius  2.0
Wprowadzenie do fundamentów Sylius 2.0
Łukasz Chruściel
 
APIP 2024 - All the Challenges of Sylius Migration to API Platform 3.pdf [REU...
APIP 2024 - All the Challenges of Sylius Migration to API Platform 3.pdf [REU...APIP 2024 - All the Challenges of Sylius Migration to API Platform 3.pdf [REU...
APIP 2024 - All the Challenges of Sylius Migration to API Platform 3.pdf [REU...
Łukasz Chruściel
 
APIP 2024 - All the Challenges of Sylius Migration to API Platform 3.pdf
APIP 2024 - All the Challenges of Sylius Migration to API Platform 3.pdfAPIP 2024 - All the Challenges of Sylius Migration to API Platform 3.pdf
APIP 2024 - All the Challenges of Sylius Migration to API Platform 3.pdf
Łukasz Chruściel
 
Need for Speed: Removing speed bumps from your Symfony projects ⚡️
Need for Speed: Removing speed bumps from your Symfony projects ⚡️Need for Speed: Removing speed bumps from your Symfony projects ⚡️
Need for Speed: Removing speed bumps from your Symfony projects ⚡️
Łukasz Chruściel
 
2024 eCommerceDays Toulouse - Sylius 2.0.pdf
2024 eCommerceDays Toulouse - Sylius 2.0.pdf2024 eCommerceDays Toulouse - Sylius 2.0.pdf
2024 eCommerceDays Toulouse - Sylius 2.0.pdf
Łukasz Chruściel
 
Unveiling the Future: Sylius 2.0 New Features
Unveiling the Future: Sylius 2.0 New FeaturesUnveiling the Future: Sylius 2.0 New Features
Unveiling the Future: Sylius 2.0 New Features
Łukasz Chruściel
 
ConFoo 2024 - Need for Speed: Removing speed bumps in API Projects
ConFoo 2024  - Need for Speed: Removing speed bumps in API ProjectsConFoo 2024  - Need for Speed: Removing speed bumps in API Projects
ConFoo 2024 - Need for Speed: Removing speed bumps in API Projects
Łukasz Chruściel
 
ConFoo 2024 - Sylius 2.0, top-notch eCommerce for customizable solution
ConFoo 2024 - Sylius 2.0, top-notch eCommerce for customizable solutionConFoo 2024 - Sylius 2.0, top-notch eCommerce for customizable solution
ConFoo 2024 - Sylius 2.0, top-notch eCommerce for customizable solution
Łukasz Chruściel
 
SyliusCon - Typical pitfalls of Sylius development.pdf
SyliusCon - Typical pitfalls of Sylius development.pdfSyliusCon - Typical pitfalls of Sylius development.pdf
SyliusCon - Typical pitfalls of Sylius development.pdf
Łukasz Chruściel
 
Need for Speed: Removing speed bumps in API Projects
Need for Speed: Removing speed bumps in API ProjectsNeed for Speed: Removing speed bumps in API Projects
Need for Speed: Removing speed bumps in API Projects
Łukasz Chruściel
 
SymfonyLive Online 2023 - Is SOLID dead? .pdf
SymfonyLive Online 2023 - Is SOLID dead? .pdfSymfonyLive Online 2023 - Is SOLID dead? .pdf
SymfonyLive Online 2023 - Is SOLID dead? .pdf
Łukasz Chruściel
 
Worldwide Software Architecture Summit'23 - BDD and why most of us do it wron...
Worldwide Software Architecture Summit'23 - BDD and why most of us do it wron...Worldwide Software Architecture Summit'23 - BDD and why most of us do it wron...
Worldwide Software Architecture Summit'23 - BDD and why most of us do it wron...
Łukasz Chruściel
 
4Developers - Rozterki i decyzje.pdf
4Developers - Rozterki i decyzje.pdf4Developers - Rozterki i decyzje.pdf
4Developers - Rozterki i decyzje.pdf
Łukasz Chruściel
 
4Developers - Sylius CRUD generation revisited.pdf
4Developers - Sylius CRUD generation revisited.pdf4Developers - Sylius CRUD generation revisited.pdf
4Developers - Sylius CRUD generation revisited.pdf
Łukasz Chruściel
 
BoilingFrogs - Rozterki i decyzje. Czego się nauczyliśmy projektując API Syliusa
BoilingFrogs - Rozterki i decyzje. Czego się nauczyliśmy projektując API SyliusaBoilingFrogs - Rozterki i decyzje. Czego się nauczyliśmy projektując API Syliusa
BoilingFrogs - Rozterki i decyzje. Czego się nauczyliśmy projektując API Syliusa
Łukasz Chruściel
 
What we've learned designing new Sylius API
What we've learned designing new Sylius APIWhat we've learned designing new Sylius API
What we've learned designing new Sylius API
Łukasz Chruściel
 
How to optimize background processes.pdf
How to optimize background processes.pdfHow to optimize background processes.pdf
How to optimize background processes.pdf
Łukasz Chruściel
 
SymfonyCon - Dilemmas and decisions..pdf
SymfonyCon - Dilemmas and decisions..pdfSymfonyCon - Dilemmas and decisions..pdf
SymfonyCon - Dilemmas and decisions..pdf
Łukasz Chruściel
 
How to optimize background processes - when Sylius meets Blackfire
How to optimize background processes - when Sylius meets BlackfireHow to optimize background processes - when Sylius meets Blackfire
How to optimize background processes - when Sylius meets Blackfire
Łukasz Chruściel
 
Ad

Recently uploaded (20)

GirikHire Unlocking the Future of Tech Talent with AI-Powered Hiring Solution...
GirikHire Unlocking the Future of Tech Talent with AI-Powered Hiring Solution...GirikHire Unlocking the Future of Tech Talent with AI-Powered Hiring Solution...
GirikHire Unlocking the Future of Tech Talent with AI-Powered Hiring Solution...
GirikHire
 
Autoposting.ai Sales Deck - Skyrocket your LinkedIn's ROI
Autoposting.ai Sales Deck - Skyrocket your LinkedIn's ROIAutoposting.ai Sales Deck - Skyrocket your LinkedIn's ROI
Autoposting.ai Sales Deck - Skyrocket your LinkedIn's ROI
Udit Goenka
 
Techdebt handling with cleancode focus and as risk taker
Techdebt handling with cleancode focus and as risk takerTechdebt handling with cleancode focus and as risk taker
Techdebt handling with cleancode focus and as risk taker
RajaNagendraKumar
 
Rebuilding Cadabra Studio: AI as Our Core Foundation
Rebuilding Cadabra Studio: AI as Our Core FoundationRebuilding Cadabra Studio: AI as Our Core Foundation
Rebuilding Cadabra Studio: AI as Our Core Foundation
Cadabra Studio
 
ICDL FULL STANDARD 2025 Luisetto mauro - Academia domani- 55 HOURS LONG pdf
ICDL FULL STANDARD  2025 Luisetto mauro - Academia domani- 55 HOURS LONG pdfICDL FULL STANDARD  2025 Luisetto mauro - Academia domani- 55 HOURS LONG pdf
ICDL FULL STANDARD 2025 Luisetto mauro - Academia domani- 55 HOURS LONG pdf
M. Luisetto Pharm.D.Spec. Pharmacology
 
Scalefusion Remote Access for Apple Devices
Scalefusion Remote Access for Apple DevicesScalefusion Remote Access for Apple Devices
Scalefusion Remote Access for Apple Devices
Scalefusion
 
iOS Developer Resume 2025 | Pramod Kumar
iOS Developer Resume 2025 | Pramod KumariOS Developer Resume 2025 | Pramod Kumar
iOS Developer Resume 2025 | Pramod Kumar
Pramod Kumar
 
grade 9 ai project cycle Artificial intelligence.pptx
grade 9 ai project cycle Artificial intelligence.pptxgrade 9 ai project cycle Artificial intelligence.pptx
grade 9 ai project cycle Artificial intelligence.pptx
manikumar465287
 
Content Mate Web App Triples Content Managers‘ Productivity
Content Mate Web App Triples Content Managers‘ ProductivityContent Mate Web App Triples Content Managers‘ Productivity
Content Mate Web App Triples Content Managers‘ Productivity
Alex Vladimirovich
 
How AI Can Improve Media Quality Testing Across Platforms (1).pptx
How AI Can Improve Media Quality Testing Across Platforms (1).pptxHow AI Can Improve Media Quality Testing Across Platforms (1).pptx
How AI Can Improve Media Quality Testing Across Platforms (1).pptx
kalichargn70th171
 
BoxLang-Dynamic-AWS-Lambda by Luis Majano.pdf
BoxLang-Dynamic-AWS-Lambda by Luis Majano.pdfBoxLang-Dynamic-AWS-Lambda by Luis Majano.pdf
BoxLang-Dynamic-AWS-Lambda by Luis Majano.pdf
Ortus Solutions, Corp
 
Agentic AI Desgin Principles in five slides.pptx
Agentic AI Desgin Principles in five slides.pptxAgentic AI Desgin Principles in five slides.pptx
Agentic AI Desgin Principles in five slides.pptx
MOSIUOA WESI
 
Boost Student Engagement with Smart Attendance Software for Schools
Boost Student Engagement with Smart Attendance Software for SchoolsBoost Student Engagement with Smart Attendance Software for Schools
Boost Student Engagement with Smart Attendance Software for Schools
Visitu
 
Optimising Claims Management with Claims Processing Systems
Optimising Claims Management with Claims Processing SystemsOptimising Claims Management with Claims Processing Systems
Optimising Claims Management with Claims Processing Systems
Insurance Tech Services
 
How a Staff Augmentation Company IN USA Powers Flutter App Breakthroughs.pdf
How a Staff Augmentation Company IN USA Powers Flutter App Breakthroughs.pdfHow a Staff Augmentation Company IN USA Powers Flutter App Breakthroughs.pdf
How a Staff Augmentation Company IN USA Powers Flutter App Breakthroughs.pdf
mary rojas
 
Delivering More with Less: AI Driven Resource Management with OnePlan
Delivering More with Less: AI Driven Resource Management with OnePlan Delivering More with Less: AI Driven Resource Management with OnePlan
Delivering More with Less: AI Driven Resource Management with OnePlan
OnePlan Solutions
 
The rise of e-commerce has redefined how retailers operate—and reconciliation...
The rise of e-commerce has redefined how retailers operate—and reconciliation...The rise of e-commerce has redefined how retailers operate—and reconciliation...
The rise of e-commerce has redefined how retailers operate—and reconciliation...
Prachi Desai
 
SQL-COMMANDS instructionsssssssssss.pptx
SQL-COMMANDS instructionsssssssssss.pptxSQL-COMMANDS instructionsssssssssss.pptx
SQL-COMMANDS instructionsssssssssss.pptx
Ashlei5
 
List Unfolding - 'unfold' as the Computational Dual of 'fold', and how 'unfol...
List Unfolding - 'unfold' as the Computational Dual of 'fold', and how 'unfol...List Unfolding - 'unfold' as the Computational Dual of 'fold', and how 'unfol...
List Unfolding - 'unfold' as the Computational Dual of 'fold', and how 'unfol...
Philip Schwarz
 
How to Generate Financial Statements in QuickBooks Like a Pro (1).pdf
How to Generate Financial Statements in QuickBooks Like a Pro (1).pdfHow to Generate Financial Statements in QuickBooks Like a Pro (1).pdf
How to Generate Financial Statements in QuickBooks Like a Pro (1).pdf
QuickBooks Training
 
GirikHire Unlocking the Future of Tech Talent with AI-Powered Hiring Solution...
GirikHire Unlocking the Future of Tech Talent with AI-Powered Hiring Solution...GirikHire Unlocking the Future of Tech Talent with AI-Powered Hiring Solution...
GirikHire Unlocking the Future of Tech Talent with AI-Powered Hiring Solution...
GirikHire
 
Autoposting.ai Sales Deck - Skyrocket your LinkedIn's ROI
Autoposting.ai Sales Deck - Skyrocket your LinkedIn's ROIAutoposting.ai Sales Deck - Skyrocket your LinkedIn's ROI
Autoposting.ai Sales Deck - Skyrocket your LinkedIn's ROI
Udit Goenka
 
Techdebt handling with cleancode focus and as risk taker
Techdebt handling with cleancode focus and as risk takerTechdebt handling with cleancode focus and as risk taker
Techdebt handling with cleancode focus and as risk taker
RajaNagendraKumar
 
Rebuilding Cadabra Studio: AI as Our Core Foundation
Rebuilding Cadabra Studio: AI as Our Core FoundationRebuilding Cadabra Studio: AI as Our Core Foundation
Rebuilding Cadabra Studio: AI as Our Core Foundation
Cadabra Studio
 
ICDL FULL STANDARD 2025 Luisetto mauro - Academia domani- 55 HOURS LONG pdf
ICDL FULL STANDARD  2025 Luisetto mauro - Academia domani- 55 HOURS LONG pdfICDL FULL STANDARD  2025 Luisetto mauro - Academia domani- 55 HOURS LONG pdf
ICDL FULL STANDARD 2025 Luisetto mauro - Academia domani- 55 HOURS LONG pdf
M. Luisetto Pharm.D.Spec. Pharmacology
 
Scalefusion Remote Access for Apple Devices
Scalefusion Remote Access for Apple DevicesScalefusion Remote Access for Apple Devices
Scalefusion Remote Access for Apple Devices
Scalefusion
 
iOS Developer Resume 2025 | Pramod Kumar
iOS Developer Resume 2025 | Pramod KumariOS Developer Resume 2025 | Pramod Kumar
iOS Developer Resume 2025 | Pramod Kumar
Pramod Kumar
 
grade 9 ai project cycle Artificial intelligence.pptx
grade 9 ai project cycle Artificial intelligence.pptxgrade 9 ai project cycle Artificial intelligence.pptx
grade 9 ai project cycle Artificial intelligence.pptx
manikumar465287
 
Content Mate Web App Triples Content Managers‘ Productivity
Content Mate Web App Triples Content Managers‘ ProductivityContent Mate Web App Triples Content Managers‘ Productivity
Content Mate Web App Triples Content Managers‘ Productivity
Alex Vladimirovich
 
How AI Can Improve Media Quality Testing Across Platforms (1).pptx
How AI Can Improve Media Quality Testing Across Platforms (1).pptxHow AI Can Improve Media Quality Testing Across Platforms (1).pptx
How AI Can Improve Media Quality Testing Across Platforms (1).pptx
kalichargn70th171
 
BoxLang-Dynamic-AWS-Lambda by Luis Majano.pdf
BoxLang-Dynamic-AWS-Lambda by Luis Majano.pdfBoxLang-Dynamic-AWS-Lambda by Luis Majano.pdf
BoxLang-Dynamic-AWS-Lambda by Luis Majano.pdf
Ortus Solutions, Corp
 
Agentic AI Desgin Principles in five slides.pptx
Agentic AI Desgin Principles in five slides.pptxAgentic AI Desgin Principles in five slides.pptx
Agentic AI Desgin Principles in five slides.pptx
MOSIUOA WESI
 
Boost Student Engagement with Smart Attendance Software for Schools
Boost Student Engagement with Smart Attendance Software for SchoolsBoost Student Engagement with Smart Attendance Software for Schools
Boost Student Engagement with Smart Attendance Software for Schools
Visitu
 
Optimising Claims Management with Claims Processing Systems
Optimising Claims Management with Claims Processing SystemsOptimising Claims Management with Claims Processing Systems
Optimising Claims Management with Claims Processing Systems
Insurance Tech Services
 
How a Staff Augmentation Company IN USA Powers Flutter App Breakthroughs.pdf
How a Staff Augmentation Company IN USA Powers Flutter App Breakthroughs.pdfHow a Staff Augmentation Company IN USA Powers Flutter App Breakthroughs.pdf
How a Staff Augmentation Company IN USA Powers Flutter App Breakthroughs.pdf
mary rojas
 
Delivering More with Less: AI Driven Resource Management with OnePlan
Delivering More with Less: AI Driven Resource Management with OnePlan Delivering More with Less: AI Driven Resource Management with OnePlan
Delivering More with Less: AI Driven Resource Management with OnePlan
OnePlan Solutions
 
The rise of e-commerce has redefined how retailers operate—and reconciliation...
The rise of e-commerce has redefined how retailers operate—and reconciliation...The rise of e-commerce has redefined how retailers operate—and reconciliation...
The rise of e-commerce has redefined how retailers operate—and reconciliation...
Prachi Desai
 
SQL-COMMANDS instructionsssssssssss.pptx
SQL-COMMANDS instructionsssssssssss.pptxSQL-COMMANDS instructionsssssssssss.pptx
SQL-COMMANDS instructionsssssssssss.pptx
Ashlei5
 
List Unfolding - 'unfold' as the Computational Dual of 'fold', and how 'unfol...
List Unfolding - 'unfold' as the Computational Dual of 'fold', and how 'unfol...List Unfolding - 'unfold' as the Computational Dual of 'fold', and how 'unfol...
List Unfolding - 'unfold' as the Computational Dual of 'fold', and how 'unfol...
Philip Schwarz
 
How to Generate Financial Statements in QuickBooks Like a Pro (1).pdf
How to Generate Financial Statements in QuickBooks Like a Pro (1).pdfHow to Generate Financial Statements in QuickBooks Like a Pro (1).pdf
How to Generate Financial Statements in QuickBooks Like a Pro (1).pdf
QuickBooks Training
 

Symfony World - Symfony components and design patterns

  • 9. Communication Reduced amount of code in one chunk Increased cohesion and reduced coupling
  • 10. Communication Reduced amount of code in one chunk Increased cohesion and reduced coupling Extendibility
  • 11. Communication Reduced amount of code in one chunk Increased cohesion and reduced coupling Extendibility SOLID
  • 14. Avg. Logic Lines of Code
  • 15. Avg. Logic Lines of Code LLoC
  • 16. Avg. Logic Lines of Code LLoC Amount of classes
  • 17. Avg. Logic Lines of Code LLoC Amount of classes AoC
  • 18. Avg. Logic Lines of Code LLoC Amount of classes AoC Avg. Cyclomatic Complexity
  • 19. Avg. Logic Lines of Code LLoC Amount of classes AoC Avg. Cyclomatic Complexity ACC
  • 22. final class CheckoutController { private array $repository = []; public function cartAction(string $operation, array $options): Cart { // Fetch object from repository if (isset($this->repository[$options['cart_id']])) { /** @var Cart $cart */ $cart = $this->repository[$options['cart_id']]; } else { $cart = new Cart($options['cart_id']); } // Perform operation if ('add_to_cart' === $operation) { $cart->addProduct($options['product'], $options['type'], $options['quantity'], $options['price']); } elseif ('remove_from_cart' === $operation) { $cart->removeProduct($options['product']); } else { throw new InvalidArgumentException('operation not supported'); } // Apply promotion if ($cart->getPrice() >= 1000) { $cart->applyPercentageDiscount(0.1); } // Apply taxation /** @var CartItem $item */ foreach ($cart->getItems() as $item) { if ('shirt' === $item->getType()) { $item->setTaxationPercentage(0.23); } if ('book' === $item->getType()) { $item->setTaxationPercentage(0.08); } } // Save into repository $this->repository[$options['cart_id']] = $cart; return $cart; } }
  • 23. final class CheckoutController { private array $repository = []; public function cartAction(string $operation, array $options): Cart { // Fetch object from repository if (isset($this->repository[$options['cart_id']])) { /** @var Cart $cart */ $cart = $this->repository[$options['cart_id']]; } else { $cart = new Cart($options['cart_id']); } // Perform operation if ('add_to_cart' === $operation) { $cart->addProduct($options['product'], $options['type'], $options['quantity'], $options['price']); } elseif ('remove_from_cart' === $operation) { $cart->removeProduct($options['product']); } else { throw new InvalidArgumentException('operation not supported'); } // Apply promotion if ($cart->getPrice() >= 1000) { $cart->applyPercentageDiscount(0.1); } // Apply taxation /** @var CartItem $item */ foreach ($cart->getItems() as $item) { if ('shirt' === $item->getType()) { $item->setTaxationPercentage(0.23); } if ('book' === $item->getType()) { $item->setTaxationPercentage(0.08); } } // Save into repository $this->repository[$options['cart_id']] = $cart; return $cart; }
  • 24. { // Fetch object from repository if (isset($this->repository[$options['cart_id']])) { /** @var Cart $cart */ $cart = $this->repository[$options['cart_id']]; } else { $cart = new Cart($options['cart_id']); } // Perform operation if ('add_to_cart' === $operation) { $cart->addProduct( $options[‘product'], $options[‘type'], $options[‘quantity'], $options[‘price'] ); } elseif ('remove_from_cart' === $operation) { $cart->removeProduct($options['product']); } else { throw new InvalidArgumentException('operation not supported'); } // Apply promotion if ($cart->getPrice() >= 1000) { $cart->applyPercentageDiscount(0.1); } // Apply taxation /** @var CartItem $item */ foreach ($cart->getItems() as $item) { if ('shirt' === $item->getType()) { $item->setTaxationPercentage(0.23); } if ('book' === $item->getType()) { $item->setTaxationPercentage(0.08); } } // Save into repository
  • 25. final class CheckoutController { private array $repository = []; public function cartAction(string $operation, array $options): Cart { // Fetch object from repository if (isset($this->repository[$options['cart_id']])) { /** @var Cart $cart */ $cart = $this->repository[$options['cart_id']]; } else { $cart = new Cart($options['cart_id']); } // Perform operation if ('add_to_cart' === $operation) { $cart->addProduct($options['product'], $options['type'], $options['quantity'], $options['price']); } elseif ('remove_from_cart' === $operation) { $cart->removeProduct($options['product']); } else { throw new InvalidArgumentException('operation not supported'); } // Apply promotion if ($cart->getPrice() >= 1000) { $cart->applyPercentageDiscount(0.1); } // Apply taxation /** @var CartItem $item */ foreach ($cart->getItems() as $item) { if ('shirt' === $item->getType()) { $item->setTaxationPercentage(0.23); } if ('book' === $item->getType()) { $item->setTaxationPercentage(0.08); } } // Save into repository $this->repository[$options['cart_id']] = $cart; return $cart; } }
  • 26. private array $repository = []; public function cartAction(string $operation, array $options): Cart { // Fetch object from repository if (isset($this->repository[$options['cart_id']])) { /** @var Cart $cart */ $cart = $this->repository[$options['cart_id']]; } else { $cart = new Cart($options['cart_id']); } // Perform operation if ('add_to_cart' === $operation) { $cart->addProduct($options['product'], $options['type'], $options['quantity'], $options['price']); } elseif ('remove_from_cart' === $operation) { $cart->removeProduct($options['product']); } else { throw new InvalidArgumentException('operation not supported'); } // Apply promotion if ($cart->getPrice() >= 1000) { $cart->applyPercentageDiscount(0.1); } // Apply taxation /** @var CartItem $item */ foreach ($cart->getItems() as $item) { if ('shirt' === $item->getType()) { $item->setTaxationPercentage(0.23); } if ('book' === $item->getType()) { $item->setTaxationPercentage(0.08); } } // Save into repository $this->repository[$options['cart_id']] = $cart;
  • 27. final class CheckoutController { private array $repository = []; public function cartAction(string $operation, array $options): Cart { // Fetch object from repository if (isset($this->repository[$options['cart_id']])) { /** @var Cart $cart */ $cart = $this->repository[$options['cart_id']]; } else { $cart = new Cart($options['cart_id']); } // Perform operation if ('add_to_cart' === $operation) { $cart->addProduct($options['product'], $options['type'], $options['quantity'], $options['price']); } elseif ('remove_from_cart' === $operation) { $cart->removeProduct($options['product']); } else { throw new InvalidArgumentException('operation not supported'); } // Apply promotion if ($cart->getPrice() >= 1000) { $cart->applyPercentageDiscount(0.1); } // Apply taxation /** @var CartItem $item */ foreach ($cart->getItems() as $item) { if ('shirt' === $item->getType()) { $item->setTaxationPercentage(0.23); } if ('book' === $item->getType()) { $item->setTaxationPercentage(0.08); } } // Save into repository $this->repository[$options['cart_id']] = $cart; return $cart; } }
  • 28. final class CheckoutController { private array $repository = []; public function cartAction(string $operation, array $options): Cart { // Fetch object from repository if (isset($this->repository[$options['cart_id']])) { /** @var Cart $cart */ $cart = $this->repository[$options['cart_id']]; } else { $cart = new Cart($options['cart_id']); } // Perform operation if ('add_to_cart' === $operation) { $cart->addProduct($options['product'], $options['type'], $options['quantity'], $options['price']); } elseif ('remove_from_cart' === $operation) { $cart->removeProduct($options['product']); } else { throw new InvalidArgumentException('operation not supported'); } // Apply promotion if ($cart->getPrice() >= 1000) { $cart->applyPercentageDiscount(0.1); } // Apply taxation /** @var CartItem $item */ foreach ($cart->getItems() as $item) { if ('shirt' === $item->getType()) { $item->setTaxationPercentage(0.23); } if ('book' === $item->getType()) { $item->setTaxationPercentage(0.08); } } // Save into repository $this->repository[$options['cart_id']] = $cart; return $cart; } }
  • 29. Avg. Logic Lines of Code 40 Amount of classes 1 Avg. Cyclomatic Complexity 8
  • 30. LLoC AoC ACC Sample project 40 1 8
  • 31. Single responsibility principle Open close principle Liskov substitution principle Interface segregation principle Dependency inversion principle
  • 32. Single responsibility principle Open close principle Liskov substitution principle Interface segregation principle Dependency inversion principle
  • 35. final class CheckoutController { private array $repository = []; public function cartAction(string $operation, array $options): Cart { // Fetch object from repository if (isset($this->repository[$options['cart_id']])) { /** @var Cart $cart */ $cart = $this->repository[$options['cart_id']]; } else { $cart = new Cart($options['cart_id']); } // Perform operation if ('add_to_cart' === $operation) { $cart->addProduct($options['product'], $options['type'], $options['quantity'], $options['price']); } elseif ('remove_from_cart' === $operation) { $cart->removeProduct($options['product']); } else { throw new InvalidArgumentException('operation not supported'); } // Apply promotion if ($cart->getPrice() >= 1000) { $cart->applyPercentageDiscount(0.1); } // Apply taxation /** @var CartItem $item */ foreach ($cart->getItems() as $item) { if ('shirt' === $item->getType()) { $item->setTaxationPercentage(0.23); } if ('book' === $item->getType()) { $item->setTaxationPercentage(0.08); } } // Save into repository $this->repository[$options['cart_id']] = $cart; return $cart; } }
  • 36. $cart = new Cart($options['cart_id']); } // Perform operation if ('add_to_cart' === $operation) { $cart->addProduct($options['product'], $options['type'], $options['quantity'], $options['price']); } elseif ('remove_from_cart' === $operation) { $cart->removeProduct($options['product']); } else { throw new InvalidArgumentException('operation not supported'); } // Apply promotion if ($cart->getPrice() >= 1000) { $cart->applyPercentageDiscount(0.1); } // Apply taxation /** @var CartItem $item */ foreach ($cart->getItems() as $item) { if ('shirt' === $item->getType()) { $item->setTaxationPercentage(0.23); } if ('book' === $item->getType()) { $item->setTaxationPercentage(0.08); } } // Save into repository $this->repository[$options['cart_id']] = $cart; return $cart; } }
  • 37. private array $repository = []; public function cartAction(string $operation, array $options): Cart { // Fetch object from repository if (isset($this->repository[$options['cart_id']])) { /** @var Cart $cart */ $cart = $this->repository[$options['cart_id']]; } else { $cart = new Cart($options['cart_id']); } // Perform operation if ('add_to_cart' === $operation) { $cart->addProduct($options['product'], $options['type'], $options['quantity'], $options['price']); } elseif ('remove_from_cart' === $operation) { $cart->removeProduct($options['product']); } else { throw new InvalidArgumentException('operation not supported'); } // Apply promotion $this->applyPromotion($cart); // Apply taxation $this->applyTaxation($cart); // Save into repository $this->repository[$options['cart_id']] = $cart; return $cart; } }
  • 38. private function applyTaxation(Cart $cart): void { /** @var CartItem $item */ foreach ($cart->getItems() as $item) { if ('shirt' === $item->getType()) { $item->setTaxationPercentage(0.23); } if ('book' === $item->getType()) { $item->setTaxationPercentage(0.08); } } } private function applyPromotion(Cart $cart): void { if ($cart->getPrice() >= 1000) { $cart->applyPercentageDiscount(0.1); } }
  • 39. private array $repository = []; public function cartAction(string $operation, array $options): Cart { // Fetch object from repository if (isset($this->repository[$options['cart_id']])) { /** @var Cart $cart */ $cart = $this->repository[$options['cart_id']]; } else { $cart = new Cart($options['cart_id']); } // Perform operation if ('add_to_cart' === $operation) { $cart->addProduct($options['product'], $options['type'], $options['quantity'], $options['price']); } elseif ('remove_from_cart' === $operation) { $cart->removeProduct($options['product']); } else { throw new InvalidArgumentException('operation not supported'); } // Apply promotion $this->applyPromotion($cart); // Apply taxation $this->applyTaxation($cart); // Save into repository $this->repository[$options['cart_id']] = $cart; return $cart; } }
  • 40. final class CheckoutController { private array $repository = []; public function cartAction(string $operation, array $options): Cart { // Fetch object from repository if (isset($this->repository[$options['cart_id']])) { /** @var Cart $cart */ $cart = $this->repository[$options['cart_id']]; } else { $cart = new Cart($options['cart_id']); } // Perform operation if ('add_to_cart' === $operation) { $cart->addProduct($options['product'], $options['type'], $options['quantity'], $options['price']); } elseif ('remove_from_cart' === $operation) { $cart->removeProduct($options['product']); } else { throw new InvalidArgumentException('operation not supported'); } $this->processOrder($cart); // Save into repository $this->repository[$options['cart_id']] = $cart; return $cart; } }
  • 41. private function processOrder(Cart $cart): void { // Apply promotion $this->applyPromotion($cart); // Apply taxation $this->applyTaxation($cart); }
  • 42. final class CheckoutController { private array $repository = []; public function cartAction(string $operation, array $options): Cart { // Fetch object from repository if (isset($this->repository[$options['cart_id']])) { /** @var Cart $cart */ $cart = $this->repository[$options['cart_id']]; } else { $cart = new Cart($options['cart_id']); } // Perform operation if ('add_to_cart' === $operation) { $cart->addProduct($options['product'], $options['type'], $options['quantity'], $options['price']); } elseif ('remove_from_cart' === $operation) { $cart->removeProduct($options['product']); } else { throw new InvalidArgumentException('operation not supported'); } $this->processOrder($cart); // Save into repository $this->repository[$options['cart_id']] = $cart; return $cart; } private function applyTaxation(Cart $cart): void { /** @var CartItem $item */ foreach ($cart->getItems() as $item) { if ('shirt' === $item->getType()) { $item->setTaxationPercentage(0.23); } if ('book' === $item->getType()) { $item->setTaxationPercentage(0.08); } } } private function applyPromotion(Cart $cart): void { if ($cart->getPrice() >= 1000) { $cart->applyPercentageDiscount(0.1); } } /** * @param Cart $cart */ private function processOrder(Cart $cart): void { // Apply promotion $this->applyPromotion($cart); // Apply taxation $this->applyTaxation($cart); } }
  • 43. final class CheckoutController { private array $repository = []; public function cartAction(string $operation, array $options): Cart { // Fetch object from repository if (isset($this->repository[$options['cart_id']])) { /** @var Cart $cart */ $cart = $this->repository[$options['cart_id']]; } else { $cart = new Cart($options['cart_id']); } // Perform operation if ('add_to_cart' === $operation) { $cart->addProduct($options['product'], $options['type'], $options['quantity'], $options['price']); } elseif ('remove_from_cart' === $operation) { $cart->removeProduct($options['product']); } else { throw new InvalidArgumentException('operation not supported'); } $this->processOrder($cart); // Save into repository $this->repository[$options['cart_id']] = $cart; return $cart; } }
  • 44. final class CheckoutController { private array $repository = []; public function cartAction(string $operation, array $options): Cart { // Fetch object from repository if (isset($this->repository[$options['cart_id']])) { /** @var Cart $cart */ $cart = $this->repository[$options['cart_id']]; } else { $cart = new Cart($options['cart_id']); } // Perform operation if ('add_to_cart' === $operation) { $cart->addProduct($options['product'], $options['type'], $options['quantity'], $options['price']); } elseif ('remove_from_cart' === $operation) { $cart->removeProduct($options['product']); } else { throw new InvalidArgumentException('operation not supported'); } $this->processOrder($cart); // Save into repository $this->repository[$options['cart_id']] = $cart; return $cart; } }
  • 46. final class CheckoutController { private array $repository = []; public function cartAction(string $operation, array $options): Cart { // Fetch object from repository if (isset($this->repository[$options['cart_id']])) { /** @var Cart $cart */ $cart = $this->repository[$options['cart_id']]; } else { $cart = new Cart($options['cart_id']); } // Perform operation if ('add_to_cart' === $operation) { $cart->addProduct($options['product'], $options['type'], $options['quantity'], $options['price']); } elseif ('remove_from_cart' === $operation) { $cart->removeProduct($options['product']); } else { throw new InvalidArgumentException('operation not supported'); } $this->orderProcessor->processOrder($cart); // Save into repository $this->repository[$options['cart_id']] = $cart; return $cart; } }
  • 47. final class CompositeOrderProcessor implements OrderProcessor { public function processOrder(Cart $cart): void { // Apply promotion $this->applyPromotion($cart); // Apply taxation $this->applyTaxation($cart); } private function applyTaxation(Cart $cart): void { /** @var CartItem $item */ foreach ($cart->getItems() as $item) { if ('shirt' === $item->getType()) { $item->setTaxationPercentage(0.23); } if ('book' === $item->getType()) { $item->setTaxationPercentage(0.08); } } } private function applyPromotion(Cart $cart): void { if ($cart->getPrice() >= 1000) { $cart->applyPercentageDiscount(0.1); } } }
  • 48. LLoC AoC ACC Sample project 40 1 8 Extracted processor 61 2 4.5
  • 50. final class CompositeOrderProcessor implements OrderProcessor { public function processOrder(Cart $cart): void { // Apply promotion $this->applyPromotion($cart); // Apply taxation $this->applyTaxation($cart); } private function applyTaxation(Cart $cart): void { /** @var CartItem $item */ foreach ($cart->getItems() as $item) { if ('shirt' === $item->getType()) { $item->setTaxationPercentage(0.23); } if ('book' === $item->getType()) { $item->setTaxationPercentage(0.08); } } } private function applyPromotion(Cart $cart): void { if ($cart->getPrice() >= 1000) { $cart->applyPercentageDiscount(0.1); } } }
  • 51. final class CompositeOrderProcessor implements OrderProcessor { public function processOrder(Cart $cart): void { // Apply promotion $this->applyPromotion($cart); // Apply taxation $this->applyTaxation($cart); } private function applyTaxation(Cart $cart): void { /** @var CartItem $item */ foreach ($cart->getItems() as $item) { if ('shirt' === $item->getType()) { $item->setTaxationPercentage(0.23); } if ('book' === $item->getType()) { $item->setTaxationPercentage(0.08); } } } private function applyPromotion(Cart $cart): void { if ($cart->getPrice() >= 1000) { $cart->applyPercentageDiscount(0.1); } } }
  • 52. final class OrderTaxationProcessor implements OrderProcessor { public function processOrder(Cart $cart): void { /** @var CartItem $item */ foreach ($cart->getItems() as $item) { if ('shirt' === $item->getType()) { $item->setTaxationPercentage(0.23); } if ('book' === $item->getType()) { $item->setTaxationPercentage(0.08); } } } }
  • 53. final class CompositeOrderProcessor implements OrderProcessor { public function processOrder(Cart $cart): void { // Apply promotion $this->applyPromotion($cart); // Apply taxation $this->applyTaxation($cart); } private function applyPromotion(Cart $cart): void { if ($cart->getPrice() >= 1000) { $cart->applyPercentageDiscount(0.1); } } }
  • 54. final class OrderPromotionProcessor implements OrderProcessor { public function processOrder(Cart $cart): void { if ($cart->getPrice() >= 1000) { $cart->applyPercentageDiscount(0.1); } } }
  • 55. final class CompositeOrderProcessor implements OrderProcessor { private OrderPromotionProcessor $orderPromotionProcessor; private OrderTaxationProcessor $orderTaxationProcessor; public function __construct( OrderPromotionProcessor $orderPromotionProcessor, OrderTaxationProcessor $orderTaxationProcessor ) { $this->orderPromotionProcessor = $orderPromotionProcessor; $this->orderTaxationProcessor = $orderTaxationProcessor; } public function processOrder(Cart $cart): void { // Apply promotion $this->orderPromotionProcessor->processOrder($cart); // Apply taxation $this->orderTaxationProcessor->processOrder($cart); } }
  • 56. LLoC AoC ACC Sample project 40 1 8 Extracted processor 61 2 4.5 Separated processors 76 4 2.75
  • 58. final class CompositeOrderProcessor implements OrderProcessor { private OrderPromotionProcessor $orderPromotionProcessor; private OrderTaxationProcessor $orderTaxationProcessor; public function __construct( OrderPromotionProcessor $orderPromotionProcessor, OrderTaxationProcessor $orderTaxationProcessor ) { $this->orderPromotionProcessor = $orderPromotionProcessor; $this->orderTaxationProcessor = $orderTaxationProcessor; } public function processOrder(Cart $cart): void { // Apply promotion $this->orderPromotionProcessor->processOrder($cart); // Apply taxation $this->orderTaxationProcessor->processOrder($cart); } }
  • 59. final class CompositeOrderProcessor implements OrderProcessor { private iterable $orderProcessors; public function __construct( OrderPromotionProcessor $orderPromotionProcessor, OrderTaxationProcessor $orderTaxationProcessor ) { $this->orderProcessors = [ $orderPromotionProcessor, $orderTaxationProcessor ]; } public function processOrder(Cart $cart): void { foreach ($this->orderProcessors as $orderProcessor) { $orderProcessor->processOrder($cart); } } }
  • 60. final class CompositeOrderProcessor implements OrderProcessor { private iterable $orderProcessors; public function __construct( OrderPromotionProcessor $orderPromotionProcessor, OrderTaxationProcessor $orderTaxationProcessor ) { $this->orderProcessors = [ $orderPromotionProcessor, $orderTaxationProcessor ]; } public function processOrder(Cart $cart): void { foreach ($this->orderProcessors as $orderProcessor) { $orderProcessor->processOrder($cart); } } }
  • 61. final class CompositeOrderProcessor implements OrderProcessor { private iterable $orderProcessors; public function __construct( OrderPromotionProcessor $orderPromotionProcessor, OrderTaxationProcessor $orderTaxationProcessor ) { $this->orderProcessors = [ $orderPromotionProcessor, $orderTaxationProcessor ]; } public function processOrder(Cart $cart): void { foreach ($this->orderProcessors as $orderProcessor) { $orderProcessor->processOrder($cart); } } }
  • 62. final class CompositeOrderProcessor implements OrderProcessor { private iterable $orderProcessors; public function __construct( OrderPromotionProcessor $orderPromotionProcessor, OrderTaxationProcessor $orderTaxationProcessor ) { $this->orderProcessors = [ $orderPromotionProcessor, $orderTaxationProcessor ]; } public function processOrder(Cart $cart): void { foreach ($this->orderProcessors as $orderProcessor) { $orderProcessor->processOrder($cart); } } }
  • 63. final class CompositeOrderProcessor implements OrderProcessor { private iterable $orderProcessors; public function __construct(iterable $orderProcessors) { $this->orderProcessors = $orderProcessors; } public function processOrder(Cart $cart): void { foreach ($this->orderProcessors as $orderProcessor) { $orderProcessor->processOrder($cart); } } }
  • 64. final class CompositeOrderProcessor implements OrderProcessor { private iterable $orderProcessors; public function __construct(iterable $orderProcessors) { $this->orderProcessors = $orderProcessors; } public function processOrder(Cart $cart): void { foreach ($this->orderProcessors as $orderProcessor) { $orderProcessor->processOrder($cart); } } }
  • 66. Can Symfony help us with it?
  • 70. LLoC AoC ACC Sample project 40 1 8 Extracted processor 61 2 4.5 Separated processors 76 4 2.75 Composite iteration 73 4 3
  • 71. Single responsibility principle Open close principle Liskov substitution principle Interface segregation principle Dependency inversion principle
  • 72. LLoC AoC ACC Sample project 40 1 8 Composite iteration 73 4 3
  • 74. final class OrderTaxationProcessor implements OrderProcessor { public function processOrder(Cart $cart): void { /** @var CartItem $item */ foreach ($cart->getItems() as $item) { if ('shirt' === $item->getType()) { $item->setTaxationPercentage(0.23); } if ('book' === $item->getType()) { $item->setTaxationPercentage(0.08); } } } }
  • 75. final class OrderTaxationProcessor implements OrderProcessor { public function processOrder(Cart $cart): void { /** @var CartItem $item */ foreach ($cart->getItems() as $item) { if ('shirt' === $item->getType()) { $item->setTaxationPercentage(0.23); } if ('book' === $item->getType()) { $item->setTaxationPercentage(0.08); } } } }
  • 76. final class OrderTaxationProcessor implements OrderProcessor { private TaxationStrategyDelegator $taxationStrategy; public function __construct(TaxationStrategyDelegator $taxationStrategy) { $this->taxationStrategy = $taxationStrategy; } public function processOrder(Cart $cart): void { /** @var CartItem $item */ foreach ($cart->getItems() as $item) { $this->taxationStrategy->processTaxation($item); } } }
  • 77. final class OrderTaxationProcessor implements OrderProcessor { private TaxationStrategyDelegator $taxationStrategy; public function __construct(TaxationStrategyDelegator $taxationStrategy) { $this->taxationStrategy = $taxationStrategy; } public function processOrder(Cart $cart): void { /** @var CartItem $item */ foreach ($cart->getItems() as $item) { $this->taxationStrategy->processTaxation($item); } } }
  • 78. final class TaxationStrategyDelegator implements TaxationStrategy { public function processTaxation(CartItem $item): void { if ('shirt' === $item->getType()) { $item->setTaxationPercentage(0.23); } if ('book' === $item->getType()) { $item->setTaxationPercentage(0.08); } } }
  • 79. final class TaxationStrategyDelegator implements TaxationStrategy { public function processTaxation(CartItem $item): void { if ('shirt' === $item->getType()) { $item->setTaxationPercentage(0.23); } if ('book' === $item->getType()) { $item->setTaxationPercentage(0.08); } } }
  • 80. final class TaxationStrategyDelegator implements TaxationStrategy { private ShirtTaxationStrategy $shirtTaxationStrategy; private BookTaxationStrategy $bookTaxationStrategy; public function __construct( ShirtTaxationStrategy $shirtTaxationStrategy, BookTaxationStrategy $bookTaxationStrategy ) { $this->shirtTaxationStrategy = $shirtTaxationStrategy; $this->bookTaxationStrategy = $bookTaxationStrategy; } public function processTaxation(CartItem $item): void { if ('shirt' === $item->getType()) { $this->shirtTaxationStrategy->processTaxation($item); } if ('book' === $item->getType()) { $this->bookTaxationStrategy->processTaxation($item); } } }
  • 81. final class TaxationStrategyDelegator implements TaxationStrategy { private ShirtTaxationStrategy $shirtTaxationStrategy; private BookTaxationStrategy $bookTaxationStrategy; public function __construct( ShirtTaxationStrategy $shirtTaxationStrategy, BookTaxationStrategy $bookTaxationStrategy ) { $this->shirtTaxationStrategy = $shirtTaxationStrategy; $this->bookTaxationStrategy = $bookTaxationStrategy; } public function processTaxation(CartItem $item): void { if ('shirt' === $item->getType()) { $this->shirtTaxationStrategy->processTaxation($item); } if ('book' === $item->getType()) { $this->bookTaxationStrategy->processTaxation($item); } } }
  • 83. final class TaxationStrategyDelegator implements TaxationStrategy { private ShirtTaxationStrategy $shirtTaxationStrategy; private BookTaxationStrategy $bookTaxationStrategy; public function __construct( ShirtTaxationStrategy $shirtTaxationStrategy, BookTaxationStrategy $bookTaxationStrategy ) { $this->shirtTaxationStrategy = $shirtTaxationStrategy; $this->bookTaxationStrategy = $bookTaxationStrategy; } public function processTaxation(CartItem $item): void { if ('shirt' === $item->getType()) { $this->shirtTaxationStrategy->processTaxation($item); } if ('book' === $item->getType()) { $this->bookTaxationStrategy->processTaxation($item); } } }
  • 84. final class TaxationStrategyDelegator implements TaxationStrategy { private ShirtTaxationStrategy $shirtTaxationStrategy; private BookTaxationStrategy $bookTaxationStrategy; public function __construct( ShirtTaxationStrategy $shirtTaxationStrategy, BookTaxationStrategy $bookTaxationStrategy ) { $this->shirtTaxationStrategy = $shirtTaxationStrategy; $this->bookTaxationStrategy = $bookTaxationStrategy; } public function processTaxation(CartItem $item): void { if ('shirt' === $item->getType()) { $this->shirtTaxationStrategy->processTaxation($item); } if ('book' === $item->getType()) { $this->bookTaxationStrategy->processTaxation($item); } } }
  • 85. final class TaxationStrategyDelegator implements TaxationStrategy { private array $indexedTaxationStrategies; public function __construct( ShirtTaxationStrategy $shirtTaxationStrategy, BookTaxationStrategy $bookTaxationStrategy ) { $this->indexedTaxationStrategies = [ 'shirt' => $shirtTaxationStrategy, 'book' => $bookTaxationStrategy, ]; } public function processTaxation(CartItem $item): void { $this->indexedTaxationStrategies[$item->getType()]->processTaxation($item); } }
  • 86. final class TaxationStrategyDelegator implements TaxationStrategy { private array $indexedTaxationStrategies; public function __construct( ShirtTaxationStrategy $shirtTaxationStrategy, BookTaxationStrategy $bookTaxationStrategy ) { $this->indexedTaxationStrategies = [ 'shirt' => $shirtTaxationStrategy, 'book' => $bookTaxationStrategy, ]; } public function processTaxation(CartItem $item): void { $this->indexedTaxationStrategies[$item->getType()]->processTaxation($item); } }
  • 87. Can Symfony help us with it?
  • 88. final class TaxationStrategyDelegator implements TaxationStrategy { private array $indexedTaxationStrategies; public function __construct( ShirtTaxationStrategy $shirtTaxationStrategy, BookTaxationStrategy $bookTaxationStrategy ) { $this->indexedTaxationStrategies = [ 'shirt' => $shirtTaxationStrategy, 'book' => $bookTaxationStrategy, ]; } public function processTaxation(CartItem $item): void { $this->indexedTaxationStrategies[$item->getType()]->processTaxation($item); } }
  • 89. final class TaxationStrategyDelegator implements TaxationStrategy { private array $indexedTaxationStrategies; public function __construct( ShirtTaxationStrategy $shirtTaxationStrategy, BookTaxationStrategy $bookTaxationStrategy ) { $this->indexedTaxationStrategies = [ 'shirt' => $shirtTaxationStrategy, 'book' => $bookTaxationStrategy, ]; } public function processTaxation(CartItem $item): void { $this->indexedTaxationStrategies[$item->getType()]->processTaxation($item); } }
  • 90. final class TaxationStrategyDelegator implements TaxationStrategy { private ServiceLocator $taxationStrategies; public function __construct(ServiceLocator $taxationStrategies) { $this->taxationStrategies = $taxationStrategies; } public function processTaxation(CartItem $item): void { $this->taxationStrategies->get($item->getType())->processTaxation($item); } }
  • 91. final class TaxationStrategyDelegator implements TaxationStrategy { private ServiceLocator $taxationStrategies; public function __construct(ServiceLocator $taxationStrategies) { $this->taxationStrategies = $taxationStrategies; } public function processTaxation(CartItem $item): void { $this->taxationStrategies->get($item->getType())->processTaxation($item); } }
  • 94. SymfonyDesignPatternsTaxationStrategiesBookTaxationStrategy: tags: - { name: 'app.taxation_strategies', type: 'book' } SymfonyDesignPatternsTaxationStrategiesShirtTaxationStrategy: tags: - { name: 'app.taxation_strategies', type: 'shirt' }
  • 95. LLoC AoC ACC Sample project 40 1 8 Composite iteration 73 4 3 Strategy iteration 102 7 1.86
  • 96. Single responsibility principle Open close principle Liskov substitution principle Interface segregation principle Dependency inversion principle
  • 97. Time for small refactoring
  • 98. final class CheckoutController { private array $repository = []; private OrderProcessor $orderProcessor; public function __construct(OrderProcessor $orderProcessor) { $this->orderProcessor = $orderProcessor; } public function cartAction(string $operation, array $options): Cart { // Fetch object from repository if (isset($this->repository[$options['cart_id']])) { /** @var Cart $cart */ $cart = $this->repository[$options['cart_id']]; } else { $cart = new Cart($options['cart_id']); } // Perform operation if ('add_to_cart' === $operation) { $cart->addProduct($options['product'], $options['type'], $options['quantity'], $options['price']); } elseif ('remove_from_cart' === $operation) { $cart->removeProduct($options['product']); } else { throw new InvalidArgumentException('operation not supported'); } $this->orderProcessor->processOrder($cart); // Save into repository $this->repository[$options['cart_id']] = $cart; return $cart; } }
  • 99. final class CheckoutController { private array $repository = []; private OrderProcessor $orderProcessor; public function __construct(OrderProcessor $orderProcessor) { $this->orderProcessor = $orderProcessor; } public function cartAction(string $operation, array $options): Cart { // Fetch object from repository if (isset($this->repository[$options['cart_id']])) { /** @var Cart $cart */ $cart = $this->repository[$options['cart_id']]; } else { $cart = new Cart($options['cart_id']); } // Perform operation if ('add_to_cart' === $operation) { $cart->addProduct($options['product'], $options['type'], $options['quantity'], $options['price']); } elseif ('remove_from_cart' === $operation) { $cart->removeProduct($options['product']); } else { throw new InvalidArgumentException('operation not supported'); } $this->orderProcessor->processOrder($cart); // Save into repository $this->repository[$options['cart_id']] = $cart; return $cart; } }
  • 100. final class CheckoutController { private OrderProcessor $orderProcessor; private CartRepository $cartRepository; public function __construct(OrderProcessor $orderProcessor, CartRepository $cartRepository) { $this->orderProcessor = $orderProcessor; $this->cartRepository = $cartRepository; } public function cartAction(string $operation, array $options): Cart { // Fetch object from repository $cart = $this->cartRepository->getCart($options['cart_id']); // Perform operation if ('add_to_cart' === $operation) { $cart->addProduct($options['product'], $options['type'], $options['quantity'], $options['price']); } elseif ('remove_from_cart' === $operation) { $cart->removeProduct($options['product']); } else { throw new InvalidArgumentException('operation not supported'); } $this->orderProcessor->processOrder($cart); // Save into repository $this->cartRepository->save($cart); return $cart; } }
  • 101. final class CheckoutController { private OrderProcessor $orderProcessor; private CartRepository $cartRepository; public function __construct(OrderProcessor $orderProcessor, CartRepository $cartRepository) { $this->orderProcessor = $orderProcessor; $this->cartRepository = $cartRepository; } public function cartAction(string $operation, array $options): Cart { // Fetch object from repository $cart = $this->cartRepository->getCart($options['cart_id']); // Perform operation if ('add_to_cart' === $operation) { $cart->addProduct($options['product'], $options['type'], $options['quantity'], $options['price']); } elseif ('remove_from_cart' === $operation) { $cart->removeProduct($options['product']); } else { throw new InvalidArgumentException('operation not supported'); } $this->orderProcessor->processOrder($cart); // Save into repository $this->cartRepository->save($cart); return $cart; } }
  • 102. final class InMemoryCartRepository implements CartRepository { private array $repository = []; public function getCart(string $cart_id): Cart { if (isset($this->repository[$cart_id])) { /** @var Cart $cart */ $cart = $this->repository[$cart_id]; } else { $cart = new Cart($cart_id); } return $cart; } }
  • 103. public function getCart(string $cart_id): Cart { if (!isset($this->repository[$cart_id])) { $this->repository[$cart_id] = new Cart($cart_id); } /** @var Cart $cart */ return $this->repository[$cart_id]; }
  • 104. LLoC AoC ACC Sample project 40 1 8 Composite iteration 73 4 3 Strategy iteration 102 7 1.86 Repository extraction 125 8 1.75
  • 106. final class CheckoutController { private array $repository = []; private OrderProcessor $orderProcessor; public function __construct(OrderProcessor $orderProcessor) { $this->orderProcessor = $orderProcessor; } public function cartAction(string $operation, array $options): Cart { // Fetch object from repository $cart = $this->cartRepository->getCart($options[‘cart_id']); // Perform operation if ('add_to_cart' === $operation) { $cart->addProduct($options['product'], $options['type'], $options['quantity'], $options['price']); } elseif ('remove_from_cart' === $operation) { $cart->removeProduct($options['product']); } else { throw new InvalidArgumentException('operation not supported'); } $this->orderProcessor->processOrder($cart); // Save into repository $this->repository[$options['cart_id']] = $cart; return $cart; } }
  • 107. final class CheckoutController { private array $repository = []; private OrderProcessor $orderProcessor; public function __construct(OrderProcessor $orderProcessor) { $this->orderProcessor = $orderProcessor; } public function cartAction(string $operation, array $options): Cart { // Fetch object from repository $cart = $this->cartRepository->getCart($options[‘cart_id']); // Perform operation if ('add_to_cart' === $operation) { $cart->addProduct($options['product'], $options['type'], $options['quantity'], $options['price']); } elseif ('remove_from_cart' === $operation) { $cart->removeProduct($options['product']); } else { throw new InvalidArgumentException('operation not supported'); } $this->orderProcessor->processOrder($cart); // Save into repository $this->repository[$options['cart_id']] = $cart; return $cart; } }
  • 108. public function cartAction(string $operation, array $options): Cart { // Fetch object from repository $cart = $this->cartRepository->getCart($options['cart_id']); // Perform operation if ('add_to_cart' === $operation) { $this->messageBus->dispatch(new AddToCart( $options['cart_id'], $options['product'], $options['type'], $options['quantity'], $options['price'] )); } elseif ('remove_from_cart' === $operation) { $this->messageBus->dispatch(new RemoveFromCart( $options['cart_id'], $options['product'], )); } else { throw new InvalidArgumentException('operation not supported'); } $this->orderProcessor->processOrder($cart); // Save into repository $this->cartRepository->save($cart); return $cart; }
  • 109. public function cartAction(string $operation, array $options): Cart { // Fetch object from repository $cart = $this->cartRepository->getCart($options['cart_id']); // Perform operation if ('add_to_cart' === $operation) { $this->messageBus->dispatch(new AddToCart( $options['cart_id'], $options['product'], $options['type'], $options['quantity'], $options['price'] )); } elseif ('remove_from_cart' === $operation) { $this->messageBus->dispatch(new RemoveFromCart( $options['cart_id'], $options['product'], )); } else { throw new InvalidArgumentException('operation not supported'); } $this->orderProcessor->processOrder($cart); // Save into repository $this->cartRepository->save($cart); return $cart; }
  • 110. final class AddToCart { private string $cartId; private string $product; private string $type; private int $quantity; private int $price; public function __construct(...) { $this->cartId = $cartId; $this->product = $product; $this->type = $type; $this->quantity = $quantity; $this->price = $price; } }
  • 111. final class AddToCartHandler implements MessageHandlerInterface { private CartRepository $cartRepository; public function __construct(CartRepository $cartRepository) { $this->cartRepository = $cartRepository; } public function __invoke(AddToCart $addToCart) { $cart = $this->cartRepository->getCart($addToCart->getCartId()); $cart->addProduct( $addToCart->getProduct(), $addToCart->getType(), $addToCart->getQuantity(), $addToCart->getPrice() ); $this->cartRepository->save($cart); } }
  • 112. public function cartAction(string $operation, array $options): Cart { // Fetch object from repository $cart = $this->cartRepository->getCart($options['cart_id']); // Perform operation if ('add_to_cart' === $operation) { $this->messageBus->dispatch(new AddToCart( $options['cart_id'], $options['product'], $options['type'], $options['quantity'], $options['price'] )); } elseif ('remove_from_cart' === $operation) { $this->messageBus->dispatch(new RemoveFromCart( $options['cart_id'], $options['product'], )); } else { throw new InvalidArgumentException('operation not supported'); } $this->orderProcessor->processOrder($cart); // Save into repository $this->cartRepository->save($cart); return $cart; }
  • 113. public function cartAction(string $operation, array $options): Cart { // Fetch object from repository $cart = $this->cartRepository->getCart($options['cart_id']); // Perform operation if ('add_to_cart' === $operation) { $this->messageBus->dispatch(new AddToCart( $options['cart_id'], $options['product'], $options['type'], $options['quantity'], $options['price'] )); } elseif ('remove_from_cart' === $operation) { $this->messageBus->dispatch(new RemoveFromCart( $options['cart_id'], $options['product'], )); } else { throw new InvalidArgumentException('operation not supported'); } $this->orderProcessor->processOrder($cart); // Save into repository $this->cartRepository->save($cart); return $cart; }
  • 114. public function cartAction(CartIdAwareCommand $operation): Cart { // Fetch object from repository $cart = $this->cartRepository->getCart($operation->getCartId()); // Perform operation $this->messageBus->dispatch($operation); $this->orderProcessor->processOrder($cart); // Save into repository $this->cartRepository->save($cart); return $cart; }
  • 115. public function cartAction(CartIdAwareCommand $operation): Cart { // Fetch object from repository $cart = $this->cartRepository->getCart($operation->getCartId()); // Perform operation $this->messageBus->dispatch($operation); $this->orderProcessor->processOrder($cart); // Save into repository $this->cartRepository->save($cart); return $cart; }
  • 116. LLoC AoC ACC Sample project 40 1 8 Composite iteration 73 4 3 Strategy iteration 102 7 1.86 Repository extraction 125 8 1.75 Command iteration 209 12 1.33
  • 118. final class AddToCartHandler implements MessageHandlerInterface { private CartRepository $cartRepository; public function __construct(CartRepository $cartRepository) { $this->cartRepository = $cartRepository; } public function __invoke(AddToCart $addToCart) { $cart = $this->cartRepository->getCart($addToCart->getCartId()); $cart->addProduct( $addToCart->getProduct(), $addToCart->getType(), $addToCart->getQuantity(), $addToCart->getPrice() ); $this->cartRepository->save($cart); } }
  • 119. final class AddToCartHandler implements MessageHandlerInterface { private CartRepository $cartRepository; public function __construct(CartRepository $cartRepository) { $this->cartRepository = $cartRepository; } public function __invoke(AddToCart $addToCart) { $cart = $this->cartRepository->getCart($addToCart->getCartId()); $cart->addProduct( $addToCart->getProduct(), $addToCart->getType(), $addToCart->getQuantity(), $addToCart->getPrice() ); $this->cartRepository->save($cart); } }
  • 120. final class AddToCartHandler implements MessageHandlerInterface { private CartRepository $cartRepository; public function __construct(CartRepository $cartRepository) { $this->cartRepository = $cartRepository; } public function __invoke(AddToCart $addToCart): Cart { $cart = $this->cartRepository->getCart($addToCart->getCartId()); $cart->addProduct( $addToCart->getProduct(), $addToCart->getType(), $addToCart->getQuantity(), $addToCart->getPrice() ); return $cart; } }
  • 121. final class AddToCartHandler implements MessageHandlerInterface { private CartRepository $cartRepository; public function __construct(CartRepository $cartRepository) { $this->cartRepository = $cartRepository; } public function __invoke(AddToCart $addToCart): Cart { $cart = $this->cartRepository->getCart($addToCart->getCartId()); $cart->addProduct( $addToCart->getProduct(), $addToCart->getType(), $addToCart->getQuantity(), $addToCart->getPrice() ); return $cart; } }
  • 122. final class TransactionDecorator { private MessageHandlerInterface $messageHandler; private CartRepository $cartRepository; public function __construct( MessageHandlerInterface $messageHandler, CartRepository $cartRepository ) { $this->messageHandler = $messageHandler; $this->cartRepository = $cartRepository; } public function __invoke(CartIdAwareCommand $cartIdAwareCommand) { // start transaction $cart = ($this->messageHandler)($cartIdAwareCommand); $this->cartRepository->save($cart); } }
  • 123. Can Symfony help us with it?
  • 125. LLoC AoC ACC Sample project 40 1 8 Composite iteration 73 4 3 Strategy iteration 102 7 1.86 Repository extraction 125 8 1.75 Command iteration 209 12 1.33 Decorator iteration 216 13 1.31
  • 128. LLoC AoC ACC Sample project 40 1 8 Composite iteration 73 4 3 Strategy iteration 102 7 1.86 Repository extraction 125 8 1.75 Command iteration 209 12 1.33 Decorator iteration 216 13 1.31
  • 129. LLoC AoC ACC Sample project 40 1 8 Composite iteration 73 4 3 Strategy iteration 102 7 1.86 Repository extraction 125 8 1.75 Command iteration 209 12 1.33 Decorator iteration 216 13 1.31
  • 130. LLoC AoC ACC Sample project 40 1 8 Composite iteration 73 4 3 Strategy iteration 102 7 1.86 Repository extraction 125 8 1.75 Command iteration 209 12 1.33 Decorator iteration 216 13 1.31
  • 131. LLoC AoC ACC Sample project 40 1 8 Composite iteration 73 4 3 Strategy iteration 102 7 1.86 Repository extraction 125 8 1.75 Command iteration 209 12 1.33 Decorator iteration 216 13 1.31
  • 135. Symfony may help you a lot