ramblings on PHP, SQL, the web, politics, ultimate frisbee and what else is on in my life
back

Decoupled mindset

Back when PHP5 came along in many ways it meant a new stepping stone for PHP. To some extend this was caused by the fact that it was impossible to write E_STRICT compliant code that also worked on PHP4, since initially using "var" for properties didn't pass strict mode. As a result many people used this as an excuse to start over and I think overall this led to higher quality code, since the community had evolved from the skill set, but was held back by the legacy baggage. Then when PHP 5.3 came around with namespaces, it had a similar "cleansing" effect. So evolution was done partly by burning bridges. What is interesting is that with composer I see a process in the community of equal impact, yet instead of burning bridges, its building them.

As more and more projects adopt composer they will not only start using 3rd party code, they will also come to realize how easy it is for them to expose their code to 3rd parties. Obviously NIH syndrome will not be purged from the planet and maybe it should never get purged entirely anyway. But its already quite clear how much the landscape of the PHP community is changing with Symfony2, Doctrine, Zend Framework2, TYPO3 and many other projects having adopted composer.

While this is taking place I still see something odd and while I am going to explain this form the perspective of a Symfony2 user, I guess the same is true for other frameworks too: Higher level, framework dependent pieces of code are still being written in a coupled mindset. Just look on KnpBundles.com and see my many individual users but to some extend even worse groups of Bundles inside a single Github organization provide the same feature sets with more or less the same approach. Also several people are writing monolithic Bundles rather than feature focused solutions providing specific services or controllers.

Just yesterday I was chatting about shopping solutions for Symfony2. There are two well established projects with Vespolina and Sylius and again tons of others. Yet this developer was thinking of starting from scratch because there were certain things he didn't like about the existing solutions. But why this all or nothing mentality? Surely there must be something that works just fine. Why not simply ensure that those features are decoupled into libraries or separate Bundles instead, if they are not already? Then all time and resources can be directed at adding or reimplementing the things that are currently missing or that do not approach the problem in the preferred way?

Now there is one reason why one might prefer monolithic or tightly coupled Bundles and that is that Symfony2 currently doesn't make it all that easy to manage cross Bundle configuration. Settings need to be repeated, Bundles need to be made aware of other Bundles etc. I think this is an area where we still have to put in work now that we are seeing more and more applications build on top of Symfony2, like for example eZPublish 5. As a first step in this direction I have created a PR that would allow a Bundle to offer a way to preprocess the application configuration to more easily integrate decoupled Bundles while minimizing the manual configuration. I would love to see some feedback, but also some more exploration of how to improve things in this area.

Comments



Re: Decoupled mindset

The solution seems obvious to me.

Bundles should be composer packages as well. Symfony can then focus on composer package management instead of configuration multi-component rules.

I don't know Symfony, so if bundles already use composer, then nevermind.

Re: Decoupled mindset

Oh they are composer packages already. What I am talking about is configuration of decoupled Bundles.

For example lets say you have 5 Bundles that each need to have the entity manager name configured, multi-language mode enabled etc. Currently one would have to repeat that configuration for each Bundle. This gets tedious quickly and so people are tending towards just putting the 5 Bundles into one monolithic Bundle instead so that the configuration only needs to be done once.

Re: Decoupled mindset

interesting PR, is this how it is handled in zf2?
what the experience of the other bundles say, the core or most used?

I mean i think you have a better view of this in the cmf case which uses tons of bundles, including sonata and what not. Could you please illustrate this even more specifically?

Re: Decoupled mindset

I added some more explanations. Aside from this I am not sure what ZF2 does here, but from the examples I have seen, they do not provide any mechanism either yet.

Re: Decoupled mindset

Ok, I talked to Marco and it seems like ZF2 does not have this issue, because its does not separate the module configs like we separate the Bundle configs. This way one could simply register a "super module" before the other modules to pre-configure any of the following modules. The draw back is that its quite easy for one module to break another module. Then again in Symfony2 its also possible for one Bundle to overwrite parameters/services of another Bundle.

Re: Decoupled mindset

Yes, ZF2 does not have a per-module configuration. The configuration is loaded from modules and merged with what is already in memory.

This has the advantage that anything provided by previous modules does not need to be redefined, but introduces:

# collisions between configuration keys, leading to namespacing of config keys and thus bigger and uglier arrays
# relevance of module order, since the rule is "last module wins"

Consider that also service configuration comes from the merged "big" configuration, so the service locator actually does not need to do anything at all.

Re: Decoupled mindset

The way this has been handled historically in eZPublish (since you mention version 5, I thought I might as well give a bit of insight from our experience):
- config is taken from 3 sources: cms (defaults), bundles, and installation-specific
- anyone can override configs from everyone else - last config param loaded wins (arrays have some more magic, everyone can add elements to them but also reset them)
- this allows bundles to easily depend on each other and cooperate
- but it also makes config troubleshooting painful: since many files can change the same parameter, it is hard to find where the actual value is coming from (short of implementing a debugger telling you that)
- and of course, it causes param names to be a bit nastier - but I do not think that's a big problem
- to allow bundles not to step on each other, we had to implement bundle-dependency-specification, which then forces the app to load the bundles in a proper order

It sounds similar to what ZF2 does.

My main gripes with the eZP4 system is (apart from the braindead ini scanner) that it does not easily allow to
. define many apps with 90% common settings and 10% app-specific
. add to the mix per-environment and per-environment-per-app settings

Re: Decoupled mindset

I think keeping configuration seperate is a huge advantage also for testability etc.. Our ZF1 modules basically run standalone and that also allows me to test them one by one, vs. bootstrapping a giant application with dozens of objects and configuration which is not required in the scope of the module (or bundle).

I guess there could be a facility which provides defaults, but in general, I'm not sure how 'readable' that will turn out to be. E.g. added magic doesn't always ensure maintainable code.

I'd rather configure a database (or entitymanager) more than once and e.g. allow granular access to the database, vs. one database connection for everything. Also think of it in terms of 'security' — one compromised module of an application can basically access everything.

It all depends I guess. But I am happy to sacrifice some convenience.

Re: Decoupled mindset

Bundles were one of the big things that kept me away from Symfony2 because I did not like how many bundles had core domain logic locked inside of them.

In my opinion, the biggest offender is how Entities (and Documents) are automatically mapped and how hard it is to configure entities from external packages.

I'm actively working on trying to solve the problem of managing external ORM/ODM mappings and want to find a way to better present my case/solution.

Maybe some of this work could be useful in helping to decouple otherwise reusable code from Bundles and Modules and Service Providers?