Skip to content

Architecture backend

Manuel Meister edited this page Jan 2, 2023 · 1 revision

The API backend of eCamp v3 is based on API platform, which is the API solution of the Symfony PHP framework.

At its core, Symfony is an MVC framework. However, API platform builds on that and solves most repetitive coding tasks for us. We don't have to (and indeed shouldn't) write any custom controllers or actions. Instead, we simply create our entity classes (anything that is saved into the database, such as Users, Camps, Activities, etc.) and configure how these entities behave in the API via annotations / PHP 8 attributes. From there, API platform automatically generates the API and the Swagger API documentation.

For further customization, API platform allows us to change it's behaviour in various places. Here is a rough visualization of the pipeline that requests in API platform run through. All colored steps can be customized, but are already implemented in their basic form. API platform rough pipeline overview

Green: Security Rules (ACL). Are declared as PHP 8 attributes on the entity classes:

#[ApiResource(
    collectionOperations: [
        'get' => ['security' => 'is_fully_authenticated()'],
        'post' => [
            'security' => 'true', // allow unauthenticated clients to create (register) users
        ],
    ],
    itemOperations: [
        'get' => ['security' => 'is_granted("ROLE_ADMIN") or object == user'],
        'patch' => ['security' => 'is_granted("ROLE_ADMIN") or object == user'],
        'delete' => ['security' => 'is_granted("ROLE_ADMIN") and !object.ownsCamps()'],
    ]
)]
class User extends BaseEntity { // ...

Blue: Normalizers and denormalizers. Convert between entities and payload PHP arrays. The PHP arrays are still independent of the actual Format that the API returns (HAL JSON, JSON+LD, GraphQL, ...). The conversion from and to these final formats are done by separate, format-specific decoders and encoders, which are not shown on the diagram because most of the time, this does not need to be customized.

Yellow: InputFilters and validations. Input filters help us to e.g. remove whitespace from the beginning and end of fields, while validations allow us to check that a payload sent to the API makes sense from a business logic perspective. These are declared as PHP 8 attributes directly on the entities' properties. Validation rules are also automatically displayed in the swagger API docs.

    /**
     * Unique email of the user.
     *
     * @ORM\Column(type="string", length=64, nullable=false, unique=true)
     */
    #[InputFilter\Trim]
    #[Assert\NotBlank]
    #[Assert\Email]
    #[ApiProperty(example: 'bi-pi@example.com')]
    private ?string $email = null;

Purple: DataPersisters and DataProviders. These are responsible for storing and loading data into and from the database, or any other place such as a message queue or an external API. They are also useful places to put business logic that runs after validation, such as creating a material list for a new camp collaborator.

More information

For more information, see the documentation as well as the excellent SymfonyCasts about API platform. While not all SymfonyCasts videos are free to watch, the transcripts are all free to read. They are also sometimes a little outdated because API platform has been developed further, but most of the time there are hints in the transcript if that is the case. Also, we try to link to relevant documentation in the commit messages when we introduce new concepts.