Symfony2: Bundle your libraries!

In vielen Software-Projekten kommen Entwickler irgendwann an einen Punkt, an dem Funktionalität implementiert wird, die in anderen Projekten wiederverwendet werden können soll. Nachdem ich in der jüngeren Vergangenheit von mehreren Kollegen gefragt wurde, wie man dies in Symfony2-Projekten bewerkstelligen kann, wollte ich schriftlich eine Zusammenfassung der wichtigsten Punkte bereitstellen. Das Resultat ist dieser Blog-Artikel, der eine Schritt-für-Schritt-Anleitung enthält, wie man wiederverwendbaren Code kapselt und in einem eigenen Symfony2-Bundle bereitstellt.

Das Wissen, welches ich hier verpacke, habe ich teils aus der Symfony2-Dokumentation, teils aus der Erfahrung, welche ich in den letzten Jahren in Symfony2-Projekten gesammelt habe. Nachdem ich des Öfteren Library-Code aus Projekten ausgelagert hatte, konnte ich einige Patterns identifizieren und vor allem feststellen, dass diese Vorgehensweisen in Symfony2-Projekten (abgesehen von syntaktischen Eigenheiten) denen in beliebigen anderen Umgebungen sehr stark ähneln.

Die folgende Liste ist als Template zu verstehen, welches in einfachen Projekten genau so verwendet werden kann, in komplexeren jedoch wahrscheinlich erweitert werden muss.

    1. Werde Dir darüber klar, welche Teile wirklich wiederverwendbar werden sollen.
      Zu oft findet man „Libraries“, die so „generisch“ geschrieben sind, dass sie genau in einem Projekt Anwendung finden. Gute Libraries konzentrieren sich darauf, entweder genau eine Funktionalität oder eine API für eine externe Abhängigkeit bereitzustellen. 
    2. Definiere alle in Punkt 1 identifizierten Klassen als Services.
      An allen Stellen, an denen solche Klassen verwendet werden, sollten sie per Dependency Injection bereitgestellt werden. Eine Ausnahme stellen Value-Objects dar. Diese können beispielsweise durch eine Factory (welche wiederum als Service definiert ist) erzeugt werden.
      Der Sinn dieses Schritts ist es, die Schnittstellen zu der Library-Funktionalität explizit zu machen. Dies ist als Vorbereitung für das Verschieben des Codes notwendig. 
    3. Erstelle das neue Bundle
      Erstelle jetzt ein neues Bundle im Ordner src, in dem der Library-Code später unterkommt: 

      Im Folgenden bezeichne ich das neue Bundle LibraryBundle und das ursprüngliche, in dem das restliche Symfony2-Projekt lebt ProjectBundle.
    4. Verschiebe den Quellcode in das LibraryBundle
      Jetzt musst Du den in Punkt 1 identifizierten Code in das LibraryBundle verschieben. Danach müssen die Service-Definitionen der umgezogenen Klassen aus dem ProjektBundle in das LibraryBundle umgezogen werden. Wenn sich dadurch die Namen der Services ändern, müssen im ProjectBundle die Referenzen angepasst werden.
    5. Zwischenziel:
      Das Projekt sollte zu diesem Zeitpunkt ohne Einschränkung funktionieren! (Wo sind Deine Tests?) 
    6. Erstelle ein neues Git-Repository für das LibraryBundle
      Als Name eignet sich --bundle“ oder /-bundle“.
    7. Verschiebe den Code des LibraryBundles in das neue Repository
      Verschiebe alle Dateien und Ordner aus dem LibraryBundlein das neue Repository. 
    8. Composer-Support für das LibraryBundle
      Zusätzlich muss das neue Repository noch eine eigene composer.json (und am besten eine README.md) bekommen: 

      Die Einträge unter autoload und target-dir sorgen dafür, dass die Klassen im neuen Bundle zuerst an die richtige Stelle im Verzeichnis vendor installiert werden und dann richtig am Autoloader registriert werden.

      Damit ist das neue Repository erst einmal fertig. Im Projekt-Repository kann man jetzt alle Überreste des LibraryBundles entfernen.

    9. Veröffentlichen des LibraryBundles
      Das neue Repository kann jetzt an einem öffentlichen Ort (warum nicht GitHub?) veröffentlicht werden.
       

      Wenn das Paket öffentlich sichtbar (nicht nur in der eigenen Organisation) sein darf, kann an dieser Stelle ein Projekt auf Packagist erstellt werden.

 

  • Einbinden des LibraryBundles im Projekt
    Damit das LibraryBundle per Composer installiert werden kann, muss es noch in der composer.jsondes Projekt-Repositorys registrieren.
     

    Wenn das LibraryBundle nicht auf Packagist veröffentlicht wurde, muss ein custom Repository definiert werden:

    Wenn man jetzt im Projekt-Repository noch composer update ausführt, wird das LibraryBundle im Verzeichnis vendor installiert.

 

Herzlichen Glückwunsch!

Deine Library-Funktionalität ist nun vollständig in ein eigenes Bundle und Repository ausgelagert! Dein Projekt sollte genauso funktionsfähig sein wie vor dem Umbau! (Ansonsten solltest du jetzt überlegen, einen Kommentar zu diesem Artikel zu hinterlassen!)

Schlussbemerkungen

Lagere Library-Funktionalität am besten erst aus, wenn sie für den Moment fertig und getestet ist. Wenn der Code noch im Entwicklungs-Stadium ist, solltest du noch abwarten. Das Problem ist, dass Änderungen im Library-Repository committet/gepusht werden müssen und danach das Project-Repository per composer aktualisiert werden muss, nur um eine kleine Änderung im LibraryBundle im Kontext testen zu können.

Wenn du Code im LibraryBundle ändern musst, kannst du die Änderungen direkt auch im Ordner vendor/Mein/LibraryBundle vornehmen. In diesem Ordner liegt ein Clone des Library-Repositorys, in dem du auch committen und pushen kannst! Somit kann man das Problem des vorherigen Abschnitts umgehen. (Über die Sauberkeit dieser Vorgehensweise möchte ich an dieser Stelle jedoch kein Urteil fällen.)

Ein paar Wegweiser zur weiteren Lektüre:

Dieser Eintrag wurde veröffentlicht in Advent 2012, Development, Open Source, PHP und verschlagwortet mit , , , , , , von Paul Seiffert. Permanenter Link zum Eintrag.

Über Paul Seiffert

Paul ist seit Oktober 2010 ein Vollmatrose der Mayflower und arbeitet in einem Team von 6 Entwicklern als Lead-Architekt und stellvertretender Teamleiter. Zuvor studierte er an der TU München Informatik mit den Schwerpunkten Software-Engineering und Algorithmik. Sein Tech-Set umfasst sowohl PHP- als auch Javascript-Technologien wie Zend Framework 1, Symfony2, Node.js und Backbone.js. Paul ist immer an neuen Technologien und Vorgehensweisen interessiert und schreibt gerne über Neuerlerntes. Einige seiner Artikel sind hier auf http://blog.mayflower.de zu finden. Twitter: @SeiffertP Github: seiffert

Für neue Blogupdates anmelden:


4 Gedanken zu “Symfony2: Bundle your libraries!

  1. please consider writing in english

    nice approach to unfold code to share in bundles

    this is one of the main reasons why the centric one bundle approach was not the default in symfony2 or the decoupled approach having the libraries not being a bundle should also not necessarily be preferred. Bundles are good for code sharing and symfony2 should not be where it is were it not for bundles.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.