Reusable code is easy with little effort with Symfony2. Symfony2 make it easy as long as you are making good use of the platform. The trick is planning ahead. In the future I’ll cover sharing your code easily. Either across projects or with the world using Composer. Composer isn’t perfect but it works well.
The Bundle concept works well as long as you are aggressive in investing in it. If you have a reusable piece of code (even potentially reusable code) it should be in its own Bundle.
I find a good rule of thumb is that any library, repository or service should be in its own Bundle. Ie, any code that represents behavior. Controllers handle your site-specific business logic, templates handle your display.
The backend logic, like talking to datastores (MySQL, Redis, PostgreSQL, Mongo, etc), API calls, etc should all be Bundles of code. That way you can reuse the logic on another site or in a different situation later.
Using an API call as an example:
- CurlBundle – wraps the built in Curl php code and adds convenience methods. It also adds Unit Tests, mocks, etc. to catch problematic change
APIBundle – uses the CurlBundle and handles common error conditions and return values, throws Exceptions for networking issues. Includes abstract domain objects and interfaces for XML and JSON handlers.
- XMLAPIBundle – handles the sending and receiving of XML data. Returns the domain objects and interfaces from the APIBundle layer
- JSONAPIBundle – performs the same functionality as the XMLAPIBundle but consumes and sends JSON.
- XYZAPIBundle – use a concrete API (like Google, Amazon, or internal).
- ABCAPIBundle – use a concrete API (like Google, Amazon, or internal).
You might say to yourself that that is a lot of Bundles just to do an API call but you are now in a position where you can create as many Consumers as you want and they all have a predictable interface (return the same types of objects, throw the same exceptions, etc). You can also add new API types like comma separated list, etc. You can also share them across projects. A couple of years from now, your small investment will mean a huge gain in productivity.
Using my rule of thumb means you need to justify not creating a new Bundle.
Of course, generally your Bundles should be self-sufficient in terms of domain objects/exceptions objects, etc. It should contain all the code to represent the behavior, return values and errors.
Whenever possible, your Bundles should expose its code via services that can be injected. Use Resources/config/services.xml (or whatever format you like, I prefer XML).
<service id="doman.curl" class="Domain\CurlBundle\Curl"> </service>
<service id="domain.api" class="Domain\APIBundle\API"> <argument type="service" id="doman.curl" /> </service>
<service id="domain.abcapi" class="Domain\ABCAPIBundle\API"> <argument type="service" id=" domain.api” /> <argument type="string">http://abc.com/api</argument> <argument type="string">ericpickup.com</argument> <argument type="string">supersecretpassword</argument> </service>
Your controllers would then call:
/** var \Domain\ABCAPIBundle\API $apiService */ $apiService = $this->get(‘domain.abcapi’);
Setting up the services might seem like a lot of work but if you even one realize that you need to change your dependencies, it will pay off. Plus it is invaluable for Unit Testing.
In the future, I’ll cover sharing your code via composer, auto-generating services from config (ie a new service per config entry, and maybe even Unit Testing these fake services.