Meet-Magento NL, 28. May 2015 Vinai Kopp
Meet-Magento NL, 28. May 2015
Vinai Kopp
About Vinai
2 The beautiful Magento Module November, 1st 2014
„Coding and Magento are obviously important parts of my life. But there is more then that. When I am not diving into the code or giving trainings, I like to spend time with my family, to run, to swim and to tend to my bees.”
3 Modern Module Architecture Meet-Magento NL, 28. May 2015
Why Architecture?
Motivation
4 Modern Module Architecture Meet-Magento NL, 28. May 2015
„We want our applications to contain two attributes: 1. High Maintainability 2. Low Technical Debt”
Chris Fidao Author of Implementing Laravel
5 Modern Module Architecture Meet-Magento NL, 28. May 2015
The Story
A Startup Delivery Service
Headline Georgia 28. Introduction
6 Modern Module Architecture Meet-Magento NL, 28. May 2015
Copy Georgia 20. Hi. I’m Vinai. This talk is about Magento 1. I think it is important to understand as much as possible about Magento 1, as it is the basis for Magento 2, where the framework has received …
normale Seite Inhalt
Headline Georgia 28. Introduction
7 Modern Module Architecture Meet-Magento NL, 28. May 2015
Copy Georgia 20. Hi. I’m Vinai. This talk is about Magento 1. I think it is important to understand as much as possible about Magento 1, as it is the basis for Magento 2, where the framework has received …
normale Seite Inhalt
8 Modern Module Architecture Meet-Magento NL, 28. May 2015
Customer Groups
9 Modern Module Architecture
Guest (NOT LOGGED IN) + General:
Delivery only to specified postcodes
Meet-Magento NL, 28. May 2015
Wholesale:
Approved at registration, no postcode check
THINK BIG
10 Modern Module Architecture
Franchise
Meet-Magento NL, 28. May 2015
Countries
Postcode Filter Rules
11 Modern Module Architecture Meet-Magento NL, 28. May 2015
Rule Field Example Rule 1
Country AT
Customer Groups NOT LOGGED IN, General
Postcodes 1, 2, 13, 12
Rule Field Example Rule 2
Country NL
Customer Groups NOT LOGGED IN, General
Postcodes 3500, 3521, 3527, ...
Wholesaler
12 Modern Module Architecture
No matching rule ===
delivery allowed
Meet-Magento NL, 28. May 2015
13 Modern Module Architecture Meet-Magento NL, 28. May 2015
Implementation #1
A Magento Module
Typical Magento Module
14 Modern Module Architecture
1. Create app/code/local/FoodInJar/PostCodeFilter/
2. Create postcodefilter_rule table with setup script
3. Create Rule model, resource model and collection
4. Create admin grid and form and controller
5. Create Observer for checkout integration
6. Create Ajax controller for additional frontend integration
Meet-Magento NL, 28. May 2015
Typical Magento Module
15 Modern Module Architecture
What does this code tell you?
Meet-Magento NL, 28. May 2015
„ I am a Magento Module.”
Typical Magento Module
16 Modern Module Architecture
How reusable is it?
Meet-Magento NL, 28. May 2015
„Well … not really.”
Typical Magento Module
17 Modern Module Architecture
Are there any tests?
Meet-Magento NL, 28. May 2015
„Why bother, it's boilerplate!”
Typical Magento Module
18 Modern Module Architecture
Does it work well with other extensions? Does it work with a new version of Magento 1.x? How fast can changes to the filter rules be implemented? How quickly can a new developer start to work on the code? How long to deployment after a bug was fixed? Can I use it in Magento 2?
Meet-Magento NL, 28. May 2015
Typical Magento Module
19 Modern Module Architecture Meet-Magento NL, 28. May 2015
„Okay, I admit it. Tests do add value.”
20 Modern Module Architecture Meet-Magento NL, 28. May 2015
Implementation #2
A Decoupled Architecture
Implementation #2
21 Modern Module Architecture Meet-Magento NL, 28. May 2015
A PHP Library
Decoupling
22 Modern Module Architecture Meet-Magento NL, 28. May 2015
My PostCodeFilter Code
Decoupling
23 Modern Module Architecture Meet-Magento NL, 28. May 2015
My PostCodeFilter Code 2
Decoupling
24 Modern Module Architecture Meet-Magento NL, 28. May 2015
My PostCodeFilter Code
Decoupling
25 Modern Module Architecture Meet-Magento NL, 28. May 2015
My PostCodeFilter Code
Flow of Information
26 Modern Module Architecture Meet-Magento NL, 28. May 2015
My PostCodeFilter Code
Storage
User Interface
ApplicaHon
Domain
Infrastructure
Implementation as a Library
27 Modern Module Architecture
Layered Architecture
Meet-Magento NL, 28. May 2015
User Interface (Delivery)
ApplicaHon (Use Cases)
Domain (Problem Space)
Infrastructure (DB)
Implementation as a Library
28 Modern Module Architecture
Layered Architecture
Meet-Magento NL, 28. May 2015
Implementation as a Library
29 Modern Module Architecture
Ports & Adapters
Hexagonical Architecture
Meet-Magento NL, 28. May 2015
Alistair Cockburn
http://alistair.cockburn.us/Hexagonal+architecture
„ Allow an application to equally be driven by users, programs, automated test or batch scripts, and to be developed and tested in isolation from its eventual run-time devices and databases.”
Implementation as a Library
30 Modern Module Architecture Meet-Magento NL, 28. May 2015
UI
Storage
Application +
Domain
Implementation as a Library
31 Modern Module Architecture Meet-Magento NL, 28. May 2015
Web
Tests
Mock DB
CSV MySQL
SOAP API
Tests
Application +
Domain
Implementation as a Library
32 Modern Module Architecture Meet-Magento NL, 28. May 2015
Postcode Filter Rules
Magento Admin Tests
Magento Frontend
Magento DB
Implementation as a Library
33 Modern Module Architecture
Well... where do I start…?
Meet-Magento NL, 28. May 2015
34 Modern Module Architecture
class CustomerSpecifiesShippingAddressTest extends \PHPUnit_Framework_TestCase { /** * @test */ public function itShouldReturnFalseIfTheCustomerMayNotOrder() { $this->assertFalse( $customerSpecifiesShippingAddress->isAllowedDestination( $customerGroupId, $country, $postCode ) ); } }
Test First
Meet-Magento NL, 28. May 2015
35 Modern Module Architecture
class CustomerSpecifiesShippingAddress { public function isAllowedDestination( $customerGroupId, $iso2country, $postCode ) { return false; } }
Test First
Meet-Magento NL, 28. May 2015
Implementation as a Library
36 Modern Module Architecture
Use Case Orientated Design
Meet-Magento NL, 28. May 2015
Implementation as a Library
37 Modern Module Architecture
Value Objects
Meet-Magento NL, 28. May 2015
Implementation as a Library
38 Modern Module Architecture
„ Architecture exposes usage”
Uncle Bob Martin
Meet-Magento NL, 28. May 2015
Plugging the Library into Magento
39 Modern Module Architecture Meet-Magento NL, 28. May 2015
My PostCodeFilter Code
Storage
Plugging the Library into Magento
40 Modern Module Architecture Meet-Magento NL, 28. May 2015
My PostCodeFilter Code
Storage
Using the Library in a Magento Module
41 Modern Module Architecture
Checking Customer Postcodes
Meet-Magento NL, 28. May 2015
My PostCodeFilter Code
42 Modern Module Architecture
use \Varien_Event_Observer as Event; use VinaiKopp\PostCodeFilter\CustomerSpecifiesShippingAddress; class VinaiKopp_PostCodeFilter_Model_Observer { // ... public function checkPostCodeIsAllowedIfNextStepIsShippingMethod( Event $event ) { /** @var Mage_Core_Controller_Response_Http $response */ $response = $event->getData('controller_action')->getResponse(); if ($this->isNextStepShippingMethod($response)) { $quote = $this->getCurrentCustomerQuote(); if (!$this->quoteMayBeOrdered($quote, $quote->getShippingAddress())) { $this->setErrorJsonResponse( $response, $quote->getShippingAddress() ); } } }
Query Postcode Filter Rules
Meet-Magento NL, 28. May 2015
43 Modern Module Architecture
use \Varien_Event_Observer as Event; use VinaiKopp\PostCodeFilter\CustomerSpecifiesShippingAddress; class VinaiKopp_PostCodeFilter_Model_Observer { // ... private function quoteMayBeOrdered( Mage_Sales_Model_Quote $quote, Mage_Sales_Model_Quote_Address $shippingAddress ) { $useCase = $this->getCustomerSpecifiesPostCodeUseCase(); return $useCase->isAllowedDestination( (int)$quote->getCustomerGroupId(), $shippingAddress->getCountry(), $shippingAddress->getPostcode() ); }
Query Postcode Filter Rules
Meet-Magento NL, 28. May 2015
Using the Library in a Magento Module
44 Modern Module Architecture
Storing Postcode Filter Rules in the Magento DB
Meet-Magento NL, 28. May 2015
Using the Library in a Magento Module
45 Modern Module Architecture Meet-Magento NL, 28. May 2015
My PostCodeFilter Code
Storage
46 Modern Module Architecture
namespace VinaiKopp\PostCodeFilter; interface RuleStorage { public function findPostCodesByCountryAndGroupId( $iso2country, $customerGroupId ); public function findRulesByCountryAndGroupIds( $iso2country, array $customerGroupIds ); public function findAllRules(); public function create($iso2country, $customerGroupId, array $postCodes); public function delete($iso2country, $customerGroupId); public function beginTransaction(); public function commitTransaction(); public function rollbackTransaction(); }
Storage Interface (Library Side)
Meet-Magento NL, 28. May 2015
47 Modern Module Architecture
use VinaiKopp\PostCodeFilter\RuleStorage; class VinaiKopp_PostCodeFilter_Model_Resource_RuleStorage implements RuleStorage { public function findPostCodesByCountryAndGroupId( $iso2country, $customerGroupId ) { $query = $this->readConnection->select() ->from($this->tableName, 'post_codes') ->where('country=?', $iso2country) ->where('customer_group_id=?', $customerGroupId); $result = $this->readConnection->fetchOne($query); if (! $result) { return []; } return $this->splitPostCodes($result); // ... }
Storage Implementation (Magento Side)
Meet-Magento NL, 28. May 2015
Instantiation of Library Classes
48 Modern Module Architecture Meet-Magento NL, 28. May 2015
Creating Something from Nothing
49 Modern Module Architecture
use VinaiKopp\PostCodeFilter\CustomerSpecifiesShippingAddress; class VinaiKopp_PostCodeFilter_Helper_Factory extends Mage_Core_Helper_Abstract { // ... public function createCustomerChecksPostCodeUseCase() { $this->registerPostCodeFilterAutoloader(); $class = 'vinaikopp_postcodefilter/ruleStorage' $storage = Mage::getResourceModel($class); return new CustomerSpecifiesShippingAddress( new RuleRepositoryReader($storage) ); }
Instantiation of Library Classes
Meet-Magento NL, 28. May 2015
50 Modern Module Architecture
class VinaiKopp_PostCodeFilter_Helper_Factory extends Mage_Core_Helper_Abstract { private $autoloaderRegistered = false; private function registerPostCodeFilterAutoloader() { if ($this->autoloaderRegistered) { return; } $this->autoloaderRegistered = true; spl_autoload_register(function ($class) { $prefix = 'VinaiKopp\\PostCodeFilter\\'; if (strncmp($prefix, $class, strlen($prefix)) !== 0) { return; } $this->buildPathToFileAndIncludeIfFileExists(); }, false, true); }
Instantiation of Library Classes
Meet-Magento NL, 28. May 2015
Using the Library in a Magento Module
51 Modern Module Architecture
Grid uses Adapter Collection
Meet-Magento NL, 28. May 2015
52 Modern Module Architecture
use VinaiKopp\PostCodeFilter\Rule\Rule; use VinaiKopp\PostCodeFilter\AdminViewsRuleList; class VinaiKopp_PostCodeFilter_Model_RuleCollection extends Varien_Data_Collection_Db { // ... public function load($printQuery = false, $logQuery = false) { $rules = $this->getUseCase()->fetchRules(); $this->_items = array_map([$this, 'convertRuleToVarienObject'], $rules); $this->_setIsLoaded(true); return $this; }
Collection adapts Postcode Filter Rules
Meet-Magento NL, 28. May 2015
53 Modern Module Architecture
use VinaiKopp\PostCodeFilter\Rule\Rule; use VinaiKopp\PostCodeFilter\AdminViewsRuleList; class VinaiKopp_PostCodeFilter_Model_RuleCollection extends Varien_Data_Collection_Db { // ... private function convertRuleToVarienObject(Rule $rule) { return new Varien_Object([ 'country' => $rule->getCountryValue(), 'customer_groups' => $rule->getCustomerGroupIdValues(), 'post_codes' => $rule->getPostCodeValues() ]); }
Collection adapts the Postcode Filter Rules
Meet-Magento NL, 28. May 2015
(GoF Adapter Pattern: Convert the interface of a class into another interface the clients expect.)
Dependencies
54 Modern Module Architecture Meet-Magento NL, 28. May 2015
OOP means Managing Dependencies
Using the Library in a Magento Module
55 Modern Module Architecture Meet-Magento NL, 28. May 2015
Using the Library in a Magento Module
56 Modern Module Architecture Meet-Magento NL, 28. May 2015
Visualizing Code Dependencies
57 Modern Module Architecture Meet-Magento NL, 28. May 2015
https://github.com/mamuz/PhpDependencyAnalysis
PhpDependencyAnalysis
Visualizing Code Dependencies: Call, Usage and Inheritance Graphs
58 Modern Module Architecture Meet-Magento NL, 28. May 2015
https://github.com/mamuz/PhpDependencyAnalysis
Does the Postcode Filter comply with the DIP?
59 Modern Module Architecture
Dependency Inversion Principle
Meet-Magento NL, 28. May 2015
• High-level modules should not depend on low-level modules. Both should depend on abstractions.
• Abstractions should not depend on details.
• Details should depend on abstractions.
Summary
60 Modern Module Architecture
Costs vs. Benefits
Meet-Magento NL, 28. May 2015
- More code (Library + Magento Adapter)
Complexity
Summary
61 Modern Module Architecture
Costs vs. Benefits
Meet-Magento NL, 28. May 2015
- Increased development cost (the first few times)
Complexity
Summary
62 Modern Module Architecture
Costs vs. Benefits
Meet-Magento NL, 28. May 2015
- Development has to be more thoughtful
Complexity
Summary
63 Modern Module Architecture
Costs vs. Benefits
Meet-Magento NL, 28. May 2015
Freedom
Summary
64 Modern Module Architecture
Costs vs. Benefits
Meet-Magento NL, 28. May 2015
+ Lower maintenance costs (Domain code independent of framework, test coverage)
Freedom
Summary
65 Modern Module Architecture
Costs vs. Benefits
Meet-Magento NL, 28. May 2015
+ Developers unfamiliar with Magento more productive
Freedom
Summary
66 Modern Module Architecture
Costs vs. Benefits
Meet-Magento NL, 28. May 2015
+ Real reusability (on component and class level)
Freedom
Summary
67 Modern Module Architecture
Costs vs. Benefits
Meet-Magento NL, 28. May 2015
+ Better upgradability (only one side of the adapter affected)
Freedom
Summary
68 Modern Module Architecture
Costs vs. Benefits
Meet-Magento NL, 28. May 2015
+ Easier to reason about domain
Freedom
Summary
69 Modern Module Architecture
Costs vs. Benefits
Meet-Magento NL, 28. May 2015
+ Usable with other platforms or frameworks (are you ready for Magento 2?)
Freedom
Summary
70 Modern Module Architecture
Costs vs. Benefits
Meet-Magento NL, 28. May 2015
+ Developer Satisfaction (able to choose testing, design, methodology, dicipline)
Freedom
Thank you!
71 Modern Module Architecture
Please, Ask questions Share thoughts Now :)
Meet-Magento NL, 28. May 2015
Vinai Kopp | @VinaiKopp | [email protected] https://github.com/Vinai/VinaiKopp_PostCodeFilter