SlideShare a Scribd company logo
Symfony Components


and Design Patterns
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
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
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);
}
}
}
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

Développer sous Sylius en 40 minutes chrono
Développer sous Sylius en 40 minutes chronoDévelopper sous Sylius en 40 minutes chrono
Développer sous Sylius en 40 minutes chrono
Maxime Huran 🌈
 
Monads do not Compose
Monads do not ComposeMonads do not Compose
Monads do not Compose
Philip Schwarz
 
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
 
Belajar Android Studio CRUD Data Mahasiswa
Belajar Android Studio CRUD Data MahasiswaBelajar Android Studio CRUD Data Mahasiswa
Belajar Android Studio CRUD Data Mahasiswa
Agus Haryanto
 
스위프트, 코틀린과 모던언어의 특징 (Swift, Kotlin and Modern Languages)
스위프트, 코틀린과 모던언어의 특징 (Swift, Kotlin and Modern Languages)스위프트, 코틀린과 모던언어의 특징 (Swift, Kotlin and Modern Languages)
스위프트, 코틀린과 모던언어의 특징 (Swift, Kotlin and Modern Languages)
Yongha Yoo
 
Workshop React.js
Workshop React.jsWorkshop React.js
Workshop React.js
Commit University
 
Grails object relational mapping: GORM
Grails object relational mapping: GORMGrails object relational mapping: GORM
Grails object relational mapping: GORM
Saurabh Dixit
 
Jpa 잘 (하는 척) 하기
Jpa 잘 (하는 척) 하기Jpa 잘 (하는 척) 하기
Jpa 잘 (하는 척) 하기
경원 이
 
Android Accessibility
Android AccessibilityAndroid Accessibility
Android Accessibility
Ascii Huang
 
Node.js middleware: Never again!
Node.js middleware: Never again!Node.js middleware: Never again!
Node.js middleware: Never again!
Timur Shemsedinov
 
Mastering RecyclerView Layouts
Mastering RecyclerView LayoutsMastering RecyclerView Layouts
Mastering RecyclerView Layouts
Dave Smith
 
Analysing in depth work manager
Analysing in depth work managerAnalysing in depth work manager
Analysing in depth work manager
bhatnagar.gaurav83
 
Practical Protocol-Oriented-Programming
Practical Protocol-Oriented-ProgrammingPractical Protocol-Oriented-Programming
Practical Protocol-Oriented-Programming
Natasha Murashev
 
Kotlin Coroutines in Practice @ KotlinConf 2018
Kotlin Coroutines in Practice @ KotlinConf 2018Kotlin Coroutines in Practice @ KotlinConf 2018
Kotlin Coroutines in Practice @ KotlinConf 2018
Roman Elizarov
 
Flux architecture
Flux architectureFlux architecture
Flux architecture
Boyan Mihaylov
 
Jetpack Compose a nova forma de implementar UI no Android
Jetpack Compose a nova forma de implementar UI no AndroidJetpack Compose a nova forma de implementar UI no Android
Jetpack Compose a nova forma de implementar UI no Android
Nelson Glauber Leal
 
날로 먹는 Django admin 활용
날로 먹는 Django admin 활용날로 먹는 Django admin 활용
날로 먹는 Django admin 활용
KyeongMook "Kay" Cha
 
Introduction to Tailwind CSS - Berkenalan dengan Tailwind CSS.pptx
Introduction to Tailwind CSS - Berkenalan dengan Tailwind CSS.pptxIntroduction to Tailwind CSS - Berkenalan dengan Tailwind CSS.pptx
Introduction to Tailwind CSS - Berkenalan dengan Tailwind CSS.pptx
Arya Wicaksana
 

What's hot (20)

Développer sous Sylius en 40 minutes chrono
Développer sous Sylius en 40 minutes chronoDévelopper sous Sylius en 40 minutes chrono
Développer sous Sylius en 40 minutes chrono
 
Monads do not Compose
Monads do not ComposeMonads do not Compose
Monads do not Compose
 
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
 
Express node js
Express node jsExpress node js
Express node js
 
Belajar Android Studio CRUD Data Mahasiswa
Belajar Android Studio CRUD Data MahasiswaBelajar Android Studio CRUD Data Mahasiswa
Belajar Android Studio CRUD Data Mahasiswa
 
스위프트, 코틀린과 모던언어의 특징 (Swift, Kotlin and Modern Languages)
스위프트, 코틀린과 모던언어의 특징 (Swift, Kotlin and Modern Languages)스위프트, 코틀린과 모던언어의 특징 (Swift, Kotlin and Modern Languages)
스위프트, 코틀린과 모던언어의 특징 (Swift, Kotlin and Modern Languages)
 
Workshop React.js
Workshop React.jsWorkshop React.js
Workshop React.js
 
Separation of concerns - DPC12
Separation of concerns - DPC12Separation of concerns - DPC12
Separation of concerns - DPC12
 
Grails object relational mapping: GORM
Grails object relational mapping: GORMGrails object relational mapping: GORM
Grails object relational mapping: GORM
 
Jpa 잘 (하는 척) 하기
Jpa 잘 (하는 척) 하기Jpa 잘 (하는 척) 하기
Jpa 잘 (하는 척) 하기
 
Android Accessibility
Android AccessibilityAndroid Accessibility
Android Accessibility
 
Node.js middleware: Never again!
Node.js middleware: Never again!Node.js middleware: Never again!
Node.js middleware: Never again!
 
Mastering RecyclerView Layouts
Mastering RecyclerView LayoutsMastering RecyclerView Layouts
Mastering RecyclerView Layouts
 
Analysing in depth work manager
Analysing in depth work managerAnalysing in depth work manager
Analysing in depth work manager
 
Practical Protocol-Oriented-Programming
Practical Protocol-Oriented-ProgrammingPractical Protocol-Oriented-Programming
Practical Protocol-Oriented-Programming
 
Kotlin Coroutines in Practice @ KotlinConf 2018
Kotlin Coroutines in Practice @ KotlinConf 2018Kotlin Coroutines in Practice @ KotlinConf 2018
Kotlin Coroutines in Practice @ KotlinConf 2018
 
Flux architecture
Flux architectureFlux architecture
Flux architecture
 
Jetpack Compose a nova forma de implementar UI no Android
Jetpack Compose a nova forma de implementar UI no AndroidJetpack Compose a nova forma de implementar UI no Android
Jetpack Compose a nova forma de implementar UI no Android
 
날로 먹는 Django admin 활용
날로 먹는 Django admin 활용날로 먹는 Django admin 활용
날로 먹는 Django admin 활용
 
Introduction to Tailwind CSS - Berkenalan dengan Tailwind CSS.pptx
Introduction to Tailwind CSS - Berkenalan dengan Tailwind CSS.pptxIntroduction to Tailwind CSS - Berkenalan dengan Tailwind CSS.pptx
Introduction to Tailwind CSS - Berkenalan dengan Tailwind CSS.pptx
 

Similar to Symfony World - Symfony components and design patterns

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
 
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
 
laravel tricks in 50minutes
laravel tricks in 50minuteslaravel tricks in 50minutes
laravel tricks in 50minutes
Barang CK
 
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
 
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
 

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)
 
購物車程式架構簡介
購物車程式架構簡介購物車程式架構簡介
購物車程式架構簡介
 
Symfony2 Building on Alpha / Beta technology
Symfony2 Building on Alpha / Beta technologySymfony2 Building on Alpha / Beta technology
Symfony2 Building on Alpha / Beta technology
 
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
 
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
 
PhpUnit - The most unknown Parts
PhpUnit - The most unknown PartsPhpUnit - The most unknown Parts
PhpUnit - The most unknown Parts
 
[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ć ...
 
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
 
Unittests für Dummies
Unittests für DummiesUnittests für Dummies
Unittests für Dummies
 
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
 
50 Laravel Tricks in 50 Minutes
50 Laravel Tricks in 50 Minutes50 Laravel Tricks in 50 Minutes
50 Laravel Tricks in 50 Minutes
 
laravel tricks in 50minutes
laravel tricks in 50minuteslaravel tricks in 50minutes
laravel tricks in 50minutes
 
Framework Project
Framework  ProjectFramework  Project
Framework Project
 
Adding Dependency Injection to Legacy Applications
Adding Dependency Injection to Legacy ApplicationsAdding Dependency Injection to Legacy Applications
Adding Dependency Injection to Legacy Applications
 
Zero to SOLID
Zero to SOLIDZero to SOLID
Zero to SOLID
 
Be RESTful (Symfony Camp 2008)
Be RESTful (Symfony Camp 2008)Be RESTful (Symfony Camp 2008)
Be RESTful (Symfony Camp 2008)
 
Clean Javascript
Clean JavascriptClean Javascript
Clean Javascript
 
Doctrine For Beginners
Doctrine For BeginnersDoctrine For Beginners
Doctrine For Beginners
 
Migrare da symfony 1 a Symfony2
 Migrare da symfony 1 a Symfony2  Migrare da symfony 1 a Symfony2
Migrare da symfony 1 a Symfony2
 
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
 

More from Ł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
 
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
 
Dutch php a short tale about state machine
Dutch php   a short tale about state machineDutch php   a short tale about state machine
Dutch php a short tale about state machine
Łukasz Chruściel
 
A short tale about state machine
A short tale about state machineA short tale about state machine
A short tale about state machine
Łukasz Chruściel
 
A short tale about state machine
A short tale about state machineA short tale about state machine
A short tale about state machine
Łukasz Chruściel
 
BDD in practice based on an open source project
BDD in practice based on an open source projectBDD in practice based on an open source project
BDD in practice based on an open source project
Łukasz Chruściel
 
Why do I love and hate php?
Why do I love and hate php?Why do I love and hate php?
Why do I love and hate php?
Łukasz Chruściel
 

More from Łukasz Chruściel (15)

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
 
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
 
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
 
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
 
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...
 
4Developers - Rozterki i decyzje.pdf
4Developers - Rozterki i decyzje.pdf4Developers - Rozterki i decyzje.pdf
4Developers - Rozterki i decyzje.pdf
 
4Developers - Sylius CRUD generation revisited.pdf
4Developers - Sylius CRUD generation revisited.pdf4Developers - Sylius CRUD generation revisited.pdf
4Developers - Sylius CRUD generation revisited.pdf
 
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
 
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
 
How to optimize background processes.pdf
How to optimize background processes.pdfHow to optimize background processes.pdf
How to optimize background processes.pdf
 
Dutch php a short tale about state machine
Dutch php   a short tale about state machineDutch php   a short tale about state machine
Dutch php a short tale about state machine
 
A short tale about state machine
A short tale about state machineA short tale about state machine
A short tale about state machine
 
A short tale about state machine
A short tale about state machineA short tale about state machine
A short tale about state machine
 
BDD in practice based on an open source project
BDD in practice based on an open source projectBDD in practice based on an open source project
BDD in practice based on an open source project
 
Why do I love and hate php?
Why do I love and hate php?Why do I love and hate php?
Why do I love and hate php?
 

Recently uploaded

Advanced Flow Concepts Every Developer Should Know
Advanced Flow Concepts Every Developer Should KnowAdvanced Flow Concepts Every Developer Should Know
Advanced Flow Concepts Every Developer Should Know
Peter Caitens
 
Cyaniclab : Software Development Agency Portfolio.pdf
Cyaniclab : Software Development Agency Portfolio.pdfCyaniclab : Software Development Agency Portfolio.pdf
Cyaniclab : Software Development Agency Portfolio.pdf
Cyanic lab
 
Exploring Innovations in Data Repository Solutions - Insights from the U.S. G...
Exploring Innovations in Data Repository Solutions - Insights from the U.S. G...Exploring Innovations in Data Repository Solutions - Insights from the U.S. G...
Exploring Innovations in Data Repository Solutions - Insights from the U.S. G...
Globus
 
Innovating Inference - Remote Triggering of Large Language Models on HPC Clus...
Innovating Inference - Remote Triggering of Large Language Models on HPC Clus...Innovating Inference - Remote Triggering of Large Language Models on HPC Clus...
Innovating Inference - Remote Triggering of Large Language Models on HPC Clus...
Globus
 
SOCRadar Research Team: Latest Activities of IntelBroker
SOCRadar Research Team: Latest Activities of IntelBrokerSOCRadar Research Team: Latest Activities of IntelBroker
SOCRadar Research Team: Latest Activities of IntelBroker
SOCRadar
 
A Comprehensive Look at Generative AI in Retail App Testing.pdf
A Comprehensive Look at Generative AI in Retail App Testing.pdfA Comprehensive Look at Generative AI in Retail App Testing.pdf
A Comprehensive Look at Generative AI in Retail App Testing.pdf
kalichargn70th171
 
How Recreation Management Software Can Streamline Your Operations.pptx
How Recreation Management Software Can Streamline Your Operations.pptxHow Recreation Management Software Can Streamline Your Operations.pptx
How Recreation Management Software Can Streamline Your Operations.pptx
wottaspaceseo
 
Why React Native as a Strategic Advantage for Startup Innovation.pdf
Why React Native as a Strategic Advantage for Startup Innovation.pdfWhy React Native as a Strategic Advantage for Startup Innovation.pdf
Why React Native as a Strategic Advantage for Startup Innovation.pdf
ayushiqss
 
Cracking the code review at SpringIO 2024
Cracking the code review at SpringIO 2024Cracking the code review at SpringIO 2024
Cracking the code review at SpringIO 2024
Paco van Beckhoven
 
Quarkus Hidden and Forbidden Extensions
Quarkus Hidden and Forbidden ExtensionsQuarkus Hidden and Forbidden Extensions
Quarkus Hidden and Forbidden Extensions
Max Andersen
 
Globus Compute wth IRI Workflows - GlobusWorld 2024
Globus Compute wth IRI Workflows - GlobusWorld 2024Globus Compute wth IRI Workflows - GlobusWorld 2024
Globus Compute wth IRI Workflows - GlobusWorld 2024
Globus
 
De mooiste recreatieve routes ontdekken met RouteYou en FME
De mooiste recreatieve routes ontdekken met RouteYou en FMEDe mooiste recreatieve routes ontdekken met RouteYou en FME
De mooiste recreatieve routes ontdekken met RouteYou en FME
Jelle | Nordend
 
First Steps with Globus Compute Multi-User Endpoints
First Steps with Globus Compute Multi-User EndpointsFirst Steps with Globus Compute Multi-User Endpoints
First Steps with Globus Compute Multi-User Endpoints
Globus
 
Paketo Buildpacks : la meilleure façon de construire des images OCI? DevopsDa...
Paketo Buildpacks : la meilleure façon de construire des images OCI? DevopsDa...Paketo Buildpacks : la meilleure façon de construire des images OCI? DevopsDa...
Paketo Buildpacks : la meilleure façon de construire des images OCI? DevopsDa...
Anthony Dahanne
 
How to Position Your Globus Data Portal for Success Ten Good Practices
How to Position Your Globus Data Portal for Success Ten Good PracticesHow to Position Your Globus Data Portal for Success Ten Good Practices
How to Position Your Globus Data Portal for Success Ten Good Practices
Globus
 
Enhancing Research Orchestration Capabilities at ORNL.pdf
Enhancing Research Orchestration Capabilities at ORNL.pdfEnhancing Research Orchestration Capabilities at ORNL.pdf
Enhancing Research Orchestration Capabilities at ORNL.pdf
Globus
 
2024 RoOUG Security model for the cloud.pptx
2024 RoOUG Security model for the cloud.pptx2024 RoOUG Security model for the cloud.pptx
2024 RoOUG Security model for the cloud.pptx
Georgi Kodinov
 
Explore Modern SharePoint Templates for 2024
Explore Modern SharePoint Templates for 2024Explore Modern SharePoint Templates for 2024
Explore Modern SharePoint Templates for 2024
Sharepoint Designs
 
Webinar: Salesforce Document Management 2.0 - Smarter, Faster, Better
Webinar: Salesforce Document Management 2.0 - Smarter, Faster, BetterWebinar: Salesforce Document Management 2.0 - Smarter, Faster, Better
Webinar: Salesforce Document Management 2.0 - Smarter, Faster, Better
XfilesPro
 
Multiple Your Crypto Portfolio with the Innovative Features of Advanced Crypt...
Multiple Your Crypto Portfolio with the Innovative Features of Advanced Crypt...Multiple Your Crypto Portfolio with the Innovative Features of Advanced Crypt...
Multiple Your Crypto Portfolio with the Innovative Features of Advanced Crypt...
Hivelance Technology
 

Recently uploaded (20)

Advanced Flow Concepts Every Developer Should Know
Advanced Flow Concepts Every Developer Should KnowAdvanced Flow Concepts Every Developer Should Know
Advanced Flow Concepts Every Developer Should Know
 
Cyaniclab : Software Development Agency Portfolio.pdf
Cyaniclab : Software Development Agency Portfolio.pdfCyaniclab : Software Development Agency Portfolio.pdf
Cyaniclab : Software Development Agency Portfolio.pdf
 
Exploring Innovations in Data Repository Solutions - Insights from the U.S. G...
Exploring Innovations in Data Repository Solutions - Insights from the U.S. G...Exploring Innovations in Data Repository Solutions - Insights from the U.S. G...
Exploring Innovations in Data Repository Solutions - Insights from the U.S. G...
 
Innovating Inference - Remote Triggering of Large Language Models on HPC Clus...
Innovating Inference - Remote Triggering of Large Language Models on HPC Clus...Innovating Inference - Remote Triggering of Large Language Models on HPC Clus...
Innovating Inference - Remote Triggering of Large Language Models on HPC Clus...
 
SOCRadar Research Team: Latest Activities of IntelBroker
SOCRadar Research Team: Latest Activities of IntelBrokerSOCRadar Research Team: Latest Activities of IntelBroker
SOCRadar Research Team: Latest Activities of IntelBroker
 
A Comprehensive Look at Generative AI in Retail App Testing.pdf
A Comprehensive Look at Generative AI in Retail App Testing.pdfA Comprehensive Look at Generative AI in Retail App Testing.pdf
A Comprehensive Look at Generative AI in Retail App Testing.pdf
 
How Recreation Management Software Can Streamline Your Operations.pptx
How Recreation Management Software Can Streamline Your Operations.pptxHow Recreation Management Software Can Streamline Your Operations.pptx
How Recreation Management Software Can Streamline Your Operations.pptx
 
Why React Native as a Strategic Advantage for Startup Innovation.pdf
Why React Native as a Strategic Advantage for Startup Innovation.pdfWhy React Native as a Strategic Advantage for Startup Innovation.pdf
Why React Native as a Strategic Advantage for Startup Innovation.pdf
 
Cracking the code review at SpringIO 2024
Cracking the code review at SpringIO 2024Cracking the code review at SpringIO 2024
Cracking the code review at SpringIO 2024
 
Quarkus Hidden and Forbidden Extensions
Quarkus Hidden and Forbidden ExtensionsQuarkus Hidden and Forbidden Extensions
Quarkus Hidden and Forbidden Extensions
 
Globus Compute wth IRI Workflows - GlobusWorld 2024
Globus Compute wth IRI Workflows - GlobusWorld 2024Globus Compute wth IRI Workflows - GlobusWorld 2024
Globus Compute wth IRI Workflows - GlobusWorld 2024
 
De mooiste recreatieve routes ontdekken met RouteYou en FME
De mooiste recreatieve routes ontdekken met RouteYou en FMEDe mooiste recreatieve routes ontdekken met RouteYou en FME
De mooiste recreatieve routes ontdekken met RouteYou en FME
 
First Steps with Globus Compute Multi-User Endpoints
First Steps with Globus Compute Multi-User EndpointsFirst Steps with Globus Compute Multi-User Endpoints
First Steps with Globus Compute Multi-User Endpoints
 
Paketo Buildpacks : la meilleure façon de construire des images OCI? DevopsDa...
Paketo Buildpacks : la meilleure façon de construire des images OCI? DevopsDa...Paketo Buildpacks : la meilleure façon de construire des images OCI? DevopsDa...
Paketo Buildpacks : la meilleure façon de construire des images OCI? DevopsDa...
 
How to Position Your Globus Data Portal for Success Ten Good Practices
How to Position Your Globus Data Portal for Success Ten Good PracticesHow to Position Your Globus Data Portal for Success Ten Good Practices
How to Position Your Globus Data Portal for Success Ten Good Practices
 
Enhancing Research Orchestration Capabilities at ORNL.pdf
Enhancing Research Orchestration Capabilities at ORNL.pdfEnhancing Research Orchestration Capabilities at ORNL.pdf
Enhancing Research Orchestration Capabilities at ORNL.pdf
 
2024 RoOUG Security model for the cloud.pptx
2024 RoOUG Security model for the cloud.pptx2024 RoOUG Security model for the cloud.pptx
2024 RoOUG Security model for the cloud.pptx
 
Explore Modern SharePoint Templates for 2024
Explore Modern SharePoint Templates for 2024Explore Modern SharePoint Templates for 2024
Explore Modern SharePoint Templates for 2024
 
Webinar: Salesforce Document Management 2.0 - Smarter, Faster, Better
Webinar: Salesforce Document Management 2.0 - Smarter, Faster, BetterWebinar: Salesforce Document Management 2.0 - Smarter, Faster, Better
Webinar: Salesforce Document Management 2.0 - Smarter, Faster, Better
 
Multiple Your Crypto Portfolio with the Innovative Features of Advanced Crypt...
Multiple Your Crypto Portfolio with the Innovative Features of Advanced Crypt...Multiple Your Crypto Portfolio with the Innovative Features of Advanced Crypt...
Multiple Your Crypto Portfolio with the Innovative Features of Advanced Crypt...
 

Symfony World - Symfony components and design patterns

  • 2.
  • 4.
  • 5.
  • 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
  • 49.
  • 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
  • 57.
  • 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); } } }
  • 82.
  • 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