Architecture Overview
dde follows a layered architecture with thin commands, manager-based orchestration, and dependency injection throughout. This document describes the namespace structure and design principles.
Namespace Overview
Section titled “Namespace Overview”Application (App\)
Section titled “Application (App\)”Application— extends Symfony Console Application. Defines the app version (APP_VERSION), filters visible commands toproject:*/system:*prefixes, adds the global--outputoption, and registers plugin commands viaPluginCommandLoader.Kernel— Symfony Kernel with PHAR-aware overrides forgetCacheDir()/getLogDir().
Commands (App\Command\)
Section titled “Commands (App\Command\)”Commands are the CLI entry points. They are thin wrappers that delegate to managers and services.
App\Command\Project\*— project-scoped commands (init, up, down, stop, shell, exec, logs, open, status, describe, update)App\Command\Project\Database\*— database commands (db, db:export, db:import, db:snapshot:*)App\Command\Project\Service\*— per-project service management (service:list, service:enable, service:disable)App\Command\System\*— system-wide commands (install, update, up, down, stop, restart, status, doctor, cleanup, service:up)App\Command\AboutCommand— version and system information
Base classes:
AbstractBaseCommand— root base class, providesFormatterResolverandresolveFormatter()AbstractProjectCommand— adds project directory detection, config resolutionAbstractDatabaseCommand— adds database adapter resolution and connection helpersAbstractSystemCommand— marker base class for system commands
All commands use the #[AsCommand] attribute for registration.
Managers (App\Manager\)
Section titled “Managers (App\Manager\)”Managers contain the core business logic and orchestrate complex operations.
| Manager | Responsibility |
|---|---|
ProjectLifecycleManager | Project up/down orchestration (services, certs, dev layers, overrides) |
SystemLifecycleManager | System up/down/stop/update orchestration (global services + versioned containers, image rebuild with --pull, post-install refresh for completion + claude-skill) |
ProjectInitManager | .dde/ directory structure creation during project:init |
ProjectInitAdaptationManager | Project adaptation logic during project:init (compose/env migration proposals; EnvMigrationProposal is its DTO) |
DockerComposeManager | Docker Compose CLI calls, runtime override generation |
DockerManager | Low-level Docker CLI (inspect, network, volume, exec, image operations) |
ImageManager | Image label inspection, dev layer build, cache invalidation |
GlobalConfigManager | Global ~/.dde/config.yml loading |
ProjectConfigManager | Project .dde/config.yml loading, merge with global config, project directory detection |
WorktreeManager | Git worktree detection, hostname resolution / rewriting (incl. subdomains), DB-name resolution, environment override computation (incl. env_file values) |
DatabaseManager | Database shell, export, import, snapshot management, port resolution |
SystemServiceManager | Versionable service container lifecycle (start, stop, status, port allocation) |
ServiceConfigManager | Service container configuration generation |
MkcertManager | mkcert CLI wrapper, cert generation, Traefik dynamic TLS config, CA root path resolution for container trust |
CompletionManager | Shell completion generation and installation |
CleanupManager | Container and volume cleanup |
ProjectInfoManager | Project info display |
ClaudeCodeManager | Detects a Claude Code installation and installs/refreshes the bundled skills/claude/dde skill |
Services (App\Service\)
Section titled “Services (App\Service\)”System services encapsulate the infrastructure containers managed by system:up/system:down. They implement ServiceInterface.
ServiceInterface— contract for system services, collected via container tagProjectNetworkAwareInterface— extendsServiceInterface; marks global services that must be attached to every per-project network (with their DNS aliases)AbstractSystemService— base class with start/stop/status logic viaDockerManagerTraefikService— reverse proxy (ports 80/443), network creation, Traefik config managementDnsmasqService— DNS resolver for the.testTLD, image build, resolver file managementSshAgentService— SSH agent socket sharing across containersMailpitService— mail testing serviceServiceRegistry— service type definitions, version defaults, port mappings, global service collectionImageBuilder— Docker image building for system servicesHostSshAgentResolver— resolves the host SSH agent socket forhostagent mode (macOS: always the Docker Desktop / OrbStack bridge; Linux:SSH_AUTH_SOCKor an explicit socket path, leading~expanded); single source of truth shared byDockerComposeManagerandSshAgentCheck
Configuration (App\Config\)
Section titled “Configuration (App\Config\)”GlobalConfig— DTO for~/.dde/config.yml(output format, DNS forward, SSH keys, service versions)ProjectConfig— DTO for.dde/config.yml(project name, services, containers)ResolvedConfig— merged configuration from global + project + defaultsWorktreeInfo— data class for git worktree metadata
Definition (App\Config\Definition\)
Section titled “Definition (App\Config\Definition\)”GlobalConfigDefinition— Symfony TreeBuilder schema for the global configProjectConfigDefinition— Symfony TreeBuilder schema for the project config
Models (App\Model\)
Section titled “Models (App\Model\)”ContainerConfig— Docker container creation parameters (image, ports, volumes, labels, etc.)ContainerInfo— running container metadata fromdocker inspectContainerStatus— container state representationServiceDefinition— service name, version, container name, portsServiceStartStatus/ServiceStatus— start outcome and running/stopped status of a service containerSystemLifecycleProgress— progress events emitted bySystemLifecycleManager, rendered live by thesystem:*commandsUserContext— host UID/GID for user mapping inside containersHostSshAgentResolution— result ofHostSshAgentResolver(available flag, mount source, reason)
Parsers (App\Parser\)
Section titled “Parsers (App\Parser\)”DockerComposeParser— reads and normalizes docker-compose.yml filesDockerfileParser— extracts information from Dockerfiles (base image, labels)
Adapters (App\Adapter\)
Section titled “Adapters (App\Adapter\)”AdapterRegistry— discovers and provides adapter scripts (built-in ones fromresources/adapters/, project-specific ones from.dde/adapters/). Handles PHAR extraction.
Database (App\Database\)
Section titled “Database (App\Database\)”DatabaseAdapterInterface— contract for database-specific operations (DSN generation, shell, export, import)MariaDbAdapter— MariaDB/MySQL implementationPostgresAdapter— PostgreSQL implementationDatabaseAdapterRegistry— maps service names to adapters
Doctor (App\Doctor\)
Section titled “Doctor (App\Doctor\)”CheckInterface— contract for health checks (tagged withdde.doctor_check)CheckResult— check outcome (name, status, message, fixHint)CheckStatus— enum:Ok,Warning,ErrorApp\Doctor\Check\*— 11 concrete check implementations
Events (App\Event\)
Section titled “Events (App\Event\)”AbstractProjectEvent— base class carrying the project directoryProjectUpPreEvent,ProjectUpPostEvent— dispatched before/after project:upProjectDownPreEvent,ProjectDownPostEvent— dispatched before/after project:down
Hooks (App\Hook\)
Section titled “Hooks (App\Hook\)”HookRunner— executes shell scripts from.dde/hooks/at lifecycle pointsHookSubscriber— event subscriber that triggersHookRunneron project events
Plugins (App\Plugin\)
Section titled “Plugins (App\Plugin\)”PluginLoader— scans~/.dde/plugins/(global) and.dde/plugins/(project) for annotated shell scriptsPluginDefinition— parsed plugin metadata (command name, description, script path)PluginProxyCommand— wraps a plugin script as a Symfony Console command, registered asproject:{name}PluginCommandLoader— lazy command loader that integrates plugins into the Symfony application
Output (App\Output\)
Section titled “Output (App\Output\)”OutputFormatterInterface— contract for output formatting (success, error, table, isInteractive)TextFormatter— human-readable console output with Symfony stylingJsonFormatter— structured JSON outputOutputFormat— enum of the supported--outputvaluesFormatterResolver— resolves and caches the active formatter
Event Listeners (App\EventListener\)
Section titled “Event Listeners (App\EventListener\)”OutputFormatListener— validates the--outputoption and configures the formatter on every commandSystemInstallCheckListener— warns when commands run beforesystem:installcompleted
Utilities (App\Util\)
Section titled “Utilities (App\Util\)”ComposeEnvEntryParser— composeenvironment:entry normalisationDockerComposeModifier— persistent modifications to a project’sdocker-compose.ymlduringproject:init: adds Traefik labels, injectsDATABASE_URL/MAILER_DSNfor detected dde services, migratesVIRTUAL_HOST/VIRTUAL_PORT(v1) to labels, and removes v1 boilerplate that the runtime overlay injects insteadDiffUtil— unified diffs for file comparisonsIdentifierSanitizer— slug sanitisation for hostnames and DB identifiersNdJsonParser— newline-delimited JSON parsing (Docker CLI output)PrivilegeEscalator— optimistic-then-sudo wrapper for host-level writes duringsystem:installProcessFactory—symfony/processfactory used by the managersShellDetectorUtil— detects the current shell (zsh, bash, etc.)TempFileUtil— temporary directories/filesTtyUtil— whether dde is attached to a terminal, i.e. whether a child process may take it overTraefikLabelGenerator— pure generation of the Traefik v3 label set for a hostname (incl. hostname allow-list againstHost()rule injection)UrlOpenerUtil— opens URLs in the default browser (cross-platform)
Exceptions (App\Exception\)
Section titled “Exceptions (App\Exception\)”HookFailedException— raised when a lifecycle hook script exits non-zero
Container Labels
Section titled “Container Labels”Every container created via DockerManager::run() (i.e. all dde-managed system containers — Traefik, dnsmasq, Mailpit, SSH-Agent, and the versioned service containers like dde-postgres-18.3) carries:
dde.managed=true— marker used byCleanupManageranddde system:down/cleanupto find every dde container, regardless of name.dde.service=<name>— service identity (traefik,mailpit,postgres, …).dde.version=<version>— only on versioned services fromSystemServiceManager.com.docker.compose.project=dde— groups dde-managed system containers under a singleddeproject in the Docker Desktop UI. dde does not actually use docker compose for these containers, but Docker Desktop only inspects this label for grouping.
Design Principles
Section titled “Design Principles”- Thin commands: Commands only handle CLI I/O. All logic lives in managers and services.
- Dependency injection: All classes use constructor injection with Symfony autowiring. No static methods or service locators.
#[AsCommand]registration: All commands use the attribute, no manual YAML configuration.symfony/processfor all external calls: Docker, git, mkcert, dig — all external tools are called viaProcess, and only from manager classes. Noshell_exec()orexec().- Strict types: Every file declares
strict_types=1. - Readonly where possible: Value objects and services use
readonlyproperties. - PHP enums for fixed sets:
CheckStatus,OutputFormat, etc. — no constant lists. - Explicit return types: No implicit returns, no mixed returns without reason.
- Not
finalby default: Only leaf classes that implement an interface or extend an abstract class arefinal; pure static utilities may befinal. - Single source of truth: Domain values (e.g. DB credentials) live in the class whose responsibility they are (
DatabaseAdapter), every other caller delegates to it. No hardcoded duplicates.