Magento 2 Dependency Injection

Gastblog van Jisse Reitsma (Yireo), trainer voor PHP programmeercursussen en Magento 2 development trainingen
Voor developers zijn er genoeg redenen om laaiend enthousiast te worden over Magento 2. Dependency Injection, een concept waar de technische architectuur van Magento 2 op is gebaseerd, is daar een goed voorbeeld van. Waar het voor developers vaak een bekend concept is kan het voor non-techneuten lastig zijn te begrijpen wat dit precies betekent voor Magento 2. Daarom vanuit mij als programmeur een uitleg over Dependency Injection (DI) voor non-programmeurs.

Object georiënteerd programmeren

De eerste belangrijke stap in het begrijpen van DI is het begrijpen van object georienteerd programmeren (OOP). OOP is belangrijk in Magento omdat hiermee functionaliteiten kunnen worden opgesplitst. Er is niet 1 grote kolossale codebase voor zowel alle onderdelen als de checkout en klantlogins. In plaats daarvan is de Magento code opgedeeld in modules, modules zijn weer opgedeeld in klassen (onderdeel van OOP) en iedere klasse heeft weer eigen methodes (ofwel functies). Door al die code onder te verdelen in groepen is de code gemakkelijker te onderhouden (en te testen), waardoor er weer minder bugs optreden.

Programmeurs hebben het onderling over OOP door te refereren naar design patterns. Dit zijn algemene programmeerstructuren waar iedere programmeur wel eens tegen aan loopt, of gebruik van maakt. Zo kan de ene programmeur tegen de andere zeggen dat hij een Abstract Factory toepast, zodat de andere programmeur snapt wat de een bedoeld zonder de code gezien te hebben. Een ander design pattern is Dependency Injection, en juist dat design pattern is dus de basis van Magento 2.

Dependencies tussen klassen

Om het probleem dat Dependency Injection (DI) oplost te begrijpen moet je eerst weten wat dependencies zijn. Dependencies (of gewoon in het Nederlands: afhankelijkheden) ontstaan overal waar software complexer wordt. Zo zijn bestellingen afhankelijk van klant- en productgegevens, en is het afhandelen van een winkelwagen weer afhankelijk van de checkout module. Door goed na te denken over de samenhang van de verschillende onderdelen in je Magento shop, welke beschreven worden in PHP klassen, kun je de relaties van die onderdelen gaan beschrijven.

Dat beschrijven is belangrijk, omdat daarmee duidelijk wordt hoe je de code kunt gebruiken en hoe niet. Zo moet je nadenken over of een Magento product catalogus ook te gebruiken moet zijn zonder een checkout mechanisme? En is voor een wishlist wel echt een klant account nodig? Wat als je nu de bestaande Magento wishlist (\Magento\Wishlist) wilt vervangen met een eigen wishlist (\Yireo\Wishlist)? Moet je dan ook de klant module (\Magento\Customer) veranderen?

Loose coupling en de richting van een dependency

Bij het opschrijven van een dependency is er dus een relatie tussen twee (of meerdere) onderdelen (oftewel, PHP klassen). Des te hechter die PHP klassen aan elkaar gekoppeld zijn, des te moeilijker het wordt om ze theoretisch weer los te koppelen. Dit is een reden waarom developers eigenlijk het concept van Loose Coupling na zouden moeten streven. Ze moeten proberen modules en klassen zo min mogelijk van elkaar afhankelijk te maken. Er zijn tal van design patterns en design concepten (bijvoorbeeld SOLID) die daarbij helpen.

Natuurlijk zal er wel altijd sprake blijven van een afhankelijkheid: Twee klassen die samen werken krijgen direct een relatie met elkaar. Maar is een wishlist nu afhankelijk van een klantaccount, of is een klantaccount afhankelijk van een wishlist? Over het algemeen zou het meer specifiekere (een wishlist) afhankelijk moeten zijn van het meer generieke (een account). Het bepalen van de richting van de dependency is dus cruciaal.

En nu Dependency Injection

Het belangrijkste doel van Dependency Injection is om de dependency te minimaliseren. Een wishlist heeft een account nodig, maar een account zou ook een wishlist nodig kunnen hebben. Maar beide benodigdheden zouden ook optioneel moeten kunnen zijn. Sterker: Beide onderdelen zouden uitwisselbaar moeten zijn. Je zou een Magento wishlist moeten kunnen vervangen met een derde partij wishlist, je zou een Magento klantaccount moeten kunnen vervangen met een ander soort account.

De eerste stap die Magento 2 hiervoor toepast is dat zowel de wishlist als het account worden opgeroepen via een Object Manager, die dynamisch (lees: met Magento XML code) kan bepalen welke klasse er op dat moment ingeladen moet worden. Waar dit voorheen overal hard-coded in PHP klassen werd aangeroepen doe je dit nu slechts één keer bovenin een klasse, binnen de PHP __constructor() methode. Die constructor wordt dus de lijm tussen klassen, andere plekken met afhankelijkheden zijn er niet. Er is dus minder code nodig.

Interface Driven Design

De tweede stap die Magento 2 voor Dependency Injection toepast is dat de belangrijkste klassen weer zijn gebaseerd op een interface (een soort van template-klasse) die je nooit rechtstreeks kan gebruiken, maar wat dient als een contract tussen programmeurs.

Op het moment dat Dependency Injection wordt toegepast is het de bedoeling dat er geen afhankelijkheid ontstaat tussen de Magento wishlist-module en de Magento klant-module. Deze onderdelen worden algemeen gemaakt. Je hebt het dan niet over een “Magento klant module” maar alleen over “een klant”. Of deze klant van een Magento module afkomt of ergens anders maakt niks uit. En om DI toe te passen koppel je niet de klassen van een wishlist of klant, maar koppel juist de interfaces. Mits dezelfde interfaces worden toegepast worden zijn deze onderdelen dan compleet uitwisselbaar. Dit concept noemen we vaker Interface Driven Design, waarbij we beginnen met het uitdenken van contracten en we pas later code schrijven om die code echt dingen te laten doen.

Waarom Dependency Injection?

Dat is allemaal leuk om te weten, maar wat heb je er nu aan als non-techneut? In de technische uiteenzetting hierboven krijg je een indruk van bepaalde concepten die programmeurs dienen te snappen voor ze succesvol met Magento 2 aan de slag kunnen. De lat wordt hoger gelegd: Je moet eerst denken en dan pas doen. Het voordeel hiervan is dat de kwaliteit van de code omhoog gaat. En een betere kwaliteit code betekent weer minder bugs.

Een ander belangrijk punt is dat dankzij DI Magento 2 klassen gemakkelijker te vervangen zijn met derde partij klassen. Met Magento 1 leverde dit al gauw conflicten op – twee Magento modules die hetzelfde trucje proberen uit te halen, en de andere module dus onklaar maken.- Dankzij DI kan er veel specifieker aangehaald worden wanneer iets vervangen wordt en wanneer niet. Er is nog steeds een kans op conflicten, maar die conflicten zijn op te lossen door het contract (de interfaces) strakker te definieren.

Nog een punt is dat dankzij Dependenci Injection de afhankelijkheden tussen modules minder groot zijn, waardoor je afzonderlijke delen weer gemakkelijker kan testen. Een concept als Unit Testing haakt daar weer op in: Als alle code-onderdelen afzonderlijk te testen zijn, wordt de kans op bugs weer minder.

Slotsom over Magento 2 en DI

Samenvattend levert Dependency Injection veel op voor Magento 2: Meer flexibiliteit, minder afhankelijkheden, betere testbaarheid, minder conflicten. Ook al is Magento 2 op dit moment nog een flinke hobbel voor webwinkeliers, het systeem zelf heeft genoeg potentie om na te gaan denken over een overstap.

Meer (technische) achtergrondinformatie staat in de Magento Developers documentatie.

Scan je eigen Magento shop op veiligheidslekken