33
votes

In the Symfony Best Practices is advised to not use bundles to organize business logic.

The bundles should be used only when the code in them is meant to be reused as-is in other applications:

But a bundle is meant to be something that can be reused as a stand-alone piece of software. If UserBundle cannot be used "as is" in other Symfony apps, then it shouldn't be its own bundle.

So, as I'm upgrading my app from Symfony 3.3 to Symfony 4, I think this is the right time to reorganize my code.

At the moment I have followed the "bundled-structure":

- src
   - AppBundle
      - Controller
      - Entity
      - Repository
      - Resources
      - ...
   - MyNamespace
      - Bundle
          - BundleTodo
              - Controller
              - Entity
              - Repository
              - Resources
              - ...
          - BundleCatalog
              - Controller
              - Entity
              - Repository
              - Resources
              - ...
          - BundleCart
              - Controller
              - Entity
              - Repository
              - Resources
              - ...
          - ...

Now, with the new directory structure, how should have I to organize my code?

I'd like to organize it this way:

-src
   - Core
      - Controller
      - Entity
      - Repository
      - ..
   - Todos
      - Controller
      - Entity
      - Repository
      - ..
   - Catalog
      - Controller
      - Entity
      - Repository
      - ..
   - Cart
      - Controller
      - Entity
      - Repository
      - ...

But, is this correct? Is there any problem with the expected folder structure of Symfony 4 and Flex?

Or is better something like this:

-src
   - Controller
       - Core
       - Todos
       - Catalog
       - Cart
       - ...
   - Entity
       - Core
       - Todos
       - Catalog
       - Cart
       - ...
   - Repository
       - Core
       - Todos
       - Catalog
       - Cart
       - ...
   - ...

The same applies also to other root folders as described in the project directory structure (about how to override it).

Are there any rules or constraints that I have to take into account deciding my new folder structure?

TRYING TO SOLVE THE PROBLEM

So, trying to solve the problem, I'm going deeper in the documentation and I will write here what I will find.


3
There is no right answer and this question is as old as the hills. Symfony is somewhat oriented towards the second approach i.e. out of the box all classes under the controllers directory are treated as controllers. Likewise anything under Entity can be treated as entities. But don't let frameworks dictate your application design. I'm more inclined towards the first approach though once you get into the details you might find yourself with plenty of other types of classes as well.Cerad
@Cerad, a right answer must exist. As you pointed out, Symfony is somewhat oriented toward the second approach, treating all classes in controller folder as controllers, entities in the entity folder, and so on. So, this is already a good point to use the second approach. The problem is deeper as how I described it: there are form types, serializer classes, and many more "things" that get a specific meaning if put in a folder instead of another.Aerendir
It is true that the framework should not dictate anything, but if I choose to use one, I have to follow what it tells me to do, instead I choose a different framework or simply don't use its "advanced" features (and I want to use them)...Aerendir
Yep. Life would be so much simpler if there was one and only one right answer for everything. The thing is that Symfony can support multiple approaches with very minor configuration changes. And the details are what makes thing messy. As you mentioned in your comment, forms, views, business logic etc have to be accounted for. I like your first approach except that I often have related "nested" features and I seldom make directories based on object types. Symfony handles this approach just fine.Cerad
"I seldom make directories based on object types": what do you mean with this phrase? Can you give an example? Anyway, I'm going deeper in the documentation and I'm finding some solutions... I'm going to update my question...Aerendir

3 Answers

15
votes

Conway's law:

organizations which design systems ... are constrained to produce designs which are copies of the communication structures of these organizations.

You should design your directory structure around how you organize work.

If you or your colleagues work full-stack on per feature basis you should group your code per feature. It will make navigation and code discovery easier.

If you or your colleagues are well specialized on back-end, front-end, translations etc. you should organize your code around that. Directory structure on per function basis will support clear split of responsibilities.

Also, the depth should depend on how big do you foresee a project to be. If it will be a 5+ years effort of 5+ people, you should probably go with splitting both on per feature and per function with nesting, as mentioned, depending on work organization. If it will be a 3 month project for one person i.e. some simple internal tool you should probably go with more flat structure. I would also recommend sticking to defaults.

Additionally, I found this article informative: https://blog.nikolaposa.in.rs/2017/01/16/on-structuring-php-projects/

13
votes

2nd structure is quite suitable for complex app, business areas splitted.
It is easy with Symfony 4 to configure its application in this way.

├─ assets/
├─ bin/
│  └─ console
├─ config/
│  ├─ doctrine/ 
│  │    ├─ core/
│  │    └─ sample/
│  ├─ packages/
│  ├─ routes/
│  └─ validator/
│  │    ├─ core/
│  │    └─ sample/
├─ public/
│  └─ index.php
├─ src/
│  ├─ Core/         
│  │  ├─ Controller/
│  │  ├─ Entity/
│  │  ├─ Repository/
│  │  └─ ...
│  ├─ Sample/      
│  └─ ...
├─ templates/
│  ├─ core/
│  └─ sample/
├─ tests/
├─ translations/
├─ var/
│  ├─ cache/
│  ├─ log/
│  └─ ...
└─ vendor/

With a little bit of configuration : service auto-wiring, auto-configuration etc... work like a charm.

# config/packages/doctrine.yaml
doctrine:
    # ...
    orm:
        # ...
        auto_mapping: true
        mappings:
            App\Core:
                is_bundle: false
                type: yml
                dir: '%kernel.project_dir%/config/doctrine/core'
                prefix: 'App\Core\Entity'
                alias: 'AppCore'


#config/routes/annotations.yaml
core_controllers:
    resource: ../../src/Core/Controller/
    type: annotation


# config/services.yaml
# But I prefer to put this on a separate config/services/_auto.yaml
services:
    App\:
        resource: '../../src/*/*'
        exclude: '../../src/*/{Entity,Migrations,Tests,Kernel.php}'

    app_controller:
        namespace: App\
        resource: '../../src/*/Controller'
        tags: ['controller.service_arguments']
10
votes

As stated in comments, Symfony can work well with all these structures, so indeed we cannot have an accepted anwser here, but here is my two cents.

To be honest, the best practices would be to organize architecture independently of the framework (it is mainly for this reason that Symfony 4 no longer imposes a bundle).

But in fact, except for really specific or complex projects, it will be more practical to have a "symfony-oriented" organization.

What follows is my personal preferences, and are also strongly influenced by the typology of my projects (CRUD oriented, Rest API, without strong business logic)

In general, I'm moving towards a structure like this:

-src
   - Controller
       - Core
       - Todos
       - ...
   - Entity
       - Core
       - Todos
       - ...
   - Repository
       - Core
       - Todos
   - Validator (or other Symfony oriented components)
       - Core
       - Todos
   - Others (depend on project)
       - Core
       - Todos
   - ...

The reasons are:

  • Less service definition with autowire - yeah, I'm lazy ;-)

    If you need to register your repositories or controllers as services, you can do it with one declaration.

  • In Symfony Flex recipes, it is usually this structure that is used.

    DoctrineBundle for example initialize src/Entity and src/Repository folders and recipes that contains entities also use this structure.

But keep in minde that Symfony Flex is not mandatory. Its purpose is mainly to ease the project's init and make the framework more accessible to beginer