Over code valt te twisten. Menig developer, of persoon die met developers samenwerkt, weet dat developers kunnen debatteren over de ‘beste manier’ van programmeren. Dit kan soms lastig zijn wanneer je met meerdere developers aan hetzelfde project werkt dat een doorlooptijd heeft van meerdere maanden of zelfs jaren. Hoe zorg je er nou voor dat je toch overeenstemming vindt? In deze blog delen we graag onze werkwijze. En uiteraard zijn er meerdere manieren om hetzelfde probleem op te lossen.

Afspraken

Voor ons begint het bij een overeenstemming tussen de developers binnen een team of het hele bedrijf. Dat is belangrijk omdat het zorgt voor gelijkgezindheid. Persoonlijk ben ik het niet altijd eens met de afspraken die we hebben gemaakt, maar het voordeel is wel dat de meerderheid voorstander is en ik mij eraan kan conformeren. Er ontstaat geen discussie over de aanpak aangezien de afspraken zijn gedefinieerd. Vanzelfsprekend is het mogelijk om aanpassingen voor te stellen welke bij akkoord worden toegevoegd aan onze overeenkomst.

Gelukkig zijn wij niet de eerste die deze uitdaging willen tackelen. Er bestaan standaarden die we als leidraad gebruiken. Wanneer het bijvoorbeeld om de stijl van onze code gaat, maken we gebruik van een style guide. En in het geval van PHP coding standards, voldoen we aan de PSR-1 en PSR-2 van PHP-FIG. Op die manier heeft alle code, ongeacht wie het schrijft, dezelfde look & feel.

Over de architectuur kunnen we minder kort zijn. Dat lees je hieronder. 🤓👇

Concepten binnen onze architectuur

We maken intensief gebruik van het Laravel framework. Een framework biedt een basis voor de meest gangbare componenten zoals routing, database interactie, sessies noem maar op. Laravel is intuïtief, heeft een expressive code style en is laagdrempelig om te leren. Met al deze voordelen ligt voor ons de focus op de implementatie van functionaliteit die waarde toevoegt voor onze klant.

We vinden het belangrijk om consistent te zijn in de wijze waarop wij onze applicaties ontwikkelen. Zoals ik al aangaf, hanteren we een aantal PSR standaarden ten behoeve van onze code style. We maken gebruik van PHP Coding Standards Fixer die bij elke pull request (PR) checkt of de geschreven code hieraan voldoet. Elke PR wordt nagekeken door tenminste twee collega’s zodat we eventuele fouten, algoritmen of andere onduidelijkheden oplossen.

Graag neem ik je mee in de wijze waarop wij onze projecten structureren. Onderaan dit artikel vind je wat handige links naar meer uitgebreide informatie over de verschillende concepten.

Design patterns

Willen we onze code schaalbaar en flexibel houden, dan speelt de architectuur een belangrijke rol. Design pattern is een generieke beschrijving die het voordeel biedt dat het oplossingspatroon herkenbaar is, ongeacht de implementatiedetails. De toepassing van design patterns verhoogt in potentie de kwaliteit van software omdat ontwerpen worden toegepast die zich in de praktijk reeds hebben bewezen. Patronen die we vaak toepasssen zijn: Factories, Model-view-controllers, Strategy pattern en Repositories.

S.O.L.I.D.

Tevens conformeren we ons aan de principes die S.O.L.I.D. voorschrijft. Classes houden we zo klein mogelijk en zijn verantwoordelijk voor één taak (Single Responsibility). We gebruiken base classes die in basis de functionaliteit bevat welke we nodig hebben. Wanneer een nieuw type implementatie is vereist, die afwijkt van de standaard implementatie, dan maken we een nieuwe class die de base class extend (Open-Closed).

Ander voorbeeld, geschreven code moet implementatie onafhankelijk zijn. Daarvoor gebruiken we interfaces. De interface kan je als parameter typehint gebruiken binnen de method. Daardoor is de implementatie gebaseerd op abstractie (de interface) in plaats van een concrete implementatie (een class). Dit is wat Dependency Inversion voorschrijft.

Belangrijk om deze patronen te beheersen. Je weet immers nooit wat er in te toekomst gaat veranderen maar hiermee heb je de flexibiliteit.

Controllers

Die houden we het liefst zo compact en dom mogelijk. Dit doen we omdat een controller fungeert als een doorgeefluik van inkomende en uitgaande data. Wat je wilt voorkomen is dat ze zich bezig houden met de flow van de applicatie. Zodra je de logica in de controller plaatst kan je de functionaliteit niet hergebruiken zonder deze te kopiëren. Stel dat je de functionaliteit wilt aanroepen via een CLI command, dan roep je niet direct de controller class aan. Het kopiëren van code is het laatste wat we willen. Vandaar dat we de data doorgeven aan één of meerdere services.

Services

Oké, controllers maken zich dus niet druk om de flow, daarvoor gebruiken we services. Voordat de controller de request data ontvangt, is deze gevalideerd door een custom request class. Deze checkt of de invoer correct is en filtert eventuele karakters. De controller geeft zijn data door aan een service. Dit zijn classes toegewijd aan een bepaald onderdeel van je applicatie. Bijvoorbeeld, een service kan de flow bepalen van een authenticatie proces. Met de nadruk op flow. Een service weet inhoudelijk niet waar de data vandaan komt. Dat is weer de taak van de repository, hierover later meer. De flow van een authenticatie proces zou kunnen zijn dat je eerst de gebruiker opzoekt o.b.v e-mailadres, vervolgens checkt of de gebruiker geactiveerd is en wanneer dit het geval is, kan de gebruiker inloggen.

Repositories

De repository maakt zich druk over waar de data vandaan komt. Dit klinkt vaag, maar eigenlijk is het heel eenvoudig. Niet alle data hoeft uit dezelfde bron te komen. Het kan zijn dat je, in het geval van authenticatie, gebruikers wilt opzoeken in de database of op basis van een externe API zoals Facebook. Hetzelfde geldt voor opslag, niet alle data wordt weggeschreven naar dezelfde datastore. Je kunt je voorstellen dat je een repository hebt die gebruikersdata wegschrijft naar een database, maar gelijktijdig ook naar een in memory database oplossing zoals Redis.

Repositories zijn om nog een andere reden handig. Je hebt namelijk de mogelijkheid om functies te definiëren die je vaker nodig hebt. Bijvoorbeeld het teruggeven van een lijst van meest beluisterde nummers in de afgelopen week. In plaats van het herhaaldelijk bepalen van deze data, kan de repository een method bevatten die dit voor je doet.

Models

Wanneer onze code met een database praat, kan het zijn dat we gebruik maken van models. In Laravel betekent dit het gebruik van Eloquent. Een laag om PDO heen om op een object georiënteerde manier data op te halen en weg te schrijven naar de database. Tevens implementeert Eloquent het Active Record design pattern. Kort gezegd betekent dit dat wanneer het model wordt opgeslagen, dit direct naar een database wordt weggeschreven.

Entities

Dit zijn entiteiten binnen een applicatie. Een entity gebruik je wanneer iets een uniek identificeerbaar object is. Een muzieknummer is een voorbeeld van iets wat uniek is. Het fijne aan een entity is dat deze niet gekoppeld is aan een bepaalde implementatie. Waar models vrijwel direct gelieerd zijn aan een database, kan een entity een losstaande class zijn die wellicht een database record vertegenwoordigd, denk bijvoorbeeld aan Doctrine’s DBAL/ORM. Anderzijds kan het ook een resultaat van een API call vertegenwoordigen. Op die manier kan je binnen je applicatie altijd uitgaan van de structuur van dit object i.p.v. dat je de letterlijke JSON als array decodeert.

Value objects

Value objects zijn simpele entities waarbij het niet gaat om de identiteit van het object, maar de waarde die het object bevat. In tegenstelling tot entities, hebben value objects geen state. Een value object kan een adres vertegenwoordigen bestaande uit een straat, huisnummer en stad. Dit object kan weer onderdeel zijn van een entity.

Jobs

Dit zijn classes die een bepaalde taak (a)synchroon kunnen uitvoeren. Dit kan handig zijn voor het uitvoeren van taken die langer duren of misschien los staan van een bepaald proces. Denk hierbij aan het exporteren van data, resizen van een foto of het genereren van een rapport. Dankzij Laravel zijn we flexibel in de wijze waarop we dit toepassen. Zo kunnen we Jobs synchroon uitvoeren, maar ook laten afhandelen door één of meerdere servers. Dit alles hangt af van de eisen die we eraan stellen.

Conclusie

Zojuist zijn we van high-level naar low-level gegaan, door dieper in de applicatie structuur te duiken maar hopelijk heb je een goede indruk gekregen van de wijze waarop wij onze projecten opzetten. Uiteraard komt er nog meer bij kijken zoals monitoring, testen etc.

Mocht je nou meer willen weten of suggesties hebben, laat het ons dan weten in de comments of mail mij even: jeroen.dehaar@dij.digital

Resource

Repository pattern: https://webdevetc.com/blog/the-repository-pattern-in-php-and-laravel
Value objects: https://codete.com/blog/value-objects/
Jobs: https://laravel.com/docs/5.8/queues
S.O.L.I.D. Principles: https://medium.com/prod-io/solid-principles-takeaways-ec0825a07247
PHP-FIG: https://www.php-fig.org/

Geschreven door: Jeroen de Haar

Meer kennis bijspijkeren? Kom dan naar onze Meetup: Ode aan de Code!

Bekijk onze Meetups

Wij zijn altijd op zoek naar getalenteerde vakgenoten!

Bekijk onze vacatures