PageSourceSearch

https://ivanchepurnyi.github.io/slides/architecture/sweden.html

html ivanchepurnyi.github.io collected 2026-10-03 09:21:21 UTC 35,032 bytes, 1,058 lines download raw bytes

1<!doctype html>
2<html lang="en">
3
4<head>
5    <meta charset="utf-8">
6
7    <title>MeetMagento Sweden 2017 - Architecture Challenges of Magento 2 Customizations</title>
8
9    <meta name="description" content="Architecture Challenges of Magento 2 Customizations">
10    <meta name="author" content="Ivan Chepurnyi">
11
12    <meta name="apple-mobile-web-app-capable" content="yes">
13    <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
14
15    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, minimal-ui">
16
17    
17<script>
18	window.baseUrl = '/bower_components/reveal.js';
19	
20	window.addJs = function (jsSrc, callback) {
21		var script = document.createElement('script');
22		script.src = baseUrl + jsSrc;
23		script.addEventListener('load', callback || function () {});
24		document.getElementsByTagName('body')[0].appendChild(script);
25	};
26	
27	window.addCss = function (cssSrc) {
28		var link = document.createElement( 'link' );
29        link.rel = 'stylesheet';
30        link.type = 'text/css';
31        link.href = baseUrl + cssSrc;
32        document.getElementsByTagName('head')[0].appendChild( link );
33	}
34    </script>
34
35
36    <!-- Printing and PDF exports -->
37    
37<script>
38	    addCss('/css/reveal.css');
39		addCss('/css/theme/white.css');
40		addCss('/lib/css/zenburn.css');
41        addCss((window.location.search.match( /print-pdf/gi ) ? '/css/print/pdf.css' : '/css/print/paper.css'));
42    </script>
42
43
44    <!--[if lt IE 9]>
45    
45<script>
46	    addJs('/lib/js/html5shiv.js');
47    </script>
47
48    <![endif]-->
49
50	<style type="text/css">
51	
52section .large {
53	font-size: 1.4em;
54}
55
56section .x-large {
57	font-size: 2.5em;
58}
59
60.template {
61    display: none;
62}
63
64.reveal section footer {
65    font-size: 1.0rem;
66}
67
68.reveal footer {
69    position: absolute;
70	top: 0px;
71	left: 0px;
72	display: block;
73	width: 100%;
74}
75
76.reveal footer div {
77   position: absolute;
78}
79
80@media print {
81	
82	.reveal footer div {
83        position: fixed;
84	}
85	
86	.reveal section.title-slide > div {
87		margin-top: 20%;
88	}
89}
90
91.reveal footer .left {
92	left: 10px;
93	top: 10px;
94}
95
96.reveal footer .right {
97	top: 10px;
98	right: 10px;
99}
100.reveal pre code {
101    font-size: 0.9rem;
102	line-height: normal;
103}
104.reveal section.title-slide {
105	height: 100%;
106	position: relative;
107}
108
109.reveal section.title-slide > div {
110	position: absolute;
111	top: 30%;
112	width: 100%;
113}
114
115.reveal section.title-slide > div > * {
116	display: block;
117    float: left;
118}
119
120.reveal section.title-slide img {
121	margin: 0;
122}
123
124.reveal section.title-slide > div .flex {
125	font-size: 1.4rem;
126	max-width: 78%;
127	padding-left: 10px;
128}
129
130.reveal section.title-slide h1 {
131	font-size: 3.6em;
132	padding-bottom: 10px;
133}
134
135.reveal section.title-slide h2 {
136	font-size: 2.2em;
137	color: #2081C4;
138}
139
140.reveal section.title-slide h1, 
141.reveal section.title-slide h2,
142.reveal section.title-slide p {
143	line-height: 0.85;
144	margin: 0;
145}
146
147.reveal section.title-slide > div .left {
148	width: 20%;
149}
150</style>
151<!--[if lt IE 9]>
152<script src="lib/js/html5shiv.js"></script>
152
153<![endif]-->
154</head>
155
156<body>
157
158<div class="reveal">
159	<footer>
160        <div class="left">MeetMagento Sweden 2017</div><div class="right">#mm17se</div>
161    </footer>
162 <!-- Any section element inside of this container is displayed as a slide -->
163 <div class="slides">
164 
165         <section class="title-slide">
166           <div>
167           	  <div class="left"><img src="/images/logo-icon.svg" class="plain" /></div>
168              <div class="flex">
169                  <h1>Architecture Challenges</h1>
170                  <h2>Magento 2 Customizations</h2>
171              </div>
172           </div>
173        </section>        
174        
175        <section>
176       	    <h2>About Me</h2>
177            <ul>
178            	<li class="fragment">Magento addict since May 2007</li>
179				<li class="fragment">I love to take challenges</li>
180   				<li class="fragment">I love to optimize performance</li>
181                <li class="fragment">I love to train others on those matters</li>
182            </ul>
183        </section>
184        
185        <section>
186        	<p class="large fragment">Are there any Software Architects?</p>
187            <p class="large fragment">Are there any Senior Developers?</p>
188            <p class="large fragment">Are there any Junior Developers?</p>
189        </section>
190                
191        <section>
192        	<p class="large">Every developer in this room is an architect</p>
193        </section>
194        
195        <section>
196           	<p class="large">We are all responsible for our Software Design</p>
197        </section>
198        
199        <section>
200        	<p class="large">Architecting software is hard...</p>
201        </section>
202        
203        <section>
204        	<p class="large">For example in the beggining of 2007</p>
205        </section>
206        
207        <section>
208        	<p class="large">ZF1 just went into first beta (0.8.0)</p>
209        </section>
210        
211        <section>
212        	<p class="large">Symfony 1.0 just got released</p>
213        </section>  
214        
215        <section>
216        	<p class="large">Composer?</p>
217        </section>
218      
219        <section>
220        	<p class="large">Unit testing was not yet a thing</p>
221        </section>  
222       
223        <section>
224        	<p class="large">Everyone was using Subversion (SVN)</p>
225        </section>
226         
227        <section>
228          <p class="large">I wish I knew everything I know now back then</p>
229        </section>
230        
231        <section>
232            <section>
233               <p class="large">Challenge #0: Using Magento 1.x approach</p>
234            </section>
235            
236            <section>
237               <p class="large">Magento 2.x is a huge step ahead and learning new concepts hard at first</p>
238            </section>
239            
240            <section>
241               <p class="large">But these new learning curve is going to pay off in the end</p>	
242            </section>	
243            
244            <section>
245                <p class="large">A lot of people tell that Magento 2 should have used Symfony and Doctrine...</p>
246            </section>
247            
248            <section>
249               <p class="large">But I am happy Magento 2.x does not use Symfony and Doctine</p>
250            </section>
251            
252            <section>
253                <p>Symfony has an excessive configuration boilerplate</p>
254                <pre><code class="xml stretch" data-trim>
255&#x3C;?xml version=&#x22;1.0&#x22; ?&#x3E;
256
257&#x3C;container xmlns=&#x22;http://symfony.com/schema/dic/services&#x22;
258    xmlns:xsi=&#x22;http://www.w3.org/2001/XMLSchema-instance&#x22;
259    xsi:schemaLocation=&#x22;http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd&#x22;&#x3E;
260
261    &#x3C;parameters&#x3E;
262        &#x3C;parameter key=&#x22;doctrine.orm.configuration.class&#x22;&#x3E;Doctrine\ORM\Configuration&#x3C;/parameter&#x3E;
263        &#x3C;parameter key=&#x22;doctrine.orm.entity_manager.class&#x22;&#x3E;Doctrine\ORM\EntityManager&#x3C;/parameter&#x3E;
264        &#x3C;parameter key=&#x22;doctrine.orm.manager_configurator.class&#x22;&#x3E;Doctrine\Bundle\DoctrineBundle\ManagerConfigurator&#x3C;/parameter&#x3E;
265
266        &#x3C;!-- cache --&#x3E;
267        &#x3C;parameter key=&#x22;doctrine.orm.cache.array.class&#x22;&#x3E;Doctrine\Common\Cache\ArrayCache&#x3C;/parameter&#x3E;
268        &#x3C;parameter key=&#x22;doctrine.orm.cache.apc.class&#x22;&#x3E;Doctrine\Common\Cache\ApcCache&#x3C;/parameter&#x3E;
269        &#x3C;parameter key=&#x22;doctrine.orm.cache.memcache.class&#x22;&#x3E;Doctrine\Common\Cache\MemcacheCache&#x3C;/parameter&#x3E;
270        &#x3C;parameter key=&#x22;doctrine.orm.cache.memcache_host&#x22;&#x3E;localhost&#x3C;/parameter&#x3E;
271        &#x3C;parameter key=&#x22;doctrine.orm.cache.memcache_port&#x22;&#x3E;11211&#x3C;/parameter&#x3E;
272        &#x3C;parameter key=&#x22;doctrine.orm.cache.memcache_instance.class&#x22;&#x3E;Memcache&#x3C;/parameter&#x3E;
273        &#x3C;parameter key=&#x22;doctrine.orm.cache.memcached.class&#x22;&#x3E;Doctrine\Common\Cache\MemcachedCache&#x3C;/parameter&#x3E;
274        &#x3C;parameter key=&#x22;doctrine.orm.cache.memcached_host&#x22;&#x3E;localhost&#x3C;/parameter&#x3E;
275        &#x3C;parameter key=&#x22;doctrine.orm.cache.memcached_port&#x22;&#x3E;11211&#x3C;/parameter&#x3E;
276        &#x3C;parameter key=&#x22;doctrine.orm.cache.memcached_instance.class&#x22;&#x3E;Memcached&#x3C;/parameter&#x3E;
277        &#x3C;parameter key=&#x22;doctrine.orm.cache.redis.class&#x22;&#x3E;Doctrine\Common\Cache\RedisCache&#x3C;/parameter&#x3E;
278        &#x3C;parameter key=&#x22;doctrine.orm.cache.redis_host&#x22;&#x3E;localhost&#x3C;/parameter&#x3E;
279        &#x3C;parameter key=&#x22;doctrine.orm.cache.redis_port&#x22;&#x3E;6379&#x3C;/parameter&#x3E;
280        &#x3C;parameter key=&#x22;doctrine.orm.cache.redis_instance.class&#x22;&#x3E;Redis&#x3C;/parameter&#x3E;
281        &#x3C;parameter key=&#x22;doctrine.orm.cache.xcache.class&#x22;&#x3E;Doctrine\Common\Cache\XcacheCache&#x3C;/parameter&#x3E;
282        &#x3C;parameter key=&#x22;doctrine.orm.cache.wincache.class&#x22;&#x3E;Doctrine\Common\Cache\WinCacheCache&#x3C;/parameter&#x3E;
283        &#x3C;parameter key=&#x22;doctrine.orm.cache.zenddata.class&#x22;&#x3E;Doctrine\Common\Cache\ZendDataCache&#x3C;/parameter&#x3E;
284
285        &#x3C;!-- metadata --&#x3E;
286        &#x3C;parameter key=&#x22;doctrine.orm.metadata.driver_chain.class&#x22;&#x3E;Doctrine\Common\Persistence\Mapping\Driver\MappingDriverChain&#x3C;/parameter&#x3E;
287        &#x3C;parameter key=&#x22;doctrine.orm.metadata.annotation.class&#x22;&#x3E;Doctrine\ORM\Mapping\Driver\AnnotationDriver&#x3C;/parameter&#x3E;
288        &#x3C;parameter key=&#x22;doctrine.orm.metadata.xml.class&#x22;&#x3E;Doctrine\ORM\Mapping\Driver\SimplifiedXmlDriver&#x3C;/parameter&#x3E;
289        &#x3C;parameter key=&#x22;doctrine.orm.metadata.yml.class&#x22;&#x3E;Doctrine\ORM\Mapping\Driver\SimplifiedYamlDriver&#x3C;/parameter&#x3E;
290        &#x3C;parameter key=&#x22;doctrine.orm.metadata.php.class&#x22;&#x3E;Doctrine\ORM\Mapping\Driver\PHPDriver&#x3C;/parameter&#x3E;
291        &#x3C;parameter key=&#x22;doctrine.orm.metadata.staticphp.class&#x22;&#x3E;Doctrine\ORM\Mapping\Driver\StaticPHPDriver&#x3C;/parameter&#x3E;
292                </code></pre>
293            </section>
294            
295            <section>
296                <p class="large">Doctrine is bound to its multi-vendor approach and forces code to be your data representation</p>
297                <pre><code class="php stretch" data-trim>
298/**
299 * @Entity @Table(name="bugs")
300 **/
301class Bug
302{
303    /**
304     * @Id @Column(type="integer") @GeneratedValue
305     **/
306    protected $id;
307    /**
308     * @Column(type="string")
309     **/
310    protected $description;
311    /**
312     * @Column(type="datetime")
313     **/
314    protected $created;
315    /**
316     * @Column(type="string")
317     **/
318    protected $status;
319
320    /**
321     * @ManyToOne(targetEntity="User", inversedBy="assignedBugs")
322     **/
323    protected $engineer;
324
325    /**
326     * @ManyToOne(targetEntity="User", inversedBy="reportedBugs")
327     **/
328    protected $reporter;
329
330    /**
331     * @ManyToMany(targetEntity="Product")
332     **/
333    protected $products;
334
335    // ... (other code)
336}
337                </code></pre>
338            </section>
339            
340            <section>
341            	<h3 class="large">Focus on your domain model</h3>
342                <p class="large">Do not drive design by your framework of choice, use it as a tool.</p>
343            </section>
344        </section>
345        
346        <section>
347        
348            <section>
349               <p class="large">Challenge #1: Looking at the core modules to create your own functionality</p>
350            </section>
351                    
352            <section>
353               <p class="large">There are some new code, but mostly it is still good old Magento 1.x code that is going to change</p>
354            </section>
355           
356            <section>
357              <p class="large">Do not look at the core module for an example of implemenation, do not copy-paste the code</p>
358            </section>
359         
360            <section>
361              <p class="large">Use core code only as a reference</p>
362            </section>
363            
364            <section>
365              <p class="large">Read <a href="http://devdocs.magento.com/">http://devdocs.magento.com/</a> for module structure guidelines</p>
366            </section>
367		</section>
368        
369        <section>
370                
371            <section>
372              <p class="large">Challenge #2: Data model dependencies</p>
373            </section>
374            
375            <section>
376              <p class="large">Global Website Store (GWS)</p>
377              
378              <aside class="notes">
379                 <p>Ask who is modeling data based on GWS?</p>
380              </aside>
381            </section>
382            
383            <section>
384              <p class="large">Binding your data model to GWS leads to scaling issues of your implementation</p>
385            </section>
386            
387            <section>
388              <h3>Example</h3>
389              <ul>
390                 <li class="fragment">EU Merchant can sell to 28 member states</li>
391                 <li class="fragment">Countries might have more then one language</li>
392                 <li class="fragment">Every country might get own domain name</li>
393                 <li class="fragment">Price might differ per country</li>
394              </ul>
395            </section>
396            
397            <section>
398              <h3>Problem</h3>
399              <ul>
400                 <li class="fragment">You might end up with 100+ store views</li>
401                 <li class="fragment">Language+Country results in data duplications in EAV structure</li>
402              </ul>
403            </section>
404            
405            <section>
406               <p class="large">Avoid existing structures where possible and inverse your dependencies</p>
407            </section>
408            
409            <section>
410               <h3>Solution</h3>
411               <ul>
412                  <li class="fragment">Create own entity: Country Scope</li>
413                  <li class="fragment">Create an adapter around store view to override configuration at runtime</li>
414                  <li class="fragment">Use store view as a language and link it to scope</li>
415                  <li class="fragment">Create country access attribute on product level if needed</li>
416               </ul>
417            </section>
418        
419        </section>
420        
421        <section>
422        
423            <section>
424               <p class="large">Challenge #3: Extending existing functionality</p>
425            </section>
426            
427            <section>
428              <p class="large">Rewrite (preference) is a bad thing</p>
429            </section>
430            
431            <section>
432              <p class="large">Plugin is not a good thing either</p>
433            </section>
434            
435            <section>
436               <h3>Plugin disadvantages</h3>
437               <ul>
438                  <li class="fragment">It is very hard to debug</li>
439                  <li class="fragment">It is global for an area</li>
440                  <li class="fragment">It can affect performance</li>
441               </ul>
442            </section>
443             
444            <section>
445              <p class="large">Local Dependency override is a good thing</p>
446            </section>
447            
448            <section>
449               <h3>How To</h3>
450               <ul>
451                  <li class="fragment">Find exact dependency you need to modify for customization</li>
452                  <li class="fragment">Create a decorator that implements same interface</li>
453                  <li class="fragment">Pass it into a class via di.xml</li>
454               </ul>
455            </section>
456        </section>
457        
458        <section>
459            
460            <section>
461               <p class="large">Challenge #4: Depending on concrete implementation</p>
462            </section>
463           
464            <section>
465               <p class="large">Injecting concrete class from another component leads to fragile dependency</p>
466            </section>
467            
468            <section>
469               <p class="large">
469Protect your code by introducing own abstractions</p>
470            </section>
471            
472            <section>
473               <h3>How To</h3>
474               <ul>
475                  <li class="fragment">Find functionality you want to use from Magento 2 module</li>
476                  <li class="fragment">Create your own interface for that implementation</li>
477                  <li class="fragment">Create an adapter based on that interface with Magento 2 module class as dependency</li>
478               </ul>
479            </section>
480        
481        </section>
482        
483        <section>
484        	<h2>Concrete Examples</h2>
485        </section>
486        
487        <section>
488        	<section>
489            	<p class="large">I need to export stock quantities of physical product SKUs for a specific store</p>
490               	<p class="fragment">So I want to use external module entity repository...</p>
491            </section>
492            
493            <section>
494            	<h2>Drawbacks</h2>
495				<ul>
496                	<li class="fragment">Direct use of repository requires too much boilerplate inside of business logic</li>
497                    <li class="fragment">Request parameters are not revealing the intent</li>
498                    <li class="fragment">Performance optimization would require complete rewrite of the code</li>
499                </ul>              
500            </section>
501            
502            <section>
503            	<p class="large">Create your own abstraction and implement Magento adapter for it</p>
504            </section>
505            
506            <section>
507            <h3>Component level abstraction</h3>
508            <pre><code class="php stretch" data-trim>
509namespace Conference\StockExport\Source;
510
511interface Entry
512{
513    public function getName();
514    
515    public function getSku();
516    
517    public function getQty();
518}
519
520interface EntryGateway
521{
522    public function getTotalPages($scopeId): int;
523
524    public function getEntriesByPage($scopeId, $page): Entry[];
525}
526</code></pre>
527            </section>
528            
529            <section>
530            <h3>Adapter for entry</h3>
531            <pre><code class="php stretch" data-trim>
532namespace Conference\StockExport\Source;
533
534
535use Magento\Catalog\Api\Data\ProductInterface;
536use Magento\CatalogInventory\Api\Data\StockStatusInterface;
537
538class MagentoEntry implements Entry
539{
540    private $product;
541    private $stockStatus;
542
543    public function __construct(ProductInterface $product, StockStatusInterface $stockStatus)
544    {
545        $this->product = $product;
546        $this->stockStatus = $stockStatus;
547    }
548
549    public function getName()
550    {
551        return $this->product->getName();
552    }
553
554    public function getSku()
555    {
556        return $this->product->getSku();
557    }
558
559    public function getQty()
560    {
561        return $this->stockStatus->getQty();
562    }
563}
564</code></pre>
565            </section>
566            
567            <section>
568            <h3>Adapter for entry gateway</h3>
569            <pre><code class="php stretch" data-trim>
570namespace Conference\StockExport\Source;
571
572use Magento\Catalog\Api\ProductRepositoryInterface;
573use Magento\CatalogInventory\Api\StockRegistryInterface;
574use Magento\Framework\Api\SearchCriteriaBuilder;
575use Magento\Store\Model\StoreManagerInterface;
576
577class MagentoEntryGateway implements EntryGateway
578{
579    private $searchCriteriaBuilder;
580
581    private $stockRegistry;
582
583    private $productRepository;
584
585    private $storeManager;
586
587    private $pageSize;
588
589    private $magentoEntryFactory;
590
591    public function __construct(
592        SearchCriteriaBuilder $searchCriteriaBuilder,
593        ProductRepositoryInterface $productRepository,
594        StoreManagerInterface $storeManager,
595        StockRegistryInterface $stockRegistry,
596        MagentoEntryFactory $magentoEntryFactory
597    ) {
598        $this->searchCriteriaBuilder = $searchCriteriaBuilder;
599        $this->productRepository = $productRepository;
600        $this->storeManager = $storeManager;
601        $this->stockRegistry = $stockRegistry;
602        $this->pageSize = 1000;
603        $this->magentoEntryFactory = $magentoEntryFactory;
604    }
605    // .. next slide
606}
607</code></pre>
608            </section>
609             <section>
610            <h3>Adapter for entry gateway</h3>
611            <pre><code class="php stretch" data-trim>
612namespace Conference\StockExport\Source;
613
614use Magento\Catalog\Api\ProductRepositoryInterface;
615use Magento\CatalogInventory\Api\StockRegistryInterface;
616use Magento\Framework\Api\SearchCriteriaBuilder;
617use Magento\Store\Model\StoreManagerInterface;
618
619class MagentoEntryGateway implements EntryGateway
620{
621    // .. prev slide
622    public function getTotalPages($scopeId): int
623    {
624        $this->storeManager->setCurrentStore($scopeId);
625        $searchCriteria = $this->createProductSearchCriteria(1, 1);
626        $searchResult = $this->productRepository->getList($searchCriteria);
627
628        return ceil($searchResult->getTotalCount() / $this->pageSize);
629    }
630
631    public function getEntriesByPage($scopeId, $page): \Traversable
632    {
633        $this->storeManager->setCurrentStore($scopeId);
634
635        $searchCriteria = $this->createProductSearchCriteria($this->pageSize, $page);
636        $searchResult = $this->productRepository->getList($searchCriteria);
637
638        foreach ($searchResult->getItems() as $productItem) {
639            yield $this->magentoEntryFactory->create([
640                'product' => $productItem,
641                'stockStatus' => $this->stockRegistry->getStockStatus(
642                    $productItem->getId(),
643                    $scopeId
644                ),
645            ]);
646        }
647    }
648    // .. next slide
649}
650</code></pre>
651            </section>
652            
653            <section>
654            <h3>Adapter for entry gateway</h3>
655            <pre><code class="php stretch" data-trim>
656namespace Conference\StockExport\Source;
657
658use Magento\Catalog\Api\ProductRepositoryInterface;
659use Magento\CatalogInventory\Api\StockRegistryInterface;
660use Magento\Framework\Api\SearchCriteriaBuilder;
661use Magento\Store\Model\StoreManagerInterface;
662
663class MagentoEntryGateway implements EntryGateway
664{
665    // .. prev slide
666    private function createProductSearchCriteria($pageSize, $currentPage): \Magento\Framework\Api\SearchCriteria
667    {
668        $searchCriteria = $this->searchCriteriaBuilder
669            ->addFilter('type_id', 'simple')
670            ->addFilter('status', '1')
671            ->setPageSize($pageSize)
672            ->setCurrentPage($currentPage)
673            ->create()
674        ;
675        return $searchCriteria;
676    }
677}
678</code></pre>
679</section>
680
681<section>
682	<h3>Somewhere in your component</h3>
683    <pre><code class="php stretch" data-trim>
684namespace Conference\StockExport;
685
686use Conference\StockExport\Source\EntryGateway;
687
688class Export
689{
690    private $entryGateway;
691
692    public function __construct(EntryGateway $entryGateway)
693    {
694        $this->entryGateway = $entryGateway;
695    }
696
697    public function export(\SplFileObject $file, $scopeId)
698    {
699        $file->fputcsv(['name', 'sku', 'qty']);
700
701        $totalPages = $this->entryGateway->getTotalPages($scopeId);
702
703        for ($page = 1; $page <= $totalPages; $page ++) {
704            foreach ($this->entryGateway->getEntriesByPage($scopeId, $page) as $entry) {
705                $file->fputcsv([$entry->getName(), $entry->getSku(), $entry->getQty()]);
706            }
707        }
708    }
709}
710    </code></pre>
711</section>
712
713<section>
714<h3>Benefits</h3>
715<ul>
716	<li class="fragment">EntryGateway is much easier to use as test dependency</li>
717  	<li class="fragment">It is less fragile, as Magento glue code is isolated from your co
717de</li>
718    <li class="fragment">Easy to implement new adapter for your component</li>
719</ul>
720</section>
721        </section>
722        
723         <section>
724        	<section>
725            	<p class="large">I want to auto-complete address by postcode and house number</p>
726            	<p class="fragment">So I need to customize Magento behaviour</p>
727            </section>
728            
729            <section>
730            	<h2>Extension Point</h2>
731                <pre><code class="php stretch" data-trim>
732 namespace Magento\Checkout\Model;
733
734class GuestShippingInformationManagement implements \Magento\Checkout\Api\GuestShippingInformationManagementInterface
735{
736    protected $quoteIdMaskFactory;
737
738    protected $shippingInformationManagement;
739
740    public function __construct(
741        \Magento\Quote\Model\QuoteIdMaskFactory $quoteIdMaskFactory,
742        \Magento\Checkout\Api\ShippingInformationManagementInterface $shippingInformationManagement
743    ) {
744        $this->quoteIdMaskFactory = $quoteIdMaskFactory;
745        $this->shippingInformationManagement = $shippingInformationManagement;
746    }
747
748    public function saveAddressInformation(
749        $cartId,
750        \Magento\Checkout\Api\Data\ShippingInformationInterface $addressInformation
751    ) {
752        $quoteIdMask = $this->quoteIdMaskFactory->create()->load($cartId, 'masked_id');
753        return $this->shippingInformationManagement->saveAddressInformation(
754            $quoteIdMask->getQuoteId(),
755            $addressInformation
756        );
757    }
758}
759</code></pre>
760            </section>
761            
762            <section>
763            	<h2>Abstraction</h2>
764                <pre><code class="php stretch" data-trim>
765namespace Conference\CompleteAddress\Model;
766
767interface CompletableAddress
768{
769    public function isComplete(): bool;
770
771    public function getPostcode(): string;
772
773    public function getHouseNumber(): string;
774
775    public function complete(string $street, string $city);
776}
777    			</code></pre>
778            </section>
779            
780             <section>
781            	<h2>Domain Logic</h2>
782                <pre><code class="php stretch" data-trim>
783namespace Conference\CompleteAddress\Model;
784
785class CompleteService
786{
787    public function completeAdddress(CompletableAddress $address)
788    {
789        if ($address->isComplete()) {
790            return;
791        }
792        
793        // .. do something with house number and postcode
794
795        $address->complete($streetName, $city);
796    }
797}
798    			</code></pre>
799            </section>
800            
801            <section>
802            	<h2>Address Adapter</h2>
803            	<pre><code class="php stretch" data-trim>
804namespace Conference\CompleteAddress\Model;
805
806use Magento\Checkout\Api\Data\ShippingInformationInterface;
807
808class MagentoCompletableAddress implements CompletableAddress
809{
810    private $shippingInformation;
811
812    public function __construct(ShippingInformationInterface $shippingInformation)
813    {
814        $this->shippingInformation = $shippingInformation;
815    }
816    // ... other methods
817    public function complete(string $street, string $city)
818    {
819        $this->getShippingAddress()->setCity($city);
820        $this->getShippingAddress()->setStreet([
821            $this->getShippingAddress()->getStreet()[0],
822            $street
823        ]);
824    }
825
826    private function getShippingAddress(): \Magento\Quote\Api\Data\AddressInterface
827    {
828        return $this->shippingInformation->getShippingAddress();
829    }
830}</code></pre>
831            </section>
832            
833            <section>
834            	<h2>Decorator</h2>
835                <pre><code class="php stretch" data-trim>
836namespace Conference\CompleteAddress\Model;
837
838use Magento\Checkout\Api\ShippingInformationManagementInterface;
839use Magento\Checkout\Api\Data\ShippingInformationInterface;
840
841class ShippingInfromationManagementDecorator implements ShippingInformationManagementInterface
842{
843    private $shippingInformationManagement;
844
845    private $completableAddressFactory;
846
847    private $completeService;
848
849    public function __construct(
850        ShippingInformationManagementInterface $shippingInformationManagement,
851        MagentoCompletableAddressFactory $completableAddressFactory,
852        CompleteService $completeService
853    ) {
854        $this->shippingInformationManagement = $shippingInformationManagement;
855        $this->completableAddressFactory = $completableAddressFactory;
856        $this->completeService = $completeService;
857    }
858
859    public function saveAddressInformation($cartId, ShippingInformationInterface $addressInformation)
860    {
861        $completableAddress = $this->completableAddressFactory->create(
862            ['shippingInformation' => $addressInformation]
863        );
864
865        $this->completeService->completeAdddress($completableAddress);
866        return $this->shippingInformationManagement->saveAddressInformation(
867            $cartId,
868            $addressInformation
869        );
870    }
871}
872    			</code></pre>
873            </section>
874            
875            <section>
876            	<h2>Configuration</h2>
877                <pre><code class="xml stretch" data-trim>
878&#x3C;?xml version=&#x22;1.0&#x22;?&#x3E;
879&#x3C;config xmlns:xsi=&#x22;http://www.w3.org/2001/XMLSchema-instance&#x22;
880        xsi:noNamespaceSchemaLocation=&#x22;urn:magento:framework:ObjectManager/etc/config.xsd&#x22;&#x3E;
881
882    &#x3C;type name=&#x22;Magento\Checkout\Model\GuestShippingInformationManagement&#x22;&#x3E;
883        &#x3C;arguments&#x3E;
884            &#x3C;argument
885                name=&#x22;shippingInformationManagement&#x22;
886                xsi:type=&#x22;object&#x22;&#x3E;
887                	Conference\CompleteAddress\Model\ShippingInformationManagementDecorator
888            &#x3C;/argument&#x3E;
889        &#x3C;/arguments&#x3E;
890    &#x3C;/type&#x3E;
891&#x3C;/config&#x3E;
892    			</code></pre>
893            </section>
894            
895        	<section>
896            	<p class="large">I want to customize Magento behaviour but there is no interface</p>
897            </section>
898            
899        	<section>
900            	<p class="large">Than you can create a plugin</p>
901            </section>
902            
903            <section>
904            	<h2>Plugin</h2>
905                <pre><code class="php stretch" data-trim>
906namespace Conference\CompleteAddress\Model;
907
908use Magento\Checkout\Model\ShippingInformationManagement;
909
910class ShippingInformationManagementPlugin
911{
912    private $completableAddressFactory;
913
914    private $completeService;
915
916    public function __construct(
917        MagentoCompletableAddressFactory $completableAddressFactory,
918        CompleteService $completeService
919    ) {
920        $this->completableAddressFactory = $completableAddressFactory;
921        $this->completeService = $completeService;
922    }
923
924    public function beforeSaveAddressInformation(
925        ShippingInformationManagement $subject,
926        $cartId,
927        ShippingInformationInterface $addressInformation
928    ) {
929        $completableAddress = $this->completableAddressFactory->create(
930            ['shippingInformation' => $addressInformation]
931        );
932
933        $this->completeService->completeAdddress($completableAddress);
934
935        return [$cartId, $addressInformation];
936    }
937}
938    			</code></pre>
939            </section>
940            <section>
941            	<h2>Configuration</h2>
942                <pre><code class="xml stretch" data-trim>
943&#x3C;?xml version=&#x22;1.0&#x22;?&#x3E;
944&#x3C;config xmlns:xsi=&#x22;http://www.w3.org/2001/XMLSchema-instance&#x22;
945        xsi:noNamespaceSchemaLocation=&#x22;urn:magento:framework:ObjectManager/etc/config.xsd&#x22;&#x3E;
946    &#x3C;type name=&#x22;Magento\Checkout\Model\ShippingInformationManagement&#x22;&#x3E;
947        &#x3C;plugin
948                type=&#x22;Conference\CompleteAddress\Model\ShippingInformationManagementPlugin&#x22;
949                name=&#x22;shipping_information_management_plugin&#x22; /&#x3E;
950    &#x3C;/type&#x3E;
951&#x3C;/config&#x3E;
952    			</code></pre>
953            </section>
954           
955        </section>
956        
957        <section>
958        	<h2>Summary</h2>
959            
960            <ul>
961            	<li class="fragment">Create your own abstractions and write adapter for a framework you use</li>
962                <li class="fragment">Use decorator for customizations and delegate to your domain model</li>                
963                <li class="fragment">Use plugins only when no interface available for decoration</li>
964            </ul>
965        </section>
966        
967        <section>
968        	<h2>Learn more</h2>
969            <ul> 
970            	<li class="fragment">Clean Architecture by Uncle Bob. <br /><a href="https://8thlight.com/blog/uncle-bob/2012/08/13/the-clean-architecture.html">bit.ly/clean-architecture</a></li>
971                <li class="fragment">Hexagonal Architecture by Alistair Cockburn <br /> <a href="http://alistair.cockburn.us/Hexagonal+architecture">bit.ly/hexagonal-architecture</a></li>
972                
973                <li class="fragment">Book: Growing Object-Oriented Software, Guided by Tests by Steve Freeman &amp; Nat Pryce</li>
974            </ul>
975        </section>
976       
977        <section>
978            <h1 class="fragment current-visible" data-fragment-index="0">Thank You</h1>
979            <h2 class="fragment" data-fragment-index="2">Q&amp;A</h2>
980            <p class="fragment twitter" data-fragment-index="2">@IvanChepurnyi</p>
981            <p class="fragment email" data-fragment-index="2">[email protected]</p>
982       </section>
983    </div>
984</div>
985
986
987
988<script>
989	addJs('/lib/js/head.min.js');
990	addJs('/js/reveal.js', function () {
991		// Full list of configuration options available at:
992		// https://github.com/hakimel/reveal.js#configuration
993		Reveal.initialize({
994			controls: true,
995			progress: true,
996			history: true,
997			center: false,
998			transition: 'convex', // none/fade/slide/convex/concave/zoom
999			keyboard: {
1000				66: 'toggleOverview',
1001				33: function () {
1002					if (Reveal.isOverview()) {
1003						return Reveal.navigateLeft();
1004					}
1005					
1006					return Reveal.navigatePrev();
1007				},
1008				34: function () {
1009					if (Reveal.isOverview()) {
1010						return Reveal.navigateRight();
1011					}
1012					
1013					return Reveal.navigateNext();
1014				}
1015			},
1016			// Optional reveal.js plugins
1017			dependencies: [
1018				{ src: baseUrl + '/lib/js/classList.js', condition: function() { return !document.body.classList; } },
1019				{ src: baseUrl + '/plugin/highlight/highlight.js', async: true, callback: function() { hljs.initHighlightingOnLoad(); } },
1020				{ src: baseUrl + '/plugin/zoom-js/zoom.js', async: true },
1021				{ src: baseUrl + '/plugin/notes/notes.js', async: true }
1022			]
1023		});
1024		
1025		var fixZoom = function () {
1026			var footer = document.querySelector('.reveal > footer');
1027			var slides = document.querySelector('.reveal > .slides');
1028			if (footer && slides) {
1029				if (slides.style.zoom) {
1030					footer.style.zoom = slides.style.zoom;
1031				} else if (slides.style.transform) {
1032					footer.style.transform = slides.style.transform.replace(/translate\([^\)]+\)/, '');
1033				}	
1034			}
1035		}
1036		
1037		Reveal.addEventListener('slidechanged', function () {
1038			fixZoom();
1039		});
1040		
1041		Reveal.addEventListener('ready', function () {
1042			var slides = document.querySelectorAll('.reveal section');
1043			for (var i in slides) {		
1044				var slide = slides.item(i);
1045				
1046				if (slide.classList.contains('title-slide')) {
1047					continue;
1048				}
1049				
1050				slide.classList.add('center');
1051			}
1052			fixZoom();
1053		});
1054	});
1055</script>
1055
1056
1057</body>
1058</html>

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.