---
title: "Apps (plugin system)"
description: "Plugin/integration ecosystem with per-user, per-organization, and per-resource installations."
type: "docs"
category: "doc"
tags: []
authors: [Anonymous]
date: "2026-09-22"
last_update: "2026-09-22"
time_minutes: 4
draft: false
unlisted: false
url: "https://www.derafu.dev/docs/symfony/platform-bundle/apps"
---

# Apps (plugin system)

The Apps module provides a plugin/integration ecosystem for your SaaS. Apps
are integrations or capabilities that users and organizations can enable —
payment gateways, messaging platforms, storage services, email providers, and
any custom capability your domain requires.

## Concepts

| Concept | Description |
|---|---|
| **App** | A registered integration (e.g. Stripe, Telegram, Dropbox). |
| **Scope** | Where an app can be installed: `user`, `organization`, or `resource`. |
| **Installation** | An app enabled for a specific user, org, or resource with config. |
| **Capability** | An interface an app can implement (e.g. payment, storage). |
| **Catalog override** | YAML configuration to adjust app metadata. |

## Defining an app

Create a service implementing `AppDefinitionInterface` (extend `AbstractApp`
for convenience):

```php
use Derafu\PlatformBundle\Apps\Abstract\AbstractApp;
use Derafu\PlatformBundle\Apps\Enum\AppScope;

final class StripeApp extends AbstractApp
{
    protected string $code = 'stripe';
    protected string $name = 'Stripe';
    protected string $description = 'Accept payments via Stripe.';
    protected string $icon = 'stripe';
    protected string $category = 'payments';

    protected array $scopes = [AppScope::Organization];

    // Keys to encrypt in the installation config
    protected array $sensitiveConfigKeys = ['secret_key', 'webhook_secret'];

    // Optional: a form key for the configuration form
    protected ?string $configFormKey = 'stripe_config';
}
```

The service is auto-discovered via the `AppDefinitionInterface`
`AutoconfigureTag`. No manual service registration needed.

## Scopes

An app can support one or more scopes:

| Scope | Entity | Use case |
|---|---|---|
| `AppScope::User` | `BaseUserAppInstallation` | Personal integration (e.g. Telegram notifications) |
| `AppScope::Organization` | `BaseOrganizationAppInstallation` | Shared integration for the whole org |
| `AppScope::Resource` | `BaseResourceAppInstallation` | Integration for a specific resource |

A multi-scope app (e.g. a storage app) can be installed at the user,
organization, and/or resource level simultaneously.

## Installing and managing apps

```php
use Derafu\PlatformBundle\Apps\Contract\Service\AppInstallationManagerInterface;

// Install for a user
$installation = $appInstallationManager->installForUser(
    appCode: 'stripe',
    user: $currentUser,
    config: ['public_key' => 'pk_live_...', 'secret_key' => 'sk_live_...']
);

// Install for an organization
$installation = $appInstallationManager->installForOrganization(
    appCode: 'stripe',
    organization: $org,
    config: ['public_key' => 'pk_live_...', 'secret_key' => 'sk_live_...']
);

// Check if installed
$installed = $appInstallationManager->isInstalledForOrganization('stripe', $org);

// Get installation
$installation = $appInstallationManager->getForOrganization('stripe', $org);

// Uninstall
$appInstallationManager->uninstallForOrganization('stripe', $org);
```

Sensitive config keys declared in `$sensitiveConfigKeys` are transparently
encrypted at rest by `AppConfigEncryptor`. Retrieval also decrypts transparently.

## The app registry

The `AppRegistryInterface` collects all registered apps. It applies catalog
overrides from configuration (scopes, flags, tags, supported resources):

```php
use Derafu\PlatformBundle\Apps\Contract\Service\AppRegistryInterface;

$app = $appRegistry->get('stripe');       // AppDefinitionInterface
$apps = $appRegistry->all();              // all registered apps
$apps = $appRegistry->byScope(AppScope::User); // filtered by scope
$apps = $appRegistry->byCategory('payments'); // filtered by category
```

## Catalog configuration

The YAML catalog section adjusts metadata of registered apps without modifying
the app class. Useful for overriding defaults per-deployment:

```yaml
derafu_platform:
    apps:
        catalog:
            stripe:
                scopes: [user, organization]
                flags: [featured]
            telegram:
                scopes: [user]
                flags: [featured]
            dropbox:
                scopes: [user, organization, resource]
                resources:
                    - App\Entity\Resource\Project
```

`flags` controls UI presentation (e.g. `featured` items appear prominently).
`resources` lists which FQCN resource classes the app supports when installed
at the resource scope.

## Capabilities

Apps can implement capability interfaces to advertise features. The
`CapabilityRegistryInterface` answers "which installed apps for this
organization support X?":

```php
use Derafu\PlatformBundle\Apps\Contract\Capability\PaymentGatewayInterface;

// Define a capability interface (app side)
interface PaymentGatewayInterface
{
    public function processPayment(int $amountCents, string $currency): string;
}

// An app implements it
final class StripeApp extends AbstractApp implements PaymentGatewayInterface
{
    public function processPayment(int $amountCents, string $currency): string
    {
        // Stripe API call...
    }
}

// Query at runtime
use Derafu\PlatformBundle\Apps\Contract\Service\CapabilityRegistryInterface;

$gateways = $capabilityRegistry->forOrganization(PaymentGatewayInterface::class, $org);
// Returns all installed apps for $org that implement PaymentGatewayInterface
```

## Resource apps

For apps that target a specific resource (e.g. "email provider for this Project"),
create a resource installation entity:

```php
use Derafu\PlatformBundle\Apps\Entity\BaseResourceAppInstallation;

#[ORM\Entity]
#[ORM\Table(name: 'project_app_installations')]
class ProjectAppInstallation extends BaseResourceAppInstallation
{
    #[ORM\ManyToOne(targetEntity: Project::class)]
    #[ORM\JoinColumn(nullable: false, onDelete: 'CASCADE')]
    private Project $project;

    public function getProject(): Project { return $this->project; }
    public function setProject(Project $project): static { $this->project = $project; return $this; }
}
```

## Settings UI

The Apps module registers a settings section automatically. Users and org
admins can browse, install, configure, and uninstall apps from a dedicated
settings page — no custom controllers needed.



---
Last updated on 22/09/2026

