Layered Architecture – Skeleton Example

In the previous article we discussed the Layered Architecture. Now let’s make a skeleton application following this architecture. This skeleton can be used to kick-start your next project and be used to build on, to create the perfect architecture for your situation.

So first we install the Symfony skeleton.

composer create-project symfony/skeleton my-project

Then next we install annotations, Twig and Doctrine. We will use both in our skeleton application.

composer require annotations twig orm-pack

Now we have the default directory structure. As you can see below.

This is not yet a layered architecture. So the first thing we do is to change the directory structure according to layers.

This looks more like it. We have our Domain, Application, Infrastructure and Presentation layer.

But we need to change a few configurations to get this working.

Doctrine

In doctrine.yaml we need to change the mapping to our new Domain/Entity directory.

mappings:
    App\Domain:
        is_bundle: false
        type: annotation
        dir: '%kernel.project_dir%/src/Domain/Entity'
        prefix: 'App\Domain\Entity'
        alias: Domain

Because our migrations are now in the Infrastructure layer, we also need to change the doctrine_migrations.yaml file.

doctrine_migrations:
    dir_name: '%kernel.project_dir%/src/Infrastructure/Migrations'
    namespace: App\Infrastructure\Migrations

Now Doctrine is configured to work with our updated directory structure.

Presentation

For the presentation layer we will create an API directory and a Web directory. In most web applications that you build, you will probably need some web interface and maybe a Rest API.

Then on the web interface you might want to create a back office and a public front end. In this skeleton we will create a basic setup to handle these situations.

Note that inside our Backoffice and Pub directory we have Asset, Controller, Form and Twig.

Inside Asset we place our js and css files. The controller and form are our symfony controllers and forms. And in the Twig directory we will place our twig template files.

Before we can use this setup, we need to change some settings. Symfony needs to knows where to find the controllers and what their path is. In services.yaml we replace the default controller configuration with the following:

# controllers are imported separately to make sure services can be injected
# as action arguments even if you don't extend any base controller class
App\Presentation\Web\Pub\Controller\:
    resource: '../src/Presentation/Web/Pub/Controller'
    tags: ['controller.service_arguments']

App\Presentation\Web\Backoffice\Controller\:
    resource: '../src/Presentation/Web/Backoffice/Controller'
    tags: ['controller.service_arguments']

App\Presentation\Api\Rest\Controller\:
    resource: '../src/Presentation/Api/Rest/Controller'
    tags: ['controller.service_arguments']

# add more service definitions when explicit configuration is needed
# please note that last definitions always *replace* previous ones

Then we also need to change routes/annotations.yaml.

web_pub:
    resource: ../../src/Presentation/Web/Pub/Controller/
    type: annotation
web_backoffice:
    resource: ../../src/Presentation/Web/Backoffice/Controller/
    type: annotation
    prefix: /admin
api_rest:
    resource: ../../src/Presentation/Api/Rest/Controller/
    type: annotation
    prefix: /api

So our back office paths will be on /admin and our REST API endpoint is /api. Off course you can change this to your liking.

Twig Namespaces

You will have noticed that instead of using the default templates directory for twig. We have 2 separate Twig directory for the Back office and the Public web pages. We can do this by using Twig namespaces.

In twig.yaml you need to create the following configuration:

twig:
    paths:
      '%kernel.project_dir%/src/Presentation/Web/Backoffice/Twig': BackOffice
      '%kernel.project_dir%/src/Presentation/Web/Pub/Twig': Pub
    debug: '%kernel.debug%'
    strict_variables: '%kernel.debug%'

Now if we want to render a BackOffice template we need to use the following string to render it.

return $this->render('@BackOffice/dashboard/index.html.twig');

This will then render the file src/Presentation/Web/Backoffice/Twig/dashboard/index.html.twig.

Finished

So now we have build a basic skeleton for creating an layered application in Symfony. You can find the finished skeleton application here.