33

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.


Aerendir
  • 5,365
  • 6
  • 45
  • 85
  • 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 Dec 01 '17 at 14:04
  • 1
    @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 Dec 01 '17 at 14:14
  • 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 Dec 01 '17 at 14:15
  • 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 Dec 01 '17 at 14:28
  • "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 Dec 01 '17 at 14:45
  • I seldom create folders called Controller or Form etc. Instead I have Feature1/Feature1Controller.php and Feature1/Feature1Form.php etc. The idea is that for a given feature there should only be a small number of related files. Good luck. – Cerad Dec 01 '17 at 14:55
  • Ok, I understand your point. I think I used a wrong nomenclature. By "feature" I mean something more than a simple feature. Think at it as, for example, a "TodoBundle" where there are many features all related to "todos". So, it is something bigger that what you intended (caused by my wrong explanation). – Aerendir Dec 01 '17 at 14:57
  • I've updated my question with more concrete namings to avoid any confusion about what I'm intending by "feature". – Aerendir Dec 01 '17 at 14:59
  • @Aerendir, same question for me, I will update symfony , and I want to simplify my old bundle structure. I will go for the third option, I prefer keep the Controller, Form, Entity ... folders to follow the symfony spirit, with the principal goal to help others to quickly understand the structure. Being to distant from the framework is a risk to complicating the handling of my work by the others dev who might join me. – Nicolas Feb 19 '18 at 07:34
  • another ressource: https://symfony.com/doc/current/setup/flex.html#upgrade-to-flex – Nicolas Feb 19 '18 at 09:55

3 Answers3

14

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/

Isinlor
  • 942
  • 1
  • 12
  • 19
13

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']
Benito103e
  • 320
  • 2
  • 11
  • Oh, yes, for sure: but which "little bit of configuration" is the tricky part! Without the specific configuration to configure (forgive me for the words joke) this answer is not useful! – Aerendir Aug 27 '18 at 10:05
  • 5
    @Aerendir you're right, I add the missing configuration. My proposition is not the best choice for small application, but I found it suitable for organizing larger ones. – Benito103e Aug 28 '18 at 12:34
  • 1
    I think this is the best way to be able to replicate code. The one that j-guyon has put is not so replicable because you have to navigate more directory and to copy from one project to another is more complicated. So thank you very much for this great answer. – jcarlosweb Sep 09 '20 at 00:54
10

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

BenMorel
  • 30,280
  • 40
  • 163
  • 285
j-guyon
  • 1,693
  • 16
  • 29
  • 4
    It's industry consensus that feature based structure keeps things developer need to change close to each other. Where as accepted structure keeps them apart in giant heaps of similarly named classes. Thus yellow citation is completely off the mark. – przemo_li May 17 '18 at 10:19