Architecture Overview

Architecture Overview

Pattern: DDD Modular Monolith

  • Bounded contexts implemented as modules in a single deployable
  • Modules communicate via domain events (async) or contract interfaces (sync)
  • Deptrac enforces module boundaries at the code level
  • Designed for future extraction to microservices if needed

Layers (per module)

  1. Domain — Pure PHP. Aggregates, value objects, events, repository interfaces.
  2. Application — Command/query handlers. Orchestrates domain + infrastructure.
  3. Infrastructure — Doctrine repos, external services, framework integration.
  4. Presentation — Controllers, DTOs. Thin HTTP layer.
  5. Contract — Public interfaces for cross-module access. Pure PHP.

Key Architectural Decisions

  • ADR-001: Symfony 8 backend (stability, ecosystem, DDD-friendly)
  • ADR-002: React Native + Expo (single codebase, OTA updates)
  • ADR-003: Zitadel for auth (self-hosted OIDC, multi-tenant)
  • ADR-004: PostgreSQL + ltree (hierarchy queries, JSONB flexibility)
  • ADR-005: Redis + Symfony Messenger (async events, job queues)
  • ADR-006: DDD + Deptrac (enforced boundaries, clean architecture)
  • ADR-007: Mailgun for email (reliable, affordable)
  • ADR-008: Hetzner hosting (EU data, cost-effective)

See .ai/decisions/ for full ADR documents.

Anti-patterns (never do this)

  • Import another module’s Domain or Infrastructure classes
  • Put business logic in controllers
  • Use Doctrine annotations in the Domain layer
  • Create cross-module foreign keys
  • Accept tenant ID as a request parameter
  • Skip domain events for cross-module communication
  • Use mixed types or suppress PHPStan errors

Multi-tenancy

  • Tenant resolution from OIDC token (Zitadel organization)
  • Every database query scoped by tenant_id
  • Row-level security as defense-in-depth
  • No shared data between tenants (except platform config)

Pipeline (Cross-cutting)

  • Audit: logs who did what, when
  • History: tracks entity changes over time
  • Moderation: content review workflow
  • Translation: auto-translate content (future)
  • All pipeline modules consume domain events — they never modify source modules