Symfony 4: How to organize folder structure (namely, your business logic)

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


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/


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']