Bundles, No Bundles and AppBundle in 10 Steps
Written by weaverryan, Leannapelham, and MolloKhan
Ah, the AppBundle: my favorite part of the Symfony best practices.
RYAN, What are you an idiot!? That's a terrible idea!
Ok, not everyone agrees - it's cool :). But here's what's interesting: if you don't like the AppBundle, I bet I actually agree with your reasons.
What!?
Let's figure this out in 10 Steps.
1) Keep Calm: Because I don't Care
I mean this with <3. We're using Symfony! This means you can do whatever you want. Nothing has changed in Symfony to prevent this, and nothing will. That's an awesome start.
2) Sorry, Your Bundles aren't Bundles: They're Directories
A traditional Symfony project is made up of bundles that are coupled together. Ok, maybe you have some standalone bundles, but somewhere, there's a group that are really coupled. And that's great! We're building an app here, not an open-source library. These coupled bundles are your app.
But in this case, they aren't really bundles: they're just directories. There's no technical advantage to having 1 or 10: we're just trying to organize things to our subjective liking.
A true bundle is a standalone, reusable entity. These are just directories.
3) AppBundle: Just a Different Directory Structure
Now, if we decide to move everything into one bundle, it's nothing more than
a different directory structure. You can even keep the same amount of organization
by putting sub-directories in Controller
or anywhere else.
You might like this, or you might not. The point is: it's subjective, there's no technical benefit of having multiple bundles.
4) AppBundle and AppKernel are Best Friends!
Your application is AppKernel, and it lives in app/
. It builds our
1 container, 1 compiled list of routes, 1 collection of translations, 1 group
of mapped entities and everything else. Even if you have these things spread
out across bundles, it's all loaded right here.
So why not move more things into app/
? Remember, we're not planning on
re-using this stuff somewhere else.
a) Move service configuration to app/
Since each kernel has only one container, it's logical to move service
config out of the bundle and into app/
.
But if I move my service configuration out of my bundle it's coupled to my app
That's right! But it probably already was coupled. And if you do need to re-use something, great! Put it in a true, standalone bundle. Here, I'm talking about moving pieces out of bundles that are truly a part of your app.
Tip
Like with everything, if you have a lot of services, feel free to create
an app/config/services
directory with multiple files.
b) Moving templates to app/
Next, let's move the templates into app/
. I know many people hate this,
because it puts the templates in a different directory than the controllers.
That's subjective, but fair - and I talk about that later.
This is a subjective change, but it has one hidden improvement: you no longer need to use the weird three-part colon syntax. As a Symfony expert, you know this syntax. But I give a lot of trainings, and these Symfony-isms give beginners a lot of trouble.
Instead, you just render the filename. The only rule you need to know is
that templates live in app/Resources/views
. This reduces complexity,
and that's huge.
5) No Bundles!?
You can keep doing this until AppBundle holds only PHP classes.
But wait, why do we need a bundle at all then? You don't! A bundle is just
a directory! And we can rename directories! We could rename this to Ryan
and even delete the bundle file.
Tip
You can start thinking of AppKernel as your one "bundle". It can do anything a bundle can, including complex stuff like registering compiler passes.
So why isn't this the Symfony best practice instead of AppBundle? A few reasons:
Without a bundle, you lose a few shortcuts, like the
_controller
shortcut and the automatic bundle aliasing for entities (e.g.AcmeDemoBundle:Post
). All of these are optional, but it's more work without them.It would be a big philosophical leap, and change needs to be done carefully. Having only one bundle was a big enough change.
But philosophically, I do hope you'll think of your AppBundle
as just
a directory for PHP classes. And for Symfony 3.0, maybe we'll get there!
6) I hate having my Templates in app/, Controllers in src/
The biggest complaint I've heard about the AppBundle is this: I don't like
that my controllers would live in src/
, but the templates they render
would live in app/
.
That's subjective, but totally fair (it hasn't bothered me).
To solve this, we could move our Ryan
directory (or AppBundle
, before
my rename) into app/
.
This works with no code changes except for a new autoload entry:
{
"autoload": {
"psr-4": { "Ryan\\": "app/src" }
}
}
I'm not recommending that everyone runs and does this, but logically, everything
is coupled to app/
, so it makes perfect sense. I hope it at least gets
you thinking!
Tip
Still want the templates closer to the controllers? No problem, keep
them in AppBundle
:).
7) But I want to create a Decoupled Library!
Sweet! Just create a directory in src/
and put your decoupled library
right there. It's ready to be re-used!
8) But I want to re-use a Bundle between projects or kernels!
Nice! Just create the bundle in src/
(or vendor/
, etc) and treat
it like true, decoupled bundle.
9) I don't know, I still want multiple Bundles
Still feel like you need more bundles? No worries - create as many as you
want. But don't be afraid to choose one bundle that you really couple
to your app/
directory - it might just make your life simpler.
10) What if I have multiple Kernels?
Multiple kernels? Sounds like a neat project :). You should have one super-coupled
bundle per kernel. For example, WebKernel
& WebBundle
, ApiKernel
and ApiBundle
. If you need to share things between kernels, put this
into proper, de-coupled bundles that are booted by each kernel.
Do We Agree Now?
One main argument against the AppBundle is that you should make your code modular. I agree! But having 1 directory or 10 doesn't make a difference. But these things do:
creating service classes, with minimal dependencies (+ skinny controllers);
(if applicable) identifying parts of your code that you truly need to re-use between projects/kernels and writing them as proper bundles or libraries;
potentially creating multiple, focused apps (e.g. backend API, frontend app, separate app for handling jobs, etc).
So even if you don't like the AppBundle, I hope you'll see that it has nothing to do with writing more or less modular code. That's still up to you :).
<3 Ryan
39 Comments
Hey Andreas!
You also make a really solid point, and one that I also agree with. Haha, funny how that can happen :). In fact, I *do* think that your point is the biggest potential risk of the new structure. But - as I think we both point out - the benefits/goal of this conversation is aimed at lowering the learning curve (and I think those "minor" benefits are not so minor for them), and so far, I've seen - in my totally subjective experience - the advantages to the AppBundle out-weigh the disadvantages. If that's true, it may be because by the time you are creating or digging into 3rd party bundles, you've comfortable enough to pick up on the small changes easily. Regardless, I'll have my "ear to the pavement" on this (and I hope everyone else will), and we can shape things going forward.
And yes about 3.0! That's our chance to make this be as natural as we really want it to be. But we should nail that down. Do you have some specific things that would make "app" feel more native in 3.0? Is it that the controllers and resources should be "closer"? Is it the missing bundle shortcuts (e.g. _controller, entity auto-aliasing, etc) if you only had an app/? Let me know - I've been thinking about this lately, and you have to answer me before you nice holiday :).
Cheers!
I don't care much about directory structure, as long as things belonging to a single context are not mixed with other contexts. /app/src
and /app/resources
would be fine, or /src
and /resources
, whatever...
For me it's mostly about various shortcuts and conventions, some examples:
Make the ControllerResolver/ControllerNameParser capable of handling a format like "MyController:index" for "application level controllers"
Streamline template reference syntax to work cleanly with and without bundles (using Puli?)
Fixing Doctrine shortcuts, possibly using a reserved prefix, or better: no prefix if they don't belong to a bundle (from the top of my head, I don't know if this is easily possible)
Fix the TemplateGuesser to be able to work with the @Template annotation outside of bundles (I know you discourage the annotation, but still ;))
Create an equivalent to a DI Extension class for the application as a point to manipulate services. While you could do that in the kernel, this would IMO be more clean and simple. Should also be entirely optional, since not required for all use cases.
@Andreas This is a *wonderful* list - I hadn't put this many ideas into my head yet this clearly. Now you've got me thinking! :D
Thank you!
Awesome post Ryan! as always. It is a great reminder to all of us that Symfony doesn't force you use certain structure at all and how you can do awesome stuff with it.
I can understand people who disagrees with some of these best practices. Some can be right, but it IMHO those disagreements are mostly related to how we are used to doing things in Symfony, especially those who've been using it for a long time.
Sometimes I think how simple would have been to do stuff I did months/years ago just by following some of these practices.
You, there and the rest of the team have done an awesome work with making things simpler, and not only for newcomers.
thanks Ryan, great article, all the mystic behind the "Bundle" disclosed... it really hard to keep up with best practices and figuring out the intent of them, but it was really nice of you to point out the nature of reusable bundles vs directories inside /src
Hey Mauricio Escudero
Thanks for your kind words :)
I just want to inform you that the app structure changed considerable in Symfony4, but it comes with a lot of handy tools, like services auto-registering & auto-configuring
If you want to know more, you can check our series on Symfony4 (the first course is totally free) https://knpuniversity.com/t...
Cheers!
i will for sure give it a read, i'm at the moment migrating from 2.8 to 3.4 so this is right up my alley right now, thx a lot!
Well, if your app has a lot of entity it would be a nightmare to have only one bundle, I am following what the guys at Sylius do with their Resource and Core Bundles.
That's a fine way to do it indeed - especially once you know what you're doing (but if beginners follow high-quality bundles like Sylius stuff, they may over-engineer things). You can also use sub-directories inside Entity in the one bundle if you like that better. Your call :).
Hi Ryan, thanks for this post.
I have a question for the right place of assets. In the Best Practices it says they should be in the web folder but not the sources like sass files. Where should I put files like these? Under app/Resources/public or in the src folder.
Hi Stefan!
Of course the answer is, it doesn't matter! Ok, but more usefully, I have created a structure like "assets/sass" before (at the root) and logically, "app/assets/sass" makes even more sense to me (though it's a little bit deeper to make your frontend developer dig, if you have one). For me, the "app/" directory is my "application", not src - so I'm putting more and more stuff there, and less and less in src/ (except PHP classes of course).
Cheers!
Hi Ryan. Great post. I'm wondering about the difference between point 7, and 8. I've always used one bundle for my applications, although I've worked on numerous apps where bundles were simply directories. Now I actually have a need for decoupled bundles and I like the idea of creating a bundle in the src/. I'm confused about "treat it like true, decoupled bundle". In addition, should I use the Symfony generator to create bundles or define the structure manually in accordance with the decoupled bundles best practices? What about the VendorBundleNameBundle.php?does it need anything special? Lastly, are there any tutorials or examples that you can recommend? Thanks.
Hi Dragan!
Ok, great questions! We don't (in the documentation) cover *how* to make decoupled, re-usable bundles very much, mostly because it affects a very small number of people. There is an article about the best practices, which isn't a "tutorial", but might help: http://symfony.com/doc/curr.... For your specific questions:
A) "treat it like a true, decoupled bundle" *basically* means that everything should live *inside* that bundle/directory. If you follow this completely, you'll naturally end up doing all the right things. Practically speaking, this means: put your templates in the bundle, put a routing file in your bundle and force your app to simply import it, add a DependencyInjection/**Extension class that automatically imports any service configuration (e.g. services.yml/xml) that your bundle has. Generally speaking, I add decoupled bundles in src/ first for simplicity, then move them to their own separate repository later. But you can do that in whatever order.
B) You *can* use the bundle generate if you want (it asks you if this is a reusable bundle, and makes different decisions). If you're not too comfortable with what a bundle needs/doesn't need, do this. But generally, your bundle doesn't *need* anything, besides the bundle class and - for a re-usable bundle - the DependencyInjection/**Extension setup, which I believe the generator will give you :)
C) Yes to using the vendor namespace! We use KnpU for our bundles (shorter than KnpUniversity) - using your GitHub name or some other handle (if this isn't for a specific company) is very common :).
D) And yes, following existing examples is the best way - and pretty much any open source bundle is a good example. Our KnpUOAuth2ClientBundle is a *small* example, which might be helpful: https://github.com/knpunive.... It shows off the most important "special" thing about re-usable bundles: the DependencyInjection/**Extension setup that loads a services.xml file: https://github.com/knpunive.... It also has a "functional" test - in case you want to test your standalone bundle: https://github.com/knpunive...
I hope that helps - good luck!
Thank you so much for your reply. I ended up following the current best practices, your knpu oauth bundle, and your tutorial on composer packages. You guys are awesome at KnpU. Keep up the excellent work!
Sweet post! I'm currently working for this Software company. The use of Bundles is now so common that we literally have people who can't work with raw codes. I recently wrote this article after they worked on a project in which they used Symfony2 FOSUserBundle.
Check it out: http://webmuch.com/override...
Hey Fabien!
More or less, yes :). Almost 4 years later, Symfony has gone from multiple bundles per app to 1 bundle to app (AppBundle) to *zero* bundles in Symfony 4. A lot of the arguments here are still relevant, and are the reasons why that change has been made over time.
Cheers!
Ryan, thanks for this great post! It goes straight to the point and provides solutions for every supposed problem created by AppBundle.
The conclusion is great: you can do this ... or the opposite ... or any other thing ... because this is Symfony and here the framework adapts to your needs and no the other way around!
Excellent post Ryan! As you know, I have been toying with going completely "bundle-less" lately. Admittedly, my motivation behind that is mostly "to see if I could do it". I agree that one "AppBundle" is the best solution (especially for new-comers) and am glad it is an official best practice.
One thing I have been doing lately is having my views in a "views" folder in the root of my project (instead of "app/Resources/views"). I feel views are first class citizens in my app. I find this also makes doing searches easier: to find something in code, I search "src/", in config/service definitions/translations/"meta", "app/", and in templates, "views/".
Hey Kevin!
You know, the location of the views folder is one of those silly, subjective things, but I also think it's a bit buried! One thing (in 3.0) that we could do it - at least - remove the app/Resources directory to simply app/, so we have app/views, app/translations, etc. Of course, you can already do this, but you get what I mean - recommending this approach is bigger. Moving it all the way to the top level is in many ways even more interesting. But I'm worried that we'll run into resistance due to multi-kernel projects. Then again, since those users are more advanced, you *could* start with a directory structure with everything in "app/" at the root, and *then* tell multiple-kernel people (it would be an easy doc) to create sub-directories for kernels and move those few things into it (views, config, the kernel class, etc).
Thoughts on that?
I would be in favor of moving the contents of `app/Resources` to just `app/`. For bundle overrides, I even think something like `app/bundles/TwigBundle/views/...` would be better. I can never remember if it is `app/Resources/TwigBundle/views` or `app/Resources/views/TwigBundle` or `app/Resources/TwigBundle/Resources/views`.
I feel the app folder is like the "metadata" for my app and I don't feel views fall into that category.
I thought multi-kernel projects were rare but after talking to some people in NY it is not as rare I thought... But, I wouldn't say it is common and it requires some advanced knowledge. What I don't know is if views are shared between kernels. If even one view is shared, it makes sense to move the views to the project root.
I went one step further. I wanted my controller code and templates to be in the same directory so I would not have to switch back and forth. So I have a master Action directory with sub-directories for each individual action. The sub-directories contain the action controller code, the templates as well as any other helper classes that belong to an individual action such as form types. Works for me.
"action controller code" - I'm curious how you have this. Is each action in a class by itself?
Yep. Not as bad as it may sound since many different routes end up using the exact same controller action. All done with services.
I tend not to use invoke mostly because I still have 5.3 servers and because I like to use meaningful names even if I only have one action method in a class.
Hey Iltar!
I'm so glad you asked about this :). The AppKernel has a build() method, which should work identically to the build method in your *Bundle class. So, you can register it right there :). The more you think about it, the more you realize that "app/" can do just about everything (only exception I know of right now is overriding bundle resource with parent/child relationship, which should still be done in small bundles for that purpose).
Cheers!
Namespacing to DependencyInjection still makes sense to me - I don't think that really changes :).
About addClassesToCompile, actually no! So that's a good catch we'll need to think about in the future. Philosophically, the method that's called to create the final dumped cache file is setClassCache on your kernel - so it really is related to the kernel. But, there's no decent hook right now to add anything directly - there's a compiler pass that collects all of them from the extensions and writes this.
Cheers!
Thanks for this post, at least now I get the motivation behind the one bundle approach, however I still disagree.
meandmymonkey kind of made my point for me, so I would only like to give you an example. About a year ago I inherited a project, which used the one bundle approach. It was a huge mess, no CS, no organization standards, etc. ThePreviousDevelopersEndedUpWithEntityNamesLikes this...
The OneBundle approach is okay, if you know what you are doing, and you are crystal clear on the reasons why are you doing it, but it should not be in a best practice guide, it's too controversial among experienced symfony devs, and a way too dangerous road to walk for new symfony developers.
Hey Adam!
I agree with some of your points here for sure. And yet, I still think that if we put everything into bundles, we end up with a more-easily-organized, but more complex project. I want things to be organized *and* have the "wins" of the AppBundle idea. I think there are a number of problems - like how to organize a single bundle / no bundle in a way that's just as nice as multiple bundles - to solve cleanly and publicize. Basically, I want us to "have our cake and eat it too". And afterall, the way we use bundles means that they really are basically just directories. We need to find the final directory structure that avoids the situation you're talking about. I think we need to be asking those questions right now (meandmymonkey has some good points on this) and getting a nice, final solution for 3.0. Right now, I will agree that we don't have a perfect solution (so the preference is subjective). I want to find the structure that's a win for everyone.
Thanks!
Hi Ryan,
great post, thanks!
Regarding code structure I often face the issue of how to share code between different applications of the same project. For example, imagine a project with a backend web application, a frontend web application, a command line application etc. All of them should be separate applications/repos so they can be deployed independently and e.g. the CLI does not have the overhead of the web application just to perform a CLI command. So it should be some kind of microservices architecture. But of course all of the applications need depend on the same model. If the frontend can generate some kind of sales lead with limited user data, the backend admin can verify, update and enrich this lead. Finally the CLI can also act on the leads somehow in the workflow. In Doctrine this would be represented by a "Lead" entity and it should be shared between all applications. What is the recommended way to do this? Creating a "CommonBundle" or "CommonLib" and share it via Composer? But then development becomes hard when synced changes to model and app need to be done or when there are a lot of developers on the same project. Is there any best practice for this scenario?
Hi Philipp!
I don't know if there's a best practice, but it's a *great* question and use-case. Heck, I would like to hear what other people think. I would say:
A) Separating between multiple repositories and sharing via Composer is probably not realistic for most people - it's just too difficult to manage. I do know one HUGE company that actually releases bundles/libraries internally like this, but they have an entire team that just works on those "internal open source" libraries.
B) For *how* to architect this, I see 3 options (and maybe you see some more):
1) One big project (and one kernel) - that has CLI stuff, backend stuff and frontend stuff. It's the most monolithic, but probably the easiest.
2) One big project, but multiple kernels. It's easy to manage like (1), but has some slightly less overhead because of the multiple kernels.
3) One big project (like 2), but separated even more into totally different directories. For example - backend/, which holds configuration, source code, web directory (e.g. backend/web) for everything in the "backend" app - then console/, which holds everything for your console app. For shared things, you could put them into a shared src/ directory. So, the root structure might be backend/, console/, frontend/ and src/. This is kind of a mixture of (2) and totally different projects.
4) Totally separate projects (microservices like you said). This is probably the best-practice, but the hardest to manage. I use this a bit with KnpU, but I'll admit I have problems sharing code between them (lack of time to properly isolate things into a Composer package).
I'd love to know your (or others') ideas on this :).
Cheers!
Hi Ryan, thanks for your suggestions and hey, it does not seem to be that easy ;-)
From the dev perspective, I would love to have a single project, but a) I need to do independant releases of all applications to different enviroments b) I only want to deploy what's actually needed, so the deployed CLI code should only be for the CLI.
I see there is some discussion going on on Twitter, thanks!
But I've also seen a project having the model stuff in a separate repo manged via Composer and yes, that's a big mess. Changes need to be done in 2 repos at the same time, other devs need to be informed, versioning becomes a pain etc.
Indeed - not so simple :). For others, here's the relevant conversation on Twitter: https://twitter.com/weaverr... - with Jon's tweet being particularly helpful (and summarizing what many people said): https://twitter.com/jwage/s....
In summary, option (3) seems to be what people favor, though some people recommend going all the way to (4) if you can manage it.
Cheers!
Option (3) sounds interesting. Some questions remain about how to deploy separately to different environments (depending on traffic and 3rd party software) but this should be solvable. And if possible, I'll also consider option (4) next time.
Thanks Ryan for promoting this discussion!
Thank you very much. I will show this as soon as possible to my team leader, who is obsessed over bundles, though his approach to architecture smells not good in my opinion :P
Anyway, to: "there's to technical benefit of having multiple bundles" - from my experience, I'd say, it depends: reusability is not the only reason for decoupling things. Maintanance in long turn, plus having a clear code design, are the viable reasons as well. When the code is not decoupled enough, it starts screaming for refactoring and making things apart; the code often starts to look bad and be too entangled, and a new developer would have a really hard time of figuring out how all this mess is connected. ;)
As you know, I beg to differ :) While you are perfectly right in every single one of your arguments concerning why you don't really need (a) traditional bundle(s) for a stand alone application, IMHO you are neglecting to point out any real benefits of getting rid of an application bundle.
Yes, an application is probably not reusable and does not need to be a bundle. But the only benefits mentioned for your approach are very, very minor (like shorter template names). For those small benefits, you are sacrificing one of the major strengths of Symfony: everything is structured the same, be it your code, 3rd party bundles, or the framework itself. And even those little syntactic advantages have drawbacks, see Doctrine shortcuts etc.
With the new approach, now we need to explain why the application code/resources "work" differently from bundles. We need to explain another directory structure. The smooth and simple to understand continuity between your own code, the framework, and supporting bundles is disrupted. This continuity is a major benefit for new developers to understand the framework itself its bundle infrastructure.
If Symfony 3.0 supports a monolithic app structure natively with all the bells and whistles, I'm all for it. But for Symfony 2.x, all of this feels quite forced and does not integrate very well. IMHO it actually creates an overhead and confusion.
tl;dr
I get your points, they are all valid on their own, but the only benefits I can see for developers are cosmetic, all the while sacrificing clarity and cleanliness.