Upload
vinaikopp
View
5.826
Download
0
Embed Size (px)
Citation preview
Architecturein the Small
1 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015
Hi!
Vinai KoppFreelance Developer and Trainer@VinaiKopp
2 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015
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
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
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
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
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
Extraction&Encapsulation
8 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015
Extraction?WTF?
9 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015
A subset of
Refactoring
10 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015
„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
12 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015
The usual purpose of refactoring:
Get rid of smelly code
13 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015
But in case of extraction
It can be more
14 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015
Simplicityvs.
Amount of code
15 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015
SimplicityCan make things
visiblethat where hidden in the code
16 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015
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
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
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
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
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
Tell, don't ask!
22 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015
Extraction enables us to properly encapsulate functionality.
23 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015
Proposition:It is good to write code that is simple to extract.
24 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015
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
Managing State with Immutable Objects
26 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015
Immutawhat?
27 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015
„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
Yes, but... why?
29 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015
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
Reasonability
31 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015
Reading ➡ Understanding
32 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015
Understanding ➡ Knowing• Is it correct
or
• Is it incorrect
33 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015
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
Immutability helps to avoid temporal coupling
35 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015
For Example:// \Magento\Braintree\Model\PaymentMethod::partialCapture()$this->config->initEnvironment($payment->getOrder()->getStoreId());
36 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015
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
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
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
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
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
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
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
ExternalInitialization methodsandSetters
add complexity
44 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015
External immutability&Internal immutability
45 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015
Internal mutability allows for memoization
46 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015
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
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
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
Testing code which uses immutable objects is simpler
50 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015
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
Yuck!
52 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015
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
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
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
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
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
Once validalways validCan something be „half valid“?
58 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015
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
So how do the values get into an immutable object?
60 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015
Constructor Injection
61 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015
__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
Named constructor$request = HttpRequest::fromGlobalState();
63 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015
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
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
But IRLthings change
66 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015
Modeling change with immutable objectsChange is introduced by creating a new instance.
67 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015
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
Contraindications
69 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015
Does immutability make code more
complexor
simple?
70 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015
A thing that changes over time.
$room = new ConferenceRoom( $location, $event, $attendees)
71 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015
Is it a new conference room just because the number of people change?
72 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015
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
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
Temporal modelingAn instance represents the objectat one moment in time.
75 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015
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
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
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
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
The Value of Validity
80 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015
Enforce object validity at instantiation.
81 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015
Validation at Instantiation• __construct()• Named constructor (aka Factory Method)• Builder or Factory
82 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015
Constructor validationThe safest place to enforce validity
83 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015
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
When it gets more complex...
85 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015
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
Each thing is a classEach object validates it's data
87 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015
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
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
What about scalars?• string• int• bool• float• null• arrays of scalars
90 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015
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
Primitives ObsessionNo Type Safety
(until PHP 7)
Tests help
92 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015
Primitives ObsessionEasy to end up with Code Duplication
93 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015
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
Primitives ObsessionNo built-in validation
Tests help (again)
95 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015
Benefits of scalar types
96 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015
Benefits of scalar types
They are immutable
97 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015
Benefits of scalar types
They are easily created
98 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015
Benefits of scalar types
They are comparable
99 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015
Benefits of scalar types
Serializable
They work over the wire
100 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015
Benefits of scalar types
Completely upgrade safe
101 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015
Benefits of scalar types
They are fast
102 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015
Benefits of scalar types
They are language independent
103 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015
Scalarsvs.Value Objects
104 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015
Scalars make greatboundary interfaces
105 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015
Scalar Boundary Interfaces• Web API• Components API• Message Queue Payload
106 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015
Scalar Boundary Interfaces• Less interface dependencies• But implicit coupling to data format
107 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015
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
Immutable Variables
109 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015
In PHP?No
110 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015
But we can treat variables as immutable.
111 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015
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
What is the problem?• Messy to move or extract• Complex to reason about
113 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015
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
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
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
Immutable Variables++Reasonability
117 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015
Immutable VariablesLess bugs
118 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015
Immutable VariablesSimple to refactor
119 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015
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
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
Reasoning about Code with Idempotence
122 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015
Another of those words...
123 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015
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
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
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
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
Idempotent validateIsFooExample 2
protected function validateIsFoo($object){ return $object instanceof Foo;}// no output
128 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015
Pure FunctionsAll pure functions are idempotent
129 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015
Pure FunctionsNo side effects
130 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015
Pure FunctionsRely only on input arguments
131 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015
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
Pure FunctionsFor the same input they always return the same output
133 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015
And my point is?
134 Architecture in the Small - MageTitans 2015 - ! - @VinaiKopp - [email protected] - © 2015
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
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
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
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