Files
calendars/docs/architecture.md
2026-01-11 02:28:04 +01:00

11 KiB

Calendar Application Architecture

Overview

The Calendar application is a modern, self-hosted calendar solution that combines a Django REST API backend with a separate CalDAV server for standards-compliant calendar data storage and synchronization. This architecture provides both a modern web interface and full CalDAV protocol support for compatibility with standard calendar clients.

System Architecture

┌─────────────────┐
│   Frontend      │
│   (Next.js)     │
└────────┬────────┘
         │
         │ HTTP/REST API + CalDAV Protocol
         │
┌────────▼─────────────────────────────────────┐
│         Django Backend                       │
│  ┌──────────────────────────────────────┐  │
│  │  REST API Endpoints                  │  │
│  │  - /api/v1.0/calendars               │  │
│  │  - /api/v1.0/users                   │  │
│  └──────────────────────────────────────┘  │
│  ┌──────────────────────────────────────┐  │
│  │  CalDAV Proxy                        │  │
│  │  - /api/v1.0/caldav/*               │  │
│  │  - /.well-known/caldav              │  │
│  └──────────────────────────────────────┘  │
│  ┌──────────────────────────────────────┐  │
│  │  Authentication (OIDC/Keycloak)      │  │
│  └──────────────────────────────────────┘  │
└────────┬───────────────────────────────────┘
         │
         │ HTTP/CalDAV Protocol
         │
┌────────▼─────────────────────────────────────┐
│         CalDAV Server                      │
│  (CalDAV Protocol Implementation)            │
│  - Calendar storage                          │
│  - Event storage (iCalendar format)          │
│  - CalDAV protocol handling                  │
└────────┬─────────────────────────────────────┘
         │
         │ PostgreSQL
         │
┌────────▼─────────────────────────────────────┐
│         PostgreSQL Database                  │
│  - Django models (users, calendars metadata) │
│  - CalDAV server schema (calendar data)      │
└──────────────────────────────────────────────┘

Component Responsibilities

Django Backend

The Django backend serves as the orchestration layer and business logic engine for the application.

Primary Responsibilities:

  • User Management & Authentication: OIDC authentication via Keycloak, user profiles, sessions, authorization
  • Calendar Metadata Management: Calendar creation/deletion, sharing, visibility settings, display preferences
  • REST API Layer: Modern RESTful API for the web frontend (JSON, standard HTTP methods, versioned at /api/v1.0/)
  • CalDAV Proxy: Proxies CalDAV requests to CalDAV server, handles authentication translation, URL routing, discovery endpoint
  • Business Logic: Calendar sharing logic, permission checks, data validation, integration coordination

Data Storage:

  • User accounts
  • Calendar metadata (name, color, visibility, owner)
  • Sharing relationships
  • Application configuration

Important: Django does NOT store actual calendar events. Events are stored in the CalDAV server.

CalDAV Server

The CalDAV server is a standards-compliant CalDAV server that handles all calendar data storage and protocol operations.

Primary Responsibilities:

  • Calendar Data Storage: Stores actual calendar events in iCalendar format, manages calendar collections
  • CalDAV Protocol Implementation: Full RFC 4791 implementation (PROPFIND, REPORT, MKCALENDAR, PUT, DELETE)
  • iCalendar Format Management: Parses and generates iCalendar files, validates syntax, handles VEVENT/VTODO components
  • Database Schema: Uses PostgreSQL with its own schema for calendar data

Authentication Integration:

  • Uses Apache authentication backend which reads REMOTE_USER environment variable
  • Django proxy sets X-Forwarded-User header, which the CalDAV server converts to REMOTE_USER
  • All communication is via HTTP - no direct database access from Django

Frontend (Next.js)

The frontend provides the user interface and interacts with both REST API and CalDAV protocol:

  • Modern React-based UI
  • Uses REST API for calendar metadata operations
  • Uses CalDAV protocol directly for event operations
  • Supports multiple languages and themes

Why This Architecture?

Design Decision: CalDAV Server Separation

The decision to use a separate CalDAV server rather than implementing CalDAV directly in Django was made for several reasons:

  1. Standards Compliance: Using a mature, well-tested CalDAV server that fully implements RFC 4791. Implementing CalDAV from scratch would be error-prone and time-consuming.

  2. Protocol Complexity: CalDAV is built on WebDAV, involving complex XML handling, property management, and collection hierarchies. A dedicated CalDAV server handles all of this complexity.

  3. Maintenance: Using a proven, maintained CalDAV server reduces maintenance burden and ensures compatibility with various CalDAV clients.

  4. Focus: Django backend can focus on business logic, user management, and REST API, while the CalDAV server handles calendar protocol operations.

  5. Shared database: The CalDAV server stores its data into Postgres, which we use in all LaSuite projects.

  6. Clean separation: All communication between Django and the CalDAV server is via HTTP, ensuring clean separation of concerns and no direct database access.

Benefits

  1. Standards Compliance

    • Full CalDAV protocol support enables compatibility with any CalDAV client (Apple Calendar, Thunderbird, etc.)
    • Users can sync calendars with external applications
    • Follows industry standards (RFC 4791)
  2. Separation of Concerns

    • Django handles business logic and user management
    • CalDAV server handles calendar protocol and data storage
    • Each component focuses on its core competency
  3. Flexibility

    • Can expose both REST API (for web app) and CalDAV (for external clients)
    • Different clients can use different protocols
    • Future-proof architecture
  4. Maintainability

    • Clear boundaries between components
    • Easier to test and debug
    • Can update components independently
  5. Performance

    • CalDAV server is optimized for CalDAV operations
    • Django can focus on application logic
    • Database can be optimized separately for each use case

Data Flow

Creating a Calendar

TODO: should this only be via caldav too?

  1. Frontend → POST /api/v1.0/calendars (REST API)
  2. Django Backend: Validates request, creates Calendar model, calls CalDAV server to create calendar collection
  3. CalDAV Server: Receives MKCALENDAR request, creates calendar collection, returns calendar path
  4. Django Backend: Stores CalDAV server path in Calendar.caldav_path, returns calendar data to frontend

Creating an Event

Events are created directly via CalDAV protocol:

  1. Frontend → PUT /api/v1.0/caldav/{user}/{calendar}/{event_uid}.ics (CalDAV)
  2. Django Backend: CalDAVProxyView authenticates user, forwards request to CalDAV server with authentication headers
  3. CalDAV Server: Receives PUT request with iCalendar data, stores event in calendar collection
  4. Django Backend: Forwards CalDAV response to frontend

CalDAV Client Access

  1. CalDAV Client → PROPFIND /api/v1.0/caldav/ (CalDAV protocol)
  2. Django Backend: Authenticates user via Django session, forwards request to CalDAV server with X-Forwarded-User header
  3. CalDAV Server: Processes CalDAV request, returns CalDAV response
  4. Django Backend: Forwards response to client

Integration Points

User Synchronization

Users are automatically created in the CalDAV server when they first access it. The CalDAV server's Apache authentication backend reads the REMOTE_USER environment variable, which is set from the X-Forwarded-User header sent by Django. No explicit user creation is needed - the CalDAV server will create principals on-demand.

Calendar Creation

When creating a calendar via REST API:

  1. Django creates Calendar model with metadata
  2. Django calls CalDAV server via HTTP to create calendar collection
  3. Django stores CalDAV server path in Calendar.caldav_path

Authentication Translation

Django sessions are translated to CalDAV server authentication:

  • Django adds X-Forwarded-User header with user email
  • CalDAV server converts X-Forwarded-User to REMOTE_USER environment variable
  • CalDAV server's Apache authentication backend reads REMOTE_USER for authentication
  • All communication is via HTTP - no direct database access

URL Routing

CalDAV clients expect specific URL patterns. The CalDAV proxy handles path translation:

  • Discovery endpoint at .well-known/caldav redirects to /api/v1.0/caldav/
  • Proxy forwards requests to CalDAV server with correct paths

Database Schema

Both Django and the CalDAV server use the same PostgreSQL database in a local Docker install, but maintain separate schemas:

Django Schema (public schema):

  • calendars_user - User accounts
  • caldav_calendar - Calendar metadata
  • caldav_calendarshare - Sharing relationships
  • Other Django app tables

CalDAV Server Schema (public schema, same database):

  • users - CalDAV server user records (for digest auth, not used with Apache auth)
  • principals - CalDAV server principals
  • calendars - Calendar collections
  • calendarinstances - Calendar instance metadata
  • calendarobjects - Calendar resources (events)
  • calendarchanges - Change tracking
  • Other CalDAV server-specific tables

This allows them to share the database locally while keeping data organized. Note that Django never directly accesses CalDAV server tables - all communication is via HTTP.