138
Architecture in the Small 1 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Architecture in-the-small-slides

Embed Size (px)

Citation preview

Page 1: Architecture in-the-small-slides

Architecturein the Small

1 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 2: Architecture in-the-small-slides

Hi!

Vinai KoppFreelance Developer and Trainer@VinaiKopp

2 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 3: Architecture in-the-small-slides

My PHP Evolution:1. Quick Hacks (1999)2. Typo3 Plugin Developer (2003)3. Magento Developer (2008)4. Experienced Magento Developer (2012)5. Discovering Code Beyond Magento (2014)

3 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 4: Architecture in-the-small-slides

I hope some of the following will be useful and interesting to play with for you, too

4 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 5: Architecture in-the-small-slides

Some of the many Peoplewho inspired and helped me• Rich Hickey• Robert C. Martin• Martin Fowler• Kent Beck

All brilliant people with a sense of humor!

5 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 6: Architecture in-the-small-slides

DisclaimerThe following thoughts are all mine, but influenced by reading these great peoples work.If I get something wrong, it is all my fault.

I might change my mind about something at any time.

6 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 7: Architecture in-the-small-slides

Topics

• Extraction & Encapsulation• Managing State with Immutable Objects• The Value of Validity• Immutable Variables• Reasoning about Code with Idempotence

7 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 8: Architecture in-the-small-slides

Extraction&Encapsulation

8 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 9: Architecture in-the-small-slides

Extraction?WTF?

9 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 10: Architecture in-the-small-slides

A subset of

Refactoring

10 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 11: Architecture in-the-small-slides

„Refactoring is the process of changing a software system in such a way that it does not alter the external behavior of the code yet improves its internal structure“

-- Martin Fowler, Refactoring (Addison-Wesley)

11 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 12: Architecture in-the-small-slides

12 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 13: Architecture in-the-small-slides

The usual purpose of refactoring:

Get rid of smelly code

13 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 14: Architecture in-the-small-slides

But in case of extraction

It can be more

14 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 15: Architecture in-the-small-slides

Simplicityvs.

Amount of code

15 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 16: Architecture in-the-small-slides

SimplicityCan make things

visiblethat where hidden in the code

16 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 17: Architecture in-the-small-slides

Extract Method Example - Before// Magento\Catalog\Model\Product

protected function convertToMediaGalleryInterface(array $mediaGallery){ $entries = []; foreach ($mediaGallery as $image) { $entry = $this ->mediaGalleryEntryConverterPool ->getConverterByMediaType($image['media_type']) ->convertTo($this, $image); $entries[] = $entry; } return $entries;}

17 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 18: Architecture in-the-small-slides

Extract Method Example - After (1/2)// Magento\Catalog\Model\Product

protected function convertToMediaGalleryInterface(array $mediaGallery){ $entries = []; foreach ($mediaGallery as $image) { $entries[] = $this->convertImageToMediaGalleryEntry($image); } return $entries;}

private function convertImageToMediaGalleryEntry(array $image){ $converter = $this->mediaGalleryEntryConverterPool ->getConverterByMediaType($image['media_type']); return $converter->convertTo($this, $image);}

18 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 19: Architecture in-the-small-slides

Extract Method Example - After (2/2)// Magento\Catalog\Model\Product

protected function convertToMediaGalleryInterface(array $mediaGallery){ $entries = []; foreach ($mediaGallery as $image) { $entries[] = $this->mediaGalleryEntryConverterPool ->convertImageToMediaGalleryEntry($image, $this); } return $entries;}

// Magento\Catalog\Model\Product\Attribute\Backend\Media\EntryConverterPool

public function convertImageToMediaGalleryEntry(array $image, Product $product){ $converter = $this->getConverterByMediaType($image['media_type']); return $converter->convertTo($product, $image);}

19 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 20: Architecture in-the-small-slides

Encapsulation„In general, encapsulation is the inclusion of one thing within another thing so that the included thing is not apparent.“

-- TechTarget SearchNetworking

20 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 21: Architecture in-the-small-slides

Encapsulation„Other objects ... use the object without having to be concerned with how the object accomplishes it.“

-- TechTarget SearchNetworking

21 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 22: Architecture in-the-small-slides

Tell, don't ask!

22 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 23: Architecture in-the-small-slides

Extraction enables us to properly encapsulate functionality.

23 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 24: Architecture in-the-small-slides

Proposition:It is good to write code that is simple to extract.

24 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 25: Architecture in-the-small-slides

Topics

• Extraction & Encapsulation• Managing State with Immutable Objects• The Value of Validity• Immutable Variables• Reasoning about Code with Idempotence

25 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 26: Architecture in-the-small-slides

Managing State with Immutable Objects

26 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 27: Architecture in-the-small-slides

Immutawhat?

27 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 28: Architecture in-the-small-slides

„In object-oriented and functional programming, an immutable object is an object whose state cannot be modified after it is created.“

-- Wikipedia, Immutable object

28 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 29: Architecture in-the-small-slides

Yes, but... why?

29 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 30: Architecture in-the-small-slides

I've read it helps avoid a whole class of bugs.

So, what are they?

30 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 31: Architecture in-the-small-slides

Reasonability

31 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 32: Architecture in-the-small-slides

Reading ➡ Understanding

32 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 33: Architecture in-the-small-slides

Understanding ➡ Knowing• Is it correct

or

• Is it incorrect

33 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 34: Architecture in-the-small-slides

When objects change in distant code, it becomes more complex to track what changed where and why.

34 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 35: Architecture in-the-small-slides

Immutability helps to avoid temporal coupling

35 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 36: Architecture in-the-small-slides

For Example:// \Magento\Braintree\Model\PaymentMethod::partialCapture()$this->config->initEnvironment($payment->getOrder()->getStoreId());

36 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 37: Architecture in-the-small-slides

Temporal Coupling:// \Magento\Braintree\Model\PaymentMethod::partialCapture()$this->config->initEnvironment($payment->getOrder()->getStoreId());

What state does the config start out in before it is initialized?

37 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 38: Architecture in-the-small-slides

Temporal Coupling:// \Magento\Braintree\Model\PaymentMethod::partialCapture()$this->config->initEnvironment($payment->getOrder()->getStoreId());

What happens if the config is initialized twice with different store ids?

38 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 39: Architecture in-the-small-slides

Temporal Coupling:// \Magento\Braintree\Model\PaymentMethod::partialCapture()$this->config->initEnvironment($payment->getOrder()->getStoreId());

What happens if another method is called before this one and the other one also relies on the config environment being initialized?

39 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 40: Architecture in-the-small-slides

Temporal Coupling:// \Magento\Braintree\Model\PaymentMethod::partialCapture()$this->config->initEnvironment($payment->getOrder()->getStoreId());

What happens if the call to initEnvironment is missed?

40 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 41: Architecture in-the-small-slides

Temporal Coupling:// \Magento\Braintree\Model\PaymentMethod::partialCapture()$this->config->initEnvironment($payment->getOrder()->getStoreId());

Extracting methods is tricky because the extracted part might rely on a different method being called first.This might not be apparent.

41 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 42: Architecture in-the-small-slides

Temporal Coupling:// \Magento\Braintree\Model\PaymentMethod::partialCapture()$this->config->initEnvironment($payment->getOrder()->getStoreId());

What happens if another object has a reference to the same config instance, but it doesn't know about or expect the config change?

42 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 43: Architecture in-the-small-slides

Temporal Coupling:// \Magento\Braintree\Model\PaymentMethod::partialCapture()$this->config->initEnvironment($payment->getOrder()->getStoreId());

What happens if a response of the payment instance is cached and then the config environment is changed?

43 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 44: Architecture in-the-small-slides

ExternalInitialization methodsandSetters

add complexity

44 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 45: Architecture in-the-small-slides

External immutability&Internal immutability

45 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 46: Architecture in-the-small-slides

Internal mutability allows for memoization

46 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 47: Architecture in-the-small-slides

Memoization :

„An optimization technique to speed up expensive method calls by storing the result after the first call and returning the cached result if the same inputs occur again.“

47 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 48: Architecture in-the-small-slides

Memoization Examplepublic function getAltitudeAvg(Location $loc, Date $date){ $coords = (string) $loc->getCoords(); $dateStr = (string) $date;

if (! @$this->memoizedResults[$coords][$dateStr]) { $avgAltitude = $this->doExpensiveCalculation($loc, $date); $this->memoizedResults[$coords][$dateStr] = $avgAltitude; }

return $this->memoizedResults[$coords][$dateStr];}

48 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 49: Architecture in-the-small-slides

Anyway...

Memoization is a lot simpler to do withexternally immutable objectssince nothing can invalidate the result.

49 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 50: Architecture in-the-small-slides

Testing code which uses immutable objects is simpler

50 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 51: Architecture in-the-small-slides

Test Double: Mutable Object$initWasCalled = false;$mockConfig = $this->getMock(\Magento\Braintree\Model\Config, [], [], '', false);

$mockConfig->method('initEnvironment') ->willReturnCallback(function () use (&$initWasCalled) { $initWasCalled = true; });

$mockConfig->method('canUseForCountry') ->willReturnCallback(function ($country) use (&$initWasCalled) { return $initWasCalled ? false : true; });

51 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 52: Architecture in-the-small-slides

Yuck!

52 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 53: Architecture in-the-small-slides

Incomplete Test Double:Mutable Object$initializedConfigMock = $this->getMock(\Magento\Braintree\Model\Config, [], [], '', false);

$initializedConfigMock->method('canUseForCountry')->willReturn(true);

Easy to miss calling initEnvironment in the system under test!

53 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 54: Architecture in-the-small-slides

Test Double: Immutable Object$configMock = $this->getMock(\Magento\Braintree\Model\Config, [], [], '', false);$configMock->method('canUseForCountry')->willReturn(true);

54 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 55: Architecture in-the-small-slides

Simpler to cacheNo need to worry that an object changes after it is written to cache.

55 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 56: Architecture in-the-small-slides

Example:

Magento EE PageCache<controller_front_send_response_before> <observers> <enterprise_pagecache> <class>enterprise_pagecache/observer</class> <method>cacheResponse</method> </enterprise_pagecache> </observers></controller_front_send_response_before>

56 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 57: Architecture in-the-small-slides

Example:

Magento EE PageCache

Events after cache write:

• controller_front_send_response_before(in a later Observer)

• controller_front_send_response_after

• http_response_send_before

57 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 58: Architecture in-the-small-slides

Once validalways validCan something be „half valid“?

58 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 59: Architecture in-the-small-slides

Example: invalidation of a mutable object

$product = $productCollection->getFirstItem();

$product->getPrice(); // 24.99$product->getSpecialPrice(); // 19.99

$product->setPrice(11.95);$product->setSpecialPrice(10.95);

$product->getFinalPrice() // 19.99

59 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 60: Architecture in-the-small-slides

So how do the values get into an immutable object?

60 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 61: Architecture in-the-small-slides

Constructor Injection

61 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 62: Architecture in-the-small-slides

__construct()$request = new HttpRequest( HttpRequest::METHOD_GET, HttpUrl::fromString('http://example.com/lookie/here'), HttpHeaders::fromArray($httpHeaders), HttpRequestBody::fromString($httpRequestBodyString));

62 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 63: Architecture in-the-small-slides

Named constructor$request = HttpRequest::fromGlobalState();

63 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 64: Architecture in-the-small-slides

Example 1: named constructorpublic static function fromGlobalState($requestBody = ''){ $method = $_SERVER['REQUEST_METHOD'];

$protocol = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS']) ? 'https' : 'http';

$path = $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; $url = $protocol . '://' . $path; $headers = self::getGlobalRequestHeaders();

return static::fromScalars($method, $url, $headers, $requestBody);}

64 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 65: Architecture in-the-small-slides

Example 2: named constructorpublic static function fromScalars( $methodString, $urlString, $headersArray, $bodyString) { $url = HttpUrl::fromString($urlString); $headers = HttpHeaders::fromArray($headersArray); $body = HttpRequestBody::fromString($bodyString);

return new self($methodString, $url, $headers, $body);}

65 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 66: Architecture in-the-small-slides

But IRLthings change

66 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 67: Architecture in-the-small-slides

Modeling change with immutable objectsChange is introduced by creating a new instance.

67 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 68: Architecture in-the-small-slides

Looks like mutation, but it ain't$today = new DateTimeImmutable();echo $today->format('Y-m-d H:i:s'); // 2015-10-29 21:32:06

$day = DateInterval::createFromDateString('24 hours');$tomorrow = $today->add($day);

echo $today->format('Y-m-d H:i:s'); // 2015-10-29 21:32:06echo $tomorrow->format('Y-m-d H:i:s');// 2015-10-30 21:32:06

68 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 69: Architecture in-the-small-slides

Contraindications

69 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 70: Architecture in-the-small-slides

Does immutability make code more

complexor

simple?

70 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 71: Architecture in-the-small-slides

A thing that changes over time.

$room = new ConferenceRoom( $location, $event, $attendees)

71 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 72: Architecture in-the-small-slides

Is it a new conference room just because the number of people change?

72 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 73: Architecture in-the-small-slides

A mutable conference room model:

$nPeople = $room->countPeople();$room->addPerson($attendee);

$room->countPeople() === $nPeople + 1;

73 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 74: Architecture in-the-small-slides

A immutable conference room model:

$nPeople = $room->countPeople();$updatedRoom = $room->addPerson($attendee);

$room->countPeople() === $nPeople;$updatedRoom->countPeople() === $nPeople + 1;

74 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 75: Architecture in-the-small-slides

Temporal modelingAn instance represents the objectat one moment in time.

75 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 76: Architecture in-the-small-slides

Temporal modeling(Immutable object at a moment in time)(Immutable object at a moment in time)(Immutable object at a moment in time)(Immutable object at a moment in time)...

All the same Entity that changes over time.

76 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 77: Architecture in-the-small-slides

Temporal modelingKeeps History !

Not a natural way to do OOP, adds complexity !

Unaccustomed way to think ! "

77 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 78: Architecture in-the-small-slides

Bottom LineIf you don't have to model changes over time, using immutability will probably make your code simpler.

If you do have to worry about changes over time...it depends.

78 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 79: Architecture in-the-small-slides

Topics

• Extraction & Encapsulation• Managing State with Immutable Objects• The Value of Validity• Immutable Variables• Reasoning about Code with Idempotence

79 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 80: Architecture in-the-small-slides

The Value of Validity

80 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 81: Architecture in-the-small-slides

Enforce object validity at instantiation.

81 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 82: Architecture in-the-small-slides

Validation at Instantiation• __construct()• Named constructor (aka Factory Method)• Builder or Factory

82 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 83: Architecture in-the-small-slides

Constructor validationThe safest place to enforce validity

83 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 84: Architecture in-the-small-slides

Example Constructor Validation public function __construct($amount){ if (!is_int($amount)) { $type = gettype($amount); $msg = sprintf('Can not create price from "%s"', $type); throw new InvalidAmountTypeException($msg); }

$this->amount = $amount;}

84 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 85: Architecture in-the-small-slides

When it gets more complex...

85 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 86: Architecture in-the-small-slides

Example Delegation of Validation// ProductAttributeListBuilder

public static function fromArray(array $attributesArray){ $attributes = array_map(function (array $attributeArray) { return ProductAttribute::fromArray($attributeArray); }, $attributesArray); return new self(...$attributes);}

public function __construct(ProductAttribute ...$attributes){ $this->validateAllAttributesHaveCompatibleContextData(...$attributes); $this->attributes = $attributes;}

86 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 87: Architecture in-the-small-slides

Each thing is a classEach object validates it's data

87 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 88: Architecture in-the-small-slides

Plug: Value ObjectsIntroduction slides on Value Objectsby Tim Bezhashvyly

http://vin.ai/tims_value_objects

88 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 89: Architecture in-the-small-slides

The cost of validityClasses require time to write, test and maintain.

• Each thing is a new class• Lots of code

89 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 90: Architecture in-the-small-slides

What about scalars?• string• int• bool• float• null• arrays of scalars

90 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 91: Architecture in-the-small-slides

Code Smell:Primitives Obsession„Primitives Obsession is using primitive data types to represent domain ideas.“

91 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 92: Architecture in-the-small-slides

Primitives ObsessionNo Type Safety

(until PHP 7)

Tests help

92 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 93: Architecture in-the-small-slides

Primitives ObsessionEasy to end up with Code Duplication

93 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 94: Architecture in-the-small-slides

Primitives ObsessionMagic values

„I knew at once what process(1.34, true) meant!“

-- Noone, ever

Class names have documentary value

94 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 95: Architecture in-the-small-slides

Primitives ObsessionNo built-in validation

Tests help (again)

95 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 96: Architecture in-the-small-slides

Benefits of scalar types

96 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 97: Architecture in-the-small-slides

Benefits of scalar types

They are immutable

97 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 98: Architecture in-the-small-slides

Benefits of scalar types

They are easily created

98 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 99: Architecture in-the-small-slides

Benefits of scalar types

They are comparable

99 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 100: Architecture in-the-small-slides

Benefits of scalar types

Serializable

They work over the wire

100 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 101: Architecture in-the-small-slides

Benefits of scalar types

Completely upgrade safe

101 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 102: Architecture in-the-small-slides

Benefits of scalar types

They are fast

102 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 103: Architecture in-the-small-slides

Benefits of scalar types

They are language independent

103 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 104: Architecture in-the-small-slides

Scalarsvs.Value Objects

104 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 105: Architecture in-the-small-slides

Scalars make greatboundary interfaces

105 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 106: Architecture in-the-small-slides

Scalar Boundary Interfaces• Web API• Components API• Message Queue Payload

106 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 107: Architecture in-the-small-slides

Scalar Boundary Interfaces• Less interface dependencies• But implicit coupling to data format

107 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 108: Architecture in-the-small-slides

Topics

• Extraction & Encapsulation• Managing State with Immutable Objects• The Value of Validity• Immutable Variables• Reasoning about Code with Idempotence

108 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 109: Architecture in-the-small-slides

Immutable Variables

109 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 110: Architecture in-the-small-slides

In PHP?No

110 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 111: Architecture in-the-small-slides

But we can treat variables as immutable.

111 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 112: Architecture in-the-small-slides

Example 1: reusing local variablespublic function testTwoRequestsAddTwoMessages(){ $request = $this->createProductUpdateRequest(); $app = new WebFront($request, $this->testFactory); $app->runWithoutSendingResponse();

$request = $this->createStockUpdateRequest(); $app = new WebFront($request, $this->testFactory); $app->runWithoutSendingResponse();

$queue = $this->testFactory->createQueue(); $this->assertCount(2, $queue);}// Multiple assignments to $request and $app

112 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 113: Architecture in-the-small-slides

What is the problem?• Messy to move or extract• Complex to reason about

113 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 114: Architecture in-the-small-slides

Example 1: treating local variables as immutablepublic function testTwoRequestsAddTwoMessages(){ $productUpdateRequest = $this->createProductUpdateRequest(); $firstApp = new WebFront($productUpdateRequest, $this->testFactory); $firstApp->runWithoutSendingResponse();

$stockUpdateRequest = $this->createStockUpdateRequest(); $secondApp = new WebFront($stockUpdateRequest, $this->testFactory); $secondApp->runWithoutSendingResponse();

$queue = $this->testFactory->createQueue(); $this->assertCount(2, $queue);}

114 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 115: Architecture in-the-small-slides

Example 2: local variable mutation in a loop// Magento\Catalog\Model\Product

protected function convertToMediaGalleryInterface(array $mediaGallery){ $entries = []; foreach ($mediaGallery as $image) { $entries[] = $this->mediaGalleryEntryConverterPool ->convertImageToMediaGalleryEntry($image, $this); } return $entries;}// Multiple assignments to $image, mutation of $entries

115 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 116: Architecture in-the-small-slides

Example 2: map/reduce helps avoiding local variable mutation// Magento\Catalog\Model\Product

protected function convertToMediaGalleryInterface(array $mediaGallery){ return array_map( function ($image) { $this->mediaGalleryEntryConverterPool ->convertImageToMediaGalleryEntry($image, $this); }, $mediaGallery );}

116 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 117: Architecture in-the-small-slides

Immutable Variables++Reasonability

117 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 118: Architecture in-the-small-slides

Immutable VariablesLess bugs

118 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 119: Architecture in-the-small-slides

Immutable VariablesSimple to refactor

119 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 120: Architecture in-the-small-slides

Immutable VariablesMy new friends:

• \Closure• array_map()• array_reduce()• array_merge()• array_filter()• recursion• ...120 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 121: Architecture in-the-small-slides

Topics

• Extraction & Encapsulation• Managing State with Immutable Objects• The Value of Validity• Immutable Variables• Reasoning about Code with Idempotence

121 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 122: Architecture in-the-small-slides

Reasoning about Code with Idempotence

122 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 123: Architecture in-the-small-slides

Another of those words...

123 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 124: Architecture in-the-small-slides

Idempotence

„In computer science, the term idempotent is used ... to describe an operation that will produce the same results if executed once or multiple times.“

-- Wikipedia, Idempotence, Computer Science

124 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 125: Architecture in-the-small-slides

What it ain'tExample 1class Logger{ public function log($level, $message) { $f = fopen($this->file . '-' . $level, 'a'); flock($f, LOCK_EX); fwrite($f, message); flock($f, LOCK_UN); fclose($f); }}// repeated calls add additional records

125 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 126: Architecture in-the-small-slides

Idempotent loggerExample 1class Logger{ public function log($level, $message) { $f = fopen($this->file . '-' . $level, 'a'); flock($f, LOCK_EX); if ($this->getLastLineFromFile() !== $message) { fwrite($f, message); } flock($f, LOCK_UN); fclose($f); }}// repeated calls add NO additional records

126 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 127: Architecture in-the-small-slides

What it ain'tExample 2protected function validateIsFoo($object){ if (! $object instanceof Foo) { echo "\$object is not an instance of Foo\n"; return false; } return true;}// repeated calls may produce repeated output

127 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 128: Architecture in-the-small-slides

Idempotent validateIsFooExample 2

protected function validateIsFoo($object){ return $object instanceof Foo;}// no output

128 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 129: Architecture in-the-small-slides

Pure FunctionsAll pure functions are idempotent

129 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 130: Architecture in-the-small-slides

Pure FunctionsNo side effects

130 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 131: Architecture in-the-small-slides

Pure FunctionsRely only on input arguments

131 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 132: Architecture in-the-small-slides

Pure FunctionsNo dependency on global state such as

• $_SERVER, $_SESSION• getcwd()• file_exists()• Mage::getIsDeveloperMode()

132 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 133: Architecture in-the-small-slides

Pure FunctionsFor the same input they always return the same output

133 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 134: Architecture in-the-small-slides

And my point is?

134 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 135: Architecture in-the-small-slides

Idempotent Functions areSimpler to reuseSimpler to compose

Pure Functions areSimpler to refactorSimpler to changeSimpler to reason about

135 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 136: Architecture in-the-small-slides

It pays to focus on the dependencies of every methodOOP is all about dependency management

136 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 137: Architecture in-the-small-slides

I'm still learningBut so far results are very encouraging.I'm happy for every tool that helps to reduce complexity

137 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015

Page 138: Architecture in-the-small-slides

Thanks for your attention!Please,Ask questions, andI'm eager to hear your thoughts and comments!

138 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015