JarnoVogelzang

Hoe voorkom je duplicatie? Simpel, door annotations!

Recentelijk werkte ik in een Symfony applicatie waarbij ik de taak had een veld in de database van naam te veranderen. Dat klinkt als een makkelijke opdracht en normaal gezien is dat ook zo. Het hoort in ieder geval simpel te zijn, sinds slechts 1 veld aan hoeft te worden gepast. Alleen het was makkelijker gezegd dan gedaan. Het probleem was namelijk dat ik niet alleen de naam van het desbetreffende veld moest aanpassen, maar ook alle code die ervan uitging dat dit specifieke veld die naam had.

Duplicatie

Hoe komt dat? Simpel, in dit project maak ik gebruik van Doctrine om mijn database op te bouwen. Er was in dit project een Doctrine Entity die er ongeveer zo uitzag (dit is niet de echte class, aangezien het gaat om closed-source code):

<?php

namespace App\Entity;

class ToDo {
  private string $name;

  public function getName(): string 
  {
     return $this->name;
  } 

  public function setName(string $name): void 
  {
     $this->name = $name;
  }
}

Dit is een normale Doctrine Entity class die je in meerdere applicaties zult tegenkomen. Het probleem echter was dat ik niet gebruik maakte van Doctrine annotations om de entity op te bouwen. Nee, ik maakte gebruik van Doctrine’s YAML mapping om de veldnamen te mappen naar database velden. Wat dat dus betekende is dat het concept ‘een ToDo heeft een veld genaamd “name”‘ op 2 plekken voorkwam. Zowel in de entity class als private field en in het YAML-bestand voor Doctrine:

App\Entity\ToDo:
  type: entity:
  fields:
    name:
      type: string 
      length: 50

Op zich niet zo’n probleem zou je zeggen. Je past de veldnaam aan in de class en in het YAML plekken, slechts 2 plekken. Was het maar zo. Niet alleen was er een YAML-bestand voor Doctrine, er was ook een YAML-bestand voor de Api Platform configuratie, een YAML-bestand voor de Symfony Serlializer en eentje voor de Symfony Validator.

Dat zijn dus minimaal 5 plekken waar ik de waarde moest aanpassen. Dus ook 5 keer de kans op een eventuele bug. Het is alsof de logica op meerdere plekken weerspiegeld wordt. Op zich niet zo’n probleem, aangezien het project volledig test driven is (zoals ik ook schreef in ‘Alleen geteste software werkt’). Maar het kost wel tijd. En tijd is geld.

Oplossing

Daarom de simpele oplossing: gebruik annotations op de plekken waar je ook aparte config-bestanden kunt gebruiken. Doctrine maakt standaard gebruik van annotations en dat geldt ook voor Api Platform, Symfony Serializer en Symfony Validator. De code komt er dan zo uit te zien:

<?php

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Serializer\Annotation\Groups;
use ApiPlatform\Core\Annotation\ApiResource;

/**
 * @ApiResource
 * @ORM\Entity
 */
class ToDo {
  /** 
   * @ORM\Column(type="string") 
   * @Assert\NotBlank
   * @Groups({"todo:read", "todo:write"})
   */
  private string $name;

  public function getName(): string 
  {
     return $this->name;
  } 

  public function setName(string $name): void 
  {
     $this->name = $name;
  }
}

Als je nu het veld ‘name’ van naamt wilt wijzigen, hoeft dat maar op 1 plek. Een bijkomend voordeel is ook dat alle logica bij elkaar staat en niet in aparte bestandjes. De volgende keer dat je dus het honderste config-bestand aanmaak, stel jezelf eens de vraag: kan dit ook in de bijbehorende code staan? Ik denk van wel! Tot volgende week!

Leave a Comment