How to Bundle!

This post ist partly a recap of the symfony life 2014 in Berlin where Toni (@havvg) and I did a "Lightning-Rant" on this topic. For some time now, we've been sporadically discussing the placement of models in the "VC-style-framework" symfony2. SFUGSTR did a whole meeting about that back in 2013 with great and controversial discussion :-) And since the new "Best Practices" book for development with symfony has emerged, I think it's time to write a little something myself.

Basics

As of now we've witnessed a lot of developers in projects using bundles to structure their applications, which - in itself - is no bad thing per se. Especially when starting to develop with symfony2 for the first time, it is not that easy to fully grasp the abstractness of the architecture and structure code accordingly. The basic idea of a bundle is having a container to put alle framework-related or -coupled stuff in a dedicated place where it can be found and interpreted. That's it! Framework stuff! A lot of people start packing "utilities", models, ... into a "CoreBundle" or "AppBundle" and that's not right (from an architectural point of view). To solve this misery there are a few approaches and I am going to discuss two of them.

Scenario

My assume following scenario, which I think is most common:

  • we have an application we know to most likely stay with symfony2 as framework
  • we are likely to have a lot of business logic
  • the application uses a database and therefore entities and repositories

The "one bundle to rule them all" approach

This is my preferred approach ;-) We're using exactly one bundle, no vendor namespace - I love calling it the "CoreBundle" - and place a second folder into the <project>src/ folder, called "Core" or whatever you want to name your business logic container. Within this container, I usually place every part of my business logic I do not get as an external library via packagist. To access this stuff, I create service definitions within the CoreBundle. Also I tend to place all controllers within the bundle, since they are also tightly coupled to the framework.

Structure:

project_root
 |- app
 |  |- ...
 |
 |- src
 |  |- CoreBundle
 |  |- Core
 |    |- subnamespace1
 |    |- subnamespace2
 |    |- ...
 | 
 ...

In my opinion this looks quite concise and also I love the extra part of the namespace, containing all my business logic (Core).

Benefits:

  • The overall structuring directory-wise is easy to grasp for people working with sf2
  • No changes concerning class-loading is needed, everything works out of the box

The "no bundle" approach

Toni (@havvg) is really fond of getting rid of all subfolders and bundles, so he suggests to pack all code flat into the src/ directory. You can find a sample project skeleton on GitHub.

Structure:

project_root
 |- app
 |  |- ...
 |
 |- src
 |  |- Controller
 |  |- Entity
 |  |- Repository
 |  |- ....
 | 
 ...

As you can see, this flattens the project structure in an immense way and also allows for nice DDD. Downside is: You have to overwrite e.g. the ApplicationKernel to be able to find Controllers and locate all the other stuff. For the people asking themselves "where are the templates and other files?!": They need to be placed in app/Resources/.

What does this mean for my OSS bundles?

I know, there are loads of bundles around in symfony-world. And lots of them have business logic within their directory structure. If you want to de-clutter this mess, try following approach: Pack all the relevant business-logic into a framework-decoupled package (library!), promote it on packagist and then write a bundle to make the library easily accessible via frameworks like symfony. Read this article for a nice approach to make your bundle framework-interoperable!

More to read on this topic:

Profiling for bottlenecks

On our way to fully understand how our software works and where there may be bottlenecks in our code, it's quite handy to have at least one server instance ready with XDebug installed. Since we've had some performance issues yesterday on fahrrad.de and the other shops, we've decided to profile the overview page. It's the page with the highest database/cache usage and a lot of nested controller calls (sf2 subrequests) and even small bottlenecks on that page normally kill the page load time. When we create profiles for later analysis with KCachegrind (KDE), we're usually profiling the page without cache enabled once and then another profile with cache enabled. So we're able to see where the initial page load might be further optimized and where the differences are to a fully cached page.

Loading the profile file with KCachegrind, I normally check the "Callee Map" first. I click on the main node from the Caller List on the left hand side: Then you get a nice image with loads of nested, colored rectangles, varying in size. Each of those rectangles represents one function call and its percentual amount of the processing time (in percent). If they are nested, it's a nested call and the size correlates to the processing time percentage. As you can see here: callee map without cache there's a lot of calls and three blocks in particular stick out of the mass: The're massive. Like we-are-40-percent-of-the-processing-time-massive. They represent our config manager and its loading of Yaml files via symfony2 Yaml component. It's a one-time thing, since the whole config stuff is cached in APC for one week, if there's no deployment in between. So nothing really obvious here... Next I check the Callgraph for any red colored method calls. Since there's nothing really bad besides our ConfigManager, we'll check the cached version of the page.

As expected, the whole config stuff is gone, lots of small rectangles: But one thing that catches the eye is the large green block in the upper left, representing a pdo object with a query. That's a real bummer... uncached call to the database. callee map with cache Switching to the Callgraph reveals: It's our realtime product availability check. callgraph

So here's at least something we can check if it's really necessary to make a database call everytime. Maybe store this in redis or...

Besides looking at and interpeting nice images, there's always The "Caller List" with all method calls and there you can check which ones use most of the processing time, how often are they called, ...

As you can see: Using the XDebug profiler really could help you identify bottlenecks!

New page generator

Gladly I can announce to have switched from pico to sculpin. Sculpin is a static page generator, using markdown like pico and basically working like Jekyll :-) Equipped like that, I decided to add a blog once more. This time mostly about coding...

Hopefully this time I'll be able to work on posts more frequently than before.