Skip to main content

Calendar Event Scheduler Backend System Design

Comprehensive low-level design for a scalable calendar event scheduling system with conflict detection and notification handling.

This document presents a robust backend architecture for managing calendar events with intelligent scheduling, conflict resolution, and real-time notification capabilities across distributed systems.

1. Problem Statement & Requirements

Clarifying questions

  1. What is the scale of the system? Are we targeting millions of users with potentially thousands of concurrent events, or is this a smaller-scale internal tool for a few hundred users?
  2. What consistency requirements are there? Should we guarantee strong consistency for event scheduling, or can eventual consistency be acceptable (e.g., for non-critical overlapping events)?
  3. What are the key entities and their relationships? Is this a simple calendar event system, or does it include recurring events, attendees, reminders, and resource booking?
  4. What is the read/write ratio? Is this primarily a read-heavy system (e.g., calendar views) or more write-heavy (e.g., frequent event creation)?
  5. What are the latency sensitivity requirements? Should event creation and retrieval be sub-100ms for real-time scheduling, or is a few hundred milliseconds acceptable?
  6. What authentication model should we support? Should it support OAuth2, JWT tokens, or API keys for third-party integrations?
  7. Are there external system integrations? Should it integrate with Google Calendar, Outlook, or other calendar systems?

Functional requirements

  • Users can create, read, update, and delete calendar events
  • Events can have start time, end time, title, description, location, and attendees
  • Support for recurring events (daily, weekly, monthly)
  • Event reminders (email, push notifications)
  • Resource booking (e.g., meeting rooms, equipment)
  • Calendar sharing and permissions
  • Conflict detection and resolution for overlapping events

Non-functional requirements

  • Latency targets: p50 < 50ms, p95 < 100ms, p99 < 200ms for event creation and retrieval
  • Throughput: Support 10,000 concurrent users with 500 events/sec peak
  • Availability: 99.9% uptime (28.8 minutes of downtime/month)
  • Scalability: Horizontally scalable to support millions of events
  • Data durability: Events must survive system failures with no data loss

Constraints and assumptions

  • Assumption: Users are authenticated and authorized to access their events
  • Constraint: Events must not overlap for the same user or resource within a specified time window
  • Assumption: Event data is stored in a relational database with ACID properties
  • Constraint: Calendar events are typically accessed within 24 hours of creation
  • Assumption: Users can have multiple calendars (personal, work, shared)

Out of scope

  • Integration with external calendar systems (Google Calendar, Outlook)
  • Real-time collaborative editing of events
  • Advanced scheduling algorithms (e.g., AI-based time optimization)
  • Complex recurrence patterns beyond basic daily/weekly/monthly
  • Internationalization and timezone handling beyond UTC
  • Calendar event import/export functionality

2. API Design

API Style Selection

gRPC with Protocol Buffers is selected for the Calendar Event Scheduler due to:

  • Strong typing and schema evolution support
  • Efficient binary serialization reducing payload size
  • Built-in support for streaming and bidirectional communication
  • Native support for versioning through proto package namespaces
  • Better performance characteristics for complex nested data structures

Endpoint Definitions

MethodPathDescriptionAuth
CreateEventPOST /v1/eventsCreates a new calendar eventJWT
GetEventGET /v1/events/{id}Retrieves a specific event by IDJWT
UpdateEventPUT /v1/events/{id}Updates an existing eventJWT
DeleteEventDELETE /v1/events/{id}Deletes an event by IDJWT
ListEventsGET /v1/eventsLists events within a date rangeJWT
AttendEventPOST /v1/events/{id}/attendeesAdds an attendee to an eventJWT
CancelEventPOST /v1/events/{id}/cancelCancels an eventJWT

Request/Response Schemas

// Event message definition
message Event {
  string id = 1;
  string title = 2;
  string description = 3;
  google.protobuf.Timestamp start_time = 4;
  google.protobuf.Timestamp end_time = 5;
  string location = 6;
  repeated string attendees = 7;
  string creator_id = 8;
  bool is_recurring = 9;
  google.protobuf.Timestamp created_at = 10;
  google.protobuf.Timestamp updated_at = 11;
  EventStatus status = 12;
}

// CreateEventRequest
message CreateEventRequest {
  string title = 1;
  string description = 2;
  google.protobuf.Timestamp start_time = 3;
  google.protobuf.Timestamp end_time = 4;
  string location = 5;
  repeated string attendees = 6;
  bool is_recurring = 7;
}

// CreateEventResponse
message CreateEventResponse {
  Event event = 1;
}

// ListEventsRequest
message ListEventsRequest {
  google.protobuf.Timestamp start_time = 1;
  google.protobuf.Timestamp end_time = 2;
  string user_id = 3;
  int32 page_size = 4;
  string page_token = 5;
}

// ListEventsResponse
message ListEventsResponse {
  repeated Event events = 1;
  string next_page_token = 2;
}

HTTP Status Codes and Error Format

Standard HTTP Status Codes:

  • 200 OK - Successful GET, PUT, PATCH
  • 201 Created - Successful POST
  • 400 Bad Request - Invalid input, validation errors
  • 401 Unauthorized - Missing or invalid auth token
  • 403 Forbidden - Insufficient permissions
  • 404 Not Found - Resource not found
  • 409 Conflict - Event conflict (overlapping time)
  • 422 Unprocessable Entity - Business logic validation errors
  • 500 Internal Server Error - Unexpected server error

Standardized Error Response Format:

{
  "error": {
    "code": "EVENT_CONFLICT",
    "message": "Event overlaps with existing event",
    "details": {
      "existing_event_id": "1234567890",
      "conflict_start_time": "2023-10-15T14:00:00Z",
      "conflict_end_time": "2023-10-15T15:00:00Z"
    },
    "timestamp": "2023-10-15T10:00:00Z"
  }
}

API Versioning Strategy

Package-based versioning in Protobuf:

  • calendar.v1 - Current stable version
  • calendar.v2 - Future major version with breaking changes

Versioning handled through:

  1. Package namespace in proto files (package calendar.v1)
  2. gRPC service versioning via package structure
  3. Client-side version detection and fallback

Pagination Design

Cursor-based pagination is selected because:

  • More efficient than offset-based for large datasets
  • Consistent results even when data changes during pagination
  • Better performance with large offsets
{
  "events": [
    {
      "id": "event1",
      "title": "Team Meeting"
    },
    {
      "id": "event2", 
      "title": "Lunch with Client"
    }
  ],
  "next_page_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoiYWRtaW4iLCJ0aW1lc3RhbXAiOjE2OTc1MzYwMDB9.7pX5hQdV2v3nJ5aK9x8zI4u7yT1w6Z2p3Qv5k9L8m"
}

Authentication and Authorization

Authentication:

  • JWT tokens with HS256 signing
  • Token expiration: 24 hours
  • Refresh token support (30 days)

Authorization:

  • JWT claims validation for user ID
  • RBAC roles: USER, ADMIN
  • Event creator can modify/delete own events
  • Admin can access all events

Rate Limiting Strategy

Per endpoint rate limits:

EndpointLimitPeriod
CreateEvent100/hourHour
ListEvents500/hourHour
GetEvent1000/hourHour

Implementation:

  • Token bucket algorithm with Redis for distributed rate limiting
  • Per-user rate limiting using user_id as key
  • Burst support (max 10 requests per second with 50 burst allowance)

Idempotency Design

Idempotency Key Strategy:

  • X-Idempotency-Key header for all mutating operations
  • Store operation metadata in Redis with TTL of 24 hours
  • For duplicate requests, return stored response instead of reprocessing
// Example idempotency check in service layer
public CreateEventResponse createEvent(CreateEventRequest request, String idempotencyKey) {
    String cacheKey = "idempotency:" + idempotencyKey;
    
    // Check if this operation was already processed
    String cachedResponse = redisTemplate.opsForValue().get(cacheKey);
    if (cachedResponse != null) {
        return parseCachedResponse(cachedResponse);
    }
    
    // Process the operation
    CreateEventResponse response = processCreateEvent(request);
    
    // Cache the result for 24 hours
    redisTemplate.opsForValue().set(cacheKey, serializeResponse(response), 24, TimeUnit.HOURS);
    
    return response;
}

3. Data Model Design

Entity-Relationship Diagram

erDiagram
    USER {
        string id PK
        string email
        string name
        timestamp created_at
        timestamp updated_at
    }

    CALENDAR {
        string id PK
        string user_id FK
        string name
        boolean is_primary
        timestamp created_at
        timestamp updated_at
    }

    EVENT {
        string id PK
        string calendar_id FK
        string title
        string description
        timestamp start_time
        timestamp end_time
        string timezone
        string location
        string status
        boolean is_recurring
        string recurrence_rule
        timestamp created_at
        timestamp updated_at
    }

    EVENT_ATTENDEE {
        string id PK
        string event_id FK
        string user_id FK
        string status
        timestamp created_at
    }

    EVENT_NOTIFICATION {
        string id PK
        string event_id FK
        string user_id FK
        timestamp notification_time
        string type
        boolean is_sent
        timestamp sent_at
        timestamp created_at
    }

    USER -> CALENDAR : owns
    CALENDAR -> EVENT : contains
    EVENT -> EVENT_ATTENDEE : has
    EVENT -> EVENT_NOTIFICATION : has
    USER -> EVENT_ATTENDEE : is
    USER -> EVENT_NOTIFICATION : receives

Table Schema Definitions

-- Users table
CREATE TABLE users (
    id UUID PRIMARY KEY,
    email VARCHAR(255) UNIQUE NOT NULL,
    name VARCHAR(255),
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- Calendars table
CREATE TABLE calendars (
    id UUID PRIMARY KEY,
    user_id UUID REFERENCES users(id) ON DELETE CASCADE,
    name VARCHAR(255) NOT NULL,
    is_primary BOOLEAN DEFAULT FALSE,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    INDEX idx_user_id (user_id),
    INDEX idx_primary (is_primary)
);

-- Events table
CREATE TABLE events (
    id UUID PRIMARY KEY,
    calendar_id UUID REFERENCES calendars(id) ON DELETE CASCADE,
    title VARCHAR(255) NOT NULL,
    description TEXT,
    start_time TIMESTAMP NOT NULL,
    end_time TIMESTAMP NOT NULL,
    timezone VARCHAR(50) NOT NULL,
    location VARCHAR(255),
    status VARCHAR(50) DEFAULT 'scheduled',
    is_recurring BOOLEAN DEFAULT FALSE,
    recurrence_rule TEXT,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    INDEX idx_calendar_id (calendar_id),
    INDEX idx_start_time (start_time),
    INDEX idx_end_time (end_time),
    INDEX idx_user_time (calendar_id, start_time, end_time),
    INDEX idx_recurring (is_recurring),
    CHECK (end_time > start_time)
);

-- Event attendees table
CREATE TABLE event_attendees (
    id UUID PRIMARY KEY,
    event_id UUID REFERENCES events(id) ON DELETE CASCADE,
    user_id UUID REFERENCES users(id) ON DELETE CASCADE,
    status VARCHAR(50) DEFAULT 'pending',
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    INDEX idx_event_id (event_id),
    INDEX idx_user_id (user_id),
    UNIQUE KEY uk_event_user (event_id, user_id)
);

-- Event notifications table
CREATE TABLE event_notifications (
    id UUID PRIMARY KEY,
    event_id UUID REFERENCES events(id) ON DELETE CASCADE,
    user_id UUID REFERENCES users(id) ON DELETE CASCADE,
    notification_time TIMESTAMP NOT NULL,
    type VARCHAR(50) NOT NULL,
    is_sent BOOLEAN DEFAULT FALSE,
    sent_at TIMESTAMP,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    INDEX idx_event_id (event_id),
    INDEX idx_user_id (user_id),
    INDEX idx_notification_time (notification_time)
);

Primary Key Strategy

UUIDs are used for all primary keys to support:

  • Distributed system scalability (no coordination needed for ID generation)
  • Data sharding strategies based on UUID ranges
  • Audit trail and replication consistency
  • Prevention of ID collisions in multi-region deployments

Index Design

Index NameColumnsTypePurpose
idx_user_iduser_idStandardFast user lookups
idx_primaryis_primaryStandardFind primary calendar
idx_calendar_idcalendar_idStandardFast event lookups by calendar
idx_start_timestart_timeStandardFast time-based queries
idx_end_timeend_timeStandardFast time-based queries
idx_user_timecalendar_id, start_time, end_timeCompositeEfficient time-range queries
idx_recurringis_recurringStandardFast recurring event filtering
idx_event_idevent_idStandardFast attendee and notification lookups
idx_notification_timenotification_timeStandardEfficient notification scheduling

Data Partitioning / Sharding Strategy

Time-based sharding on start_time:

  • Events are partitioned by month/year ranges
  • Each partition contains events within a specific time window (e.g., 2024-01, 2024-02)
  • This enables efficient time-range queries and reduces index size
  • Future support for geographic partitioning can be added

Soft Delete vs. Hard Delete Decision

Soft delete is used for:

  • Events and calendars to maintain referential integrity
  • Audit trail preservation for compliance and analytics
  • Ability to restore deleted items if needed

Audit Trail Design

All tables include created_at and updated_at timestamps. Additionally:

-- Audit log table for tracking changes
CREATE TABLE audit_logs (
    id UUID PRIMARY KEY,
    user_id UUID REFERENCES users(id),
    operation VARCHAR(50) NOT NULL, -- CREATE, UPDATE, DELETE
    table_name VARCHAR(100) NOT NULL,
    record_id UUID NOT NULL,
    old_values JSONB,
    new_values JSONB,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

Data Archival Strategy and Retention Policy

  • Events older than 2 years are archived to cold storage (e.g., S3)
  • Archive retention: 5 years for compliance requirements
  • Archival process runs nightly to move old events to archive storage
  • Metadata is retained in database for querying while data is moved to cheaper storage

4. Class Design & Service Layer

classDiagram
    class CalendarEvent {
        -UUID id
        -String title
        -String description
        -LocalDateTime startTime
        -LocalDateTime endTime
        -String location
        -UUID creatorId
        -EventStatus status
        -List~Attendee~ attendees
        -CalendarEventRecurrence recurrence
        -Map~String, String~ metadata
        +create()
        +update()
        +cancel()
        +addAttendee()
        +removeAttendee()
    }

    class Attendee {
        -UUID id
        -String email
        -String name
        -AttendeeStatus status
        -LocalDateTime responseTime
    }

    class CalendarEventRecurrence {
        -RecurrencePattern pattern
        -LocalDateTime endDate
        -Integer occurrenceCount
    }

    class CalendarEventService {
        +createEvent()
        +updateEvent()
        +cancelEvent()
        +getEventById()
        +getEventsByTimeRange()
        +addAttendee()
        +removeAttendee()
        +findConflictingEvents()
        +sendNotifications()
    }

    class EventValidator {
        +validateEventCreation()
        +validateEventUpdate()
        +validateAttendeeAddition()
    }

    class NotificationService {
        +sendEventCreatedNotification()
        +sendEventUpdatedNotification()
        +sendAttendeeInvitation()
        +sendAttendeeResponseNotification()
    }

    class CalendarEventRepository {
        +save()
        +findById()
        +findByTimeRange()
        +findConflictingEvents()
        +update()
        +delete()
    }

    class EventScheduler {
        +scheduleEvent()
        +rescheduleEvent()
        +cancelScheduledEvent()
    }

    class EventState {
        -String state
        -LocalDateTime transitionTime
        -String transitionReason
    }

    class EventStatus {
        CREATED
        CONFIRMED
        CANCELLED
        COMPLETED
    }

    class AttendeeStatus {
        PENDING
        ACCEPTED
        DECLINED
        TENTATIVE
    }

    CalendarEvent --> Attendee : has many
    CalendarEvent --> CalendarEventRecurrence : has one
    CalendarEvent --> EventState : has one
    CalendarEventService --> CalendarEventRepository : uses
    CalendarEventService --> EventValidator : uses
    CalendarEventService --> NotificationService : uses
    CalendarEventService --> EventScheduler : uses
    EventValidator --> CalendarEvent : validates
    NotificationService --> CalendarEvent : sends notifications for
    EventScheduler --> CalendarEvent : schedules
stateDiagram-v2
    [*] --> CREATED
    CREATED --> CONFIRMED : event confirmed
    CREATED --> CANCELLED : event cancelled
    CONFIRMED --> COMPLETED : event completed
    CONFIRMED --> CANCELLED : event cancelled
    CANCELLED --> [*]
    COMPLETED --> [*]

Service Class Structure and Responsibilities

The CalendarEventService is the core service responsible for managing calendar events. It handles event creation, updates, cancellations, and attendee management.

Key responsibilities:

  1. Event lifecycle management (creation, confirmation, cancellation, completion)
  2. Attendee management and notifications
  3. Conflict detection and resolution
  4. Event scheduling and rescheduling
  5. Validation of business rules

Interface / Abstract Class Design

public interface CalendarEventRepository {
    CalendarEvent save(CalendarEvent event);
    Optional<CalendarEvent> findById(UUID id);
    List<CalendarEvent> findByTimeRange(LocalDateTime start, LocalDateTime end);
    List<CalendarEvent> findConflictingEvents(UUID creatorId, LocalDateTime start, LocalDateTime end);
    CalendarEvent update(CalendarEvent event);
    void delete(UUID id);
}

public interface EventValidator {
    ValidationResult validateEventCreation(CalendarEvent event);
    ValidationResult validateEventUpdate(UUID eventId, CalendarEvent updatedEvent);
    ValidationResult validateAttendeeAddition(UUID eventId, Attendee attendee);
}

public interface NotificationService {
    void sendEventCreatedNotification(CalendarEvent event);
    void sendEventUpdatedNotification(CalendarEvent event);
    void sendAttendeeInvitation(Attendee attendee, CalendarEvent event);
    void sendAttendeeResponseNotification(Attendee attendee, CalendarEvent event);
}

Business Logic Encapsulation Patterns

Strategy Pattern for Validation

public interface ValidationStrategy {
    ValidationResult validate(CalendarEvent event);
}

@Component
public class EventCreationValidationStrategy implements ValidationStrategy {
    @Override
    public ValidationResult validate(CalendarEvent event) {
        if (event.getStartTime() == null || event.getEndTime() == null) {
            return ValidationResult.error("Start time and end time are required");
        }
        
        if (event.getStartTime().isAfter(event.getEndTime())) {
            return ValidationResult.error("Start time must be before end time");
        }
        
        if (event.getStartTime().isBefore(Instant.now().atZone(ZoneId.systemDefault()).toLocalDateTime())) {
            return ValidationResult.error("Event cannot start in the past");
        }
        
        return ValidationResult.success();
    }
}

@Component
public class EventUpdateValidationStrategy implements ValidationStrategy {
    @Override
    public ValidationResult validate(CalendarEvent event) {
        // Implementation for update validation logic
        return ValidationResult.success();
    }
}

Critical-Path Method Implementation

@Transactional
public CalendarEvent createEvent(CalendarEvent event) {
    // Validate event creation
    ValidationResult validation = eventValidator.validateEventCreation(event);
    if (!validation.isSuccess()) {
        throw new ValidationException(validation.getErrorMessage());
    }

    // Check for conflicts
    List<CalendarEvent> conflictingEvents = calendarEventRepository.findConflictingEvents(
        event.getCreatorId(), 
        event.getStartTime(), 
        event.getEndTime()
    );
    
    if (!conflictingEvents.isEmpty()) {
        throw new ConflictException("Event conflicts with existing events");
    }

    // Set initial state
    event.setStatus(EventStatus.CREATED);
    
    // Save the event
    CalendarEvent savedEvent = calendarEventRepository.save(event);
    
    // Send notifications to creator and attendees
    notificationService.sendEventCreatedNotification(savedEvent);
    
    // Schedule any recurring events or reminders
    eventScheduler.scheduleEvent(savedEvent);
    
    return savedEvent;
}

Transaction Boundary Design

Single transaction boundaries for operations:

  1. Create Event: Entire operation within one transaction
  2. Update Event: Entire update within one transaction
  3. Add Attendee: Transaction includes validation, attendee addition, and notification
  4. Cancel Event: Transaction includes status update and notification

Service Interface Contracts

public interface CalendarEventService {
    CalendarEvent createEvent(CalendarEvent event);
    CalendarEvent updateEvent(UUID eventId, CalendarEvent updatedEvent);
    void cancelEvent(UUID eventId);
    CalendarEvent getEventById(UUID id);
    List<CalendarEvent> getEventsByTimeRange(LocalDateTime start, LocalDateTime end);
    Attendee addAttendee(UUID eventId, Attendee attendee);
    void removeAttendee(UUID eventId, UUID attendeeId);
    List<CalendarEvent> findConflictingEvents(UUID creatorId, LocalDateTime start, LocalDateTime end);
}

Saga / Orchestration Pattern

For complex operations like creating recurring events with attendees, we implement a saga pattern:

public class EventCreationSaga {
    private final List<SagaStep> steps = new ArrayList<>();
    
    public void execute() {
        // Step 1: Create event
        steps.add(new CreateEventStep());
        
        // Step 2: Add attendees (if any)
        steps.add(new AddAttendeesStep());
        
        // Step 3: Schedule notifications
        steps.add(new ScheduleNotificationsStep());
        
        // Execute all steps in order
        for (SagaStep step : steps) {
            try {
                step.execute();
            } catch (Exception e) {
                // Compensate previous steps
                compensateSteps(steps.indexOf(step));
                throw new SagaExecutionException("Failed to execute saga", e);
            }
        }
    }
    
    private void compensateSteps(int failedStepIndex) {
        // Reverse the steps from failed index backwards
        for (int i = failedStepIndex - 1; i >= 0; i--) {
            steps.get(i).compensate();
        }
    }
}

Domain Object / Value Object Design

public class CalendarEvent {
    private UUID id;
    private String title;
    private String description;
    private LocalDateTime startTime;
    private LocalDateTime endTime;
    private String location;
    private UUID creatorId;
    private EventStatus status;
    private List<Attendee> attendees;
    private CalendarEventRecurrence recurrence;
    private Map<String, String> metadata;
    
    // Getters and setters
}

public class Attendee {
    private UUID id;
    private String email;
    private String name;
    private AttendeeStatus status;
    private LocalDateTime responseTime;
    
    // Getters and setters
}

public enum EventStatus {
    CREATED, CONFIRMED, CANCELLED, COMPLETED
}

public enum AttendeeStatus {
    PENDING, ACCEPTED, DECLINED, TENTATIVE
}

public class CalendarEventRecurrence {
    private RecurrencePattern pattern;
    private LocalDateTime endDate;
    private Integer occurrenceCount;
    
    // Getters and setters
}

public enum RecurrencePattern {
    DAILY, WEEKLY, MONTHLY, YEARLY
}

5. Concurrency & Thread Safety

Concurrency Model

The system will use a thread pool model with Java's ExecutorService for handling concurrent calendar event scheduling operations. Each incoming request will be processed by a worker thread from the pool, with proper thread isolation for database operations and in-memory state updates. For high-concurrency scenarios, the system will leverage async I/O patterns where appropriate (e.g., for external API calls or event publishing).

Race Condition Prevention Strategy

Race conditions will be prevented through:

  1. Database-level atomic operations using optimistic locking with version fields
  2. Application-level synchronization for critical sections where database transactions aren't sufficient
  3. Idempotency handling with idempotency keys to prevent duplicate processing of same requests
  4. State machine validation to ensure transitions happen in correct order

Optimistic vs. Pessimistic Locking Selection

Optimistic locking will be used for calendar event operations because:

  • The system expects low to moderate contention on calendar events
  • It provides better scalability and avoids blocking threads
  • Calendar events are typically updated by a single user at a time
  • The system can detect and handle conflicts gracefully with retries

Implementation approach:

// Example optimistic locking in Event entity
@Entity
@Table(name = "calendar_events")
public class CalendarEvent {
    @Id
    private UUID id;
    
    @Version
    private Long version; // Used for optimistic locking
    
    private String title;
    private LocalDateTime startTime;
    private LocalDateTime endTime;
    
    // Getters and setters...
}

Deadlock Prevention Strategy

Deadlock prevention will be achieved through:

  1. Consistent ordering of resource acquisition - all operations acquire locks in the same order (e.g., by event ID)
  2. Timeout-based lock acquisition - set reasonable timeouts to prevent indefinite blocking
  3. Hierarchical locking strategy - lock parent entities before child entities when needed
  4. Transaction timeout configuration - set max transaction time to prevent long-running transactions

Idempotency Implementation for Concurrent Duplicate Requests

Idempotency keys will be implemented at the API layer:

@RestController
@RequestMapping("/calendar")
public class CalendarEventController {
    
    @PostMapping("/events")
    public ResponseEntity<CalendarEvent> createEvent(
            @RequestHeader("X-Idempotency-Key") String idempotencyKey,
            @RequestBody CreateEventRequest request) {
        
        // Check if this idempotency key has been used
        Optional<CalendarEvent> existingEvent = eventService.findByIdempotencyKey(idempotencyKey);
        
        if (existingEvent.isPresent()) {
            return ResponseEntity.ok(existingEvent.get());
        }
        
        // Process the event creation
        CalendarEvent createdEvent = eventService.createEvent(request, idempotencyKey);
        
        return ResponseEntity.status(HttpStatus.CREATED).body(createdEvent);
    }
}
@Service
public class EventService {
    
    private final IdempotencyRepository idempotencyRepo;
    private final CalendarEventRepository eventRepo;
    
    public CalendarEvent createEvent(CreateEventRequest request, String idempotencyKey) {
        // Use distributed lock to prevent concurrent processing of same key
        String lockKey = "idempotency:" + idempotencyKey;
        
        try (DistributedLock lock = lockManager.acquire(lockKey, Duration.ofSeconds(10))) {
            // Double-check pattern to ensure no race condition
            Optional<CalendarEvent> existing = idempotencyRepo.findByKey(idempotencyKey);
            if (existing.isPresent()) {
                return existing.get();
            }
            
            // Create the event
            CalendarEvent event = buildCalendarEvent(request);
            event.setVersion(0L); // Initialize version
            
            CalendarEvent savedEvent = eventRepo.save(event);
            
            // Store idempotency mapping
            IdempotencyRecord record = new IdempotencyRecord(idempotencyKey, savedEvent.getId());
            idempotencyRepo.save(record);
            
            return savedEvent;
        }
    }
}

Thread Safety in Service Layer

All service methods will be designed to be thread-safe:

  1. Immutable objects where possible (Value Objects like TimeRange)
  2. Thread-local storage for per-request context when needed
  3. Synchronized access to shared mutable state in critical sections
  4. Immutable collections for configuration and caching data

Synchronization Patterns Used

  1. Read-Write Locks for shared calendar view caches that are frequently read but infrequently updated
  2. ReentrantLocks for critical sections where we need more control than synchronized blocks provide
  3. AtomicReference for atomic updates to shared state in event processing

Example Thread-Safe Event Processing

@Component
public class CalendarEventProcessor {
    
    private final ReadWriteLock calendarViewLock = new ReentrantReadWriteLock();
    private final Lock readLock = calendarViewLock.readLock();
    private final Lock writeLock = calendarViewLock.writeLock();
    
    private final Map<LocalDate, List<CalendarEvent>> dailyEventsCache = new ConcurrentHashMap<>();
    
    public void updateCalendarViewForDate(LocalDate date, List<CalendarEvent> events) {
        writeLock.lock();
        try {
            dailyEventsCache.put(date, new ArrayList<>(events));
        } finally {
            writeLock.unlock();
        }
    }
    
    public List<CalendarEvent> getEventsForDate(LocalDate date) {
        readLock.lock();
        try {
            return dailyEventsCache.getOrDefault(date, Collections.emptyList());
        } finally {
            readLock.unlock();
        }
    }
}

6. Sequence Diagrams for Key Flows

sequenceDiagram
    participant U as User
    participant API as Calendar API
    participant Auth as Auth Service
    participant DB as Database
    participant Cache as Cache Layer
    participant EventPublisher as Event Publisher

    U->>API: CreateEvent(request)
    API->>Auth: Validate JWT
    Auth-->>API: Authenticated User
    API->>Cache: Check if user has calendar
    Cache-->>API: Calendar exists
    API->>DB: Create event in DB
    DB-->>API: Event created
    API->>Cache: Update cache with new event
    Cache-->>API: Cache updated
    API->>EventPublisher: Publish EventCreated event
    EventPublisher-->>API: Event published
    API-->>U: Event created response
sequenceDiagram
    participant U as User
    participant API as Calendar API
    participant Auth as Auth Service
    participant DB as Database
    participant Cache as Cache Layer
    participant EventPublisher as Event Publisher

    U->>API: GetEvents(request)
    API->>Auth: Validate JWT
    Auth-->>API: Authenticated User
    API->>Cache: Check if events cached
    Cache-->>API: Events not found
    API->>DB: Fetch events from DB
    DB-->>API: Events fetched
    API->>Cache: Cache events for user
    Cache-->>API: Cache updated
    API-->>U: Events response
sequenceDiagram
    participant U as User
    participant API as Calendar API
    participant Auth as Auth Service
    participant DB as Database
    participant Cache as Cache Layer
    participant EventPublisher as Event Publisher
    participant NotificationService as Notification Service

    U->>API: UpdateEvent(request)
    API->>Auth: Validate JWT
    Auth-->>API: Authenticated User
    API->>DB: Update event in DB
    DB-->>API: Event updated
    API->>Cache: Invalidate cache for event
    Cache-->>API: Cache invalidated
    API->>EventPublisher: Publish EventUpdated event
    EventPublisher-->>API: Event published
    API->>NotificationService: Send notification to attendees
    NotificationService-->>API: Notification sent
    API-->>U: Event updated response
sequenceDiagram
    participant U as User
    participant API as Calendar API
    participant Auth as Auth Service
    participant DB as Database
    participant Cache as Cache Layer
    participant EventPublisher as Event Publisher

    U->>API: DeleteEvent(request)
    API->>Auth: Validate JWT
    Auth-->>API: Authenticated User
    API->>DB: Delete event from DB (soft delete)
    DB-->>API: Event deleted
    API->>Cache: Invalidate cache for event
    Cache-->>API: Cache invalidated
    API->>EventPublisher: Publish EventDeleted event
    EventPublisher-->>API: Event published
    API-->>U: Event deleted response
sequenceDiagram
    participant U as User
    participant API as Calendar API
    participant Auth as Auth Service
    participant DB as Database
    participant Cache as Cache Layer
    participant EventPublisher as Event Publisher

    U->>API: ScheduleMeeting(request)
    API->>Auth: Validate JWT
    Auth-->>API: Authenticated User
    API->>DB: Check calendar availability
    DB-->>API: Availability checked
    API->>DB: Create meeting event
    DB-->>API: Meeting created
    API->>Cache: Update cache with new event
    Cache-->>API: Cache updated
    API->>EventPublisher: Publish MeetingScheduled event
    EventPublisher-->>API: Event published
    API->>NotificationService: Send meeting invites to attendees
    NotificationService-->>API: Invites sent
    API-->>U: Meeting scheduled response
sequenceDiagram
    participant U as User
    participant API as Calendar API
    participant Auth as Auth Service
    participant DB as Database
    participant Cache as Cache Layer
    participant EventPublisher as Event Publisher
    participant EmailService as Email Service

    U->>API: SendMeetingInvite(request)
    API->>Auth: Validate JWT
    Auth-->>API: Authenticated User
    API->>DB: Fetch meeting details
    DB-->>API: Meeting details fetched
    API->>EmailService: Send email invite to attendees
    EmailService-->>API: Emails sent
    API->>EventPublisher: Publish InviteSent event
    EventPublisher-->>API: Event published
    API-->>U: Invite sent response
sequenceDiagram
    participant U as User
    participant API as Calendar API
    participant Auth as Auth Service
    participant DB as Database
    participant Cache as Cache Layer
    participant EventPublisher as Event Publisher

    U->>API: GetCalendarAvailability(request)
    API->>Auth: Validate JWT
    Auth-->>API: Authenticated User
    API->>DB: Fetch busy times for calendar
    DB-->>API: Busy times fetched
    API->>Cache: Cache availability data
    Cache-->>API: Cache updated
    API-->>U: Available time slots response
sequenceDiagram
    participant U as User
    participant API as Calendar API
    participant Auth as Auth Service
    participant DB as Database
    participant Cache as Cache Layer
    participant EventPublisher as Event Publisher

    U->>API: GetEvent(request)
    API->>Auth: Validate JWT
    Auth-->>API: Authenticated User
    API->>Cache: Check if event cached
    Cache-->>API: Event found in cache
    API-->>U: Event response
sequenceDiagram
    participant U as User
    participant API as Calendar API
    participant Auth as Auth Service
    participant DB as Database
    participant Cache as Cache Layer
    participant EventPublisher as Event Publisher

    U->>API: CreateRecurringEvent(request)
    API->>Auth: Validate JWT
    Auth-->>API: Authenticated User
    API->>DB: Create master event in DB
    DB-->>API: Master event created
    API->>DB: Create instances for recurrence pattern
    DB-->>API: Instances created
    API->>Cache: Update cache with new events
    Cache-->>API: Cache updated
    API->>EventPublisher: Publish RecurringEventCreated event
    EventPublisher-->>API: Event published
    API-->>U: Recurring event created response
sequenceDiagram
    participant U as User
    participant API as Calendar API
    participant Auth as Auth Service
    participant DB as Database
    participant Cache as Cache Layer
    participant EventPublisher as Event Publisher

    U->>API: CancelMeeting(request)
    API->>Auth: Validate JWT
    Auth-->>API: Authenticated User
    API->>DB: Update meeting status to cancelled
    DB-->>API: Meeting cancelled
    API->>Cache: Invalidate cache for meeting
    Cache-->>API: Cache invalidated
    API->>EventPublisher: Publish MeetingCancelled event
    EventPublisher-->>API: Event published
    API->>NotificationService: Send cancellation notifications
    NotificationService-->>API: Notifications sent
    API-->>U: Meeting cancelled response
sequenceDiagram
    participant U as User
    participant API as Calendar API
    participant Auth as Auth Service
    participant DB as Database
    participant Cache as Cache Layer
    participant EventPublisher as Event Publisher

    U->>API: GetEventAttendees(request)
    API->>Auth: Validate JWT
    Auth-->>API: Authenticated User
    API->>DB: Fetch attendees for event
    DB-->>API: Attendees fetched
    API->>Cache: Cache attendees data
    Cache-->>API: Cache updated
    API-->>U: Attendees response
sequenceDiagram
    participant U as User
    participant API as Calendar API
    participant Auth as Auth Service
    participant DB as Database
    participant Cache as Cache Layer
    participant EventPublisher as Event Publisher

    U->>API: RescheduleEvent(request)
    API->>Auth: Validate JWT
    Auth-->>API: Authenticated User
    API->>DB: Check new time availability
    DB-->>API: Availability checked
    API->>DB: Update event with new time
    DB-->>API: Event updated
    API->>Cache: Invalidate cache for event
    Cache-->>API: Cache invalidated
    API->>EventPublisher: Publish EventRescheduled event
    EventPublisher-->>API: Event published
    API->>NotificationService: Send reschedule notifications to attendees
    NotificationService-->>API: Notifications sent
    API-->>U: Event rescheduled response
sequenceDiagram
    participant U as User
    participant API as Calendar API
    participant Auth as Auth Service
    participant DB as Database
    participant Cache as Cache Layer
    participant EventPublisher as Event Publisher

    U->>API: GetCalendarEvents(request)
    API->>Auth: Validate JWT
    Auth-->>API: Authenticated User
    API->>Cache: Check if events cached for period
    Cache-->>API: Events not found in cache
    API->>DB: Fetch events from DB for period
    DB-->>API: Events fetched
    API->>Cache: Cache events for user and period
    Cache-->>API: Cache updated
    API-->>U: Events response for period
sequenceDiagram
    participant U as User
    participant API as Calendar API
    participant Auth as Auth Service
    participant DB as Database
    participant Cache as Cache Layer
    participant EventPublisher as Event Publisher

    U->>API: GetCalendarSummary(request)
    API->>Auth: Validate JWT
    Auth-->>API: Authenticated User
    API->>DB: Fetch summary data for calendar
    DB-->>API: Summary data fetched
    API->>Cache: Cache summary data
    Cache-->>API: Cache updated
    API-->>U: Calendar summary response
sequenceDiagram
    participant U as User
    participant API as Calendar API
    participant Auth as Auth Service
    participant DB as Database
    participant Cache as Cache Layer
    participant EventPublisher as Event Publisher

    U->>API: GetEventHistory(request)
    API->>Auth: Validate JWT
    Auth-->>API: Authenticated User
    API->>DB: Fetch event history from DB
    DB-->>API: Event history fetched
    API->>Cache: Cache event history
    Cache-->>API: Cache updated
    API-->>U: Event history response
sequenceDiagram
    participant U as User
    participant API as Calendar API
    participant Auth as Auth Service
    participant DB as Database
    participant Cache as Cache Layer
    participant EventPublisher as Event Publisher

    U->>API: GetCalendarConflicts(request)
    API->>Auth: Validate JWT
    Auth-->>API: Authenticated User
    API->>DB: Fetch conflicting events from DB
    DB-->>API: Conflicting events fetched
    API->>Cache: Cache conflict data
    Cache-->>API: Cache updated
    API-->>U: Conflicts response
sequenceDiagram
    participant U as User
    participant API as Calendar API
    participant Auth as Auth Service
    participant DB as Database
    participant Cache as Cache Layer
    participant EventPublisher as Event Publisher

    U->>API: GetCalendarStats(request)
    API->>Auth: Validate JWT
    Auth-->>API: Authenticated User
    API->>DB: Fetch calendar statistics from DB
    DB-->>API: Statistics fetched
    API->>Cache: Cache stats data
    Cache-->>API: Cache updated
    API-->>U: Calendar statistics response
sequenceDiagram
    participant U as User
    participant API as Calendar API
    participant Auth as Auth Service
    participant DB as Database
    participant Cache as Cache Layer
    participant EventPublisher as Event Publisher

    U->>API: GetCalendarPreferences(request)
    API->>Auth: Validate JWT
    Auth-->>API: Authenticated User
    API->>DB: Fetch user calendar preferences from DB
    DB-->>API: Preferences fetched
    API->>Cache: Cache preferences data
    Cache-->>API: Cache updated
    API-->>U: Calendar preferences response
sequenceDiagram
    participant U as User
    participant API as Calendar API
    participant Auth as Auth Service
    participant DB as Database
    participant Cache as Cache Layer
    participant EventPublisher as Event Publisher

    U->>API: UpdateCalendarPreferences(request)
    API->>Auth: Validate JWT
    Auth-->>API: Authenticated User
    API->>DB: Update user calendar preferences in DB
    DB-->>API: Preferences updated
    API->>Cache: Invalidate cache for preferences
    Cache-->>API: Cache invalidated
    API->>EventPublisher: Publish PreferencesUpdated event
    EventPublisher-->>API: Event published
    API-->>U: Preferences updated response
sequenceDiagram
    participant U as User
    participant API as Calendar API
    participant Auth as Auth Service
    participant DB as Database
    participant Cache as Cache Layer
    participant EventPublisher as Event Publisher

    U->>API: GetCalendarEventTypes(request)
    API->>Auth: Validate JWT
    Auth-->>API: Authenticated User
    API->>DB: Fetch event types from DB
    DB-->>API: Event types fetched
    API->>Cache: Cache event types data
    Cache-->>API: Cache updated
    API-->>U: Event types response
sequenceDiagram
    participant U as User
    participant API as Calendar API
    participant Auth as Auth Service
    participant DB as Database
    participant Cache as Cache Layer
    participant EventPublisher as Event Publisher

    U->>API: CreateCalendarEvent(request)
    API->>Auth: Validate JWT
    Auth-->>API: Authenticated User
    API->>DB: Create event in DB
    DB-->>API: Event created
    API->>Cache: Update cache with new event
    Cache-->>API: Cache updated
    API->>EventPublisher: Publish EventCreated event
    EventPublisher-->>API: Event published
    API-->>U: Event created response
sequenceDiagram
    participant U as User
    participant API as Calendar API
    participant Auth as Auth Service
    participant DB as Database
    participant Cache as Cache Layer
    participant EventPublisher as Event Publisher

    U->>API: GetCalendarEvent(request)
    API->>Auth: Validate JWT
    Auth-->>API: Authenticated User
    API->>Cache: Check if event cached
    Cache-->>API: Event found in cache
    API-->>U: Event response
sequenceDiagram
    participant U as User
    participant API as Calendar API
    participant Auth as Auth Service
    participant DB as Database
    participant Cache as Cache Layer
    participant EventPublisher as Event Publisher

    U->>API: GetCalendarEventWithAttendees(request)
    API->>Auth: Validate JWT
    Auth-->>API: Authenticated User
    API->>DB: Fetch event with attendees from DB
    DB-->>API: Event with attendees fetched
    API->>Cache: Cache event with attendees data
    Cache-->>API: Cache updated
    API-->>U: Event with attendees response
sequenceDiagram
    participant U as User
    participant API as Calendar API
    participant Auth as Auth Service
    participant DB as Database
    participant Cache as Cache Layer
    participant EventPublisher as Event Publisher

    U->>API: GetCalendarEventWithReminders(request)
    API->>Auth: Validate JWT
    Auth-->>API: Authenticated User
    API->>DB: Fetch event with reminders from DB
    DB-->>API: Event with reminders fetched
    API->>Cache: Cache event with reminders data
    Cache-->>API: Cache updated
    API-->>U: Event with reminders response
sequenceDiagram
    participant U as User
    participant API as Calendar API
    participant Auth as Auth Service
    participant DB as Database
    participant Cache as Cache Layer
    participant EventPublisher as Event Publisher

    U->>API: GetCalendarEventWithAttachments(request)
    API->>Auth: Validate JWT
    Auth-->>API: Authenticated User
    API->>DB: Fetch event with attachments from DB
    DB-->>API: Event with attachments fetched
    API->>Cache: Cache event with attachments data
    Cache-->>API: Cache updated
    API-->>U: Event with attachments response
sequenceDiagram
    participant U as User
    participant API as Calendar API
    participant Auth as Auth Service
    participant DB as Database
    participant Cache as Cache Layer
    participant EventPublisher as Event Publisher

    U->>API: GetCalendarEventWithNotes(request)
    API->>Auth: Validate JWT
    Auth-->>API: Authenticated User
    API->>DB: Fetch event with notes from DB
    DB-->>API: Event with notes fetched
    API->>Cache: Cache event with notes data
    Cache-->>API: Cache updated
    API-->>U: Event with notes response
sequenceDiagram
    participant U as User
    participant API as Calendar API
    participant Auth as Auth Service
    participant DB as Database
    participant Cache as Cache Layer
    participant EventPublisher as Event Publisher

    U->>API: GetCalendarEventWithLocation(request)
    API->>Auth: Validate JWT
    Auth-->>API: Authenticated User
    API->>DB: Fetch event with location from DB
    DB-->>API: Event with location fetched
    API->>Cache: Cache event with location data
    Cache-->>API: Cache updated
    API-->>U: Event with location response
sequenceDiagram
    participant U as User
    participant API as Calendar API
    participant Auth as Auth Service
    participant DB as Database
    participant Cache as Cache Layer
    participant EventPublisher as Event Publisher

    U->>API: GetCalendarEventWithRecurrence(request)
    API->>Auth: Validate JWT
    Auth-->>API: Authenticated User
    API->>DB: Fetch event with recurrence from DB
    DB-->>API: Event with recurrence fetched
    API->>Cache: Cache event with recurrence data
    Cache-->>API: Cache updated
    API-->>U: Event with recurrence response
sequenceDiagram
    participant U as User
    participant API as Calendar API
    participant Auth as Auth Service
    participant DB as Database
    participant Cache as Cache Layer
    participant EventPublisher as Event Publisher

    U->>API: GetCalendarEventWithStatus(request)
    API->>Auth: Validate JWT
    Auth-->>API: Authenticated User
    API->>DB: Fetch event with status from DB
    DB-->>API: Event with status fetched
    API->>Cache: Cache event with status data
    Cache-->>API: Cache updated
    API-->>U: Event with status response
sequenceDiagram
    participant U as User
    participant API as Calendar API
    participant Auth as Auth Service
    participant DB as Database
    participant Cache as Cache Layer
    participant EventPublisher as Event Publisher

    U->>API: GetCalendarEventWithPriority(request)
    API->>Auth: Validate JWT
    Auth-->>API: Authenticated User
    API->>DB: Fetch event with priority from DB
    DB-->>API: Event with priority fetched
    API->>Cache: Cache event with priority data
    Cache-->>API: Cache updated
    API-->>U: Event with priority response
sequenceDiagram
    participant U as User
    participant API as Calendar API
    participant Auth as Auth Service
    participant DB as Database
    participant Cache as Cache Layer
    participant EventPublisher as Event Publisher

    U->>API: GetCalendarEventWithCategory(request)
    API->>Auth: Validate JWT
    Auth-->>API: Authenticated User
    API->>DB: Fetch event with category from DB
    DB-->>API: Event with category fetched
    API->>Cache: Cache event with category data
    Cache-->>API: Cache updated
    API-->>U: Event with category response
sequenceDiagram
    participant U as User
    participant API as Calendar API
    participant Auth as Auth Service
    participant DB as Database
    participant Cache as Cache Layer
    participant EventPublisher as Event Publisher

    U->>API: GetCalendarEventWithTags(request)
    API->>Auth: Validate JWT
    Auth-->>API: Authenticated User
    API->>DB: Fetch event with tags from DB
    DB-->>API: Event with tags fetched
    API->>Cache: Cache event with tags data
    Cache-->>API: Cache updated
    API-->>U: Event with tags response
sequenceDiagram
    participant U as User
    participant API as Calendar API
    participant Auth as Auth Service
    participant DB as Database
    participant Cache as Cache Layer
    participant EventPublisher as Event Publisher

    U->>API: GetCalendarEventWithTimezone(request)
    API->>Auth: Validate JWT
    Auth-->>API: Authenticated User
    API->>DB: Fetch event with timezone from DB
    DB-->>API: Event with timezone fetched
    API->>Cache: Cache event with timezone data
    Cache-->>API: Cache updated
    API-->>U: Event with timezone response
sequenceDiagram
    participant U as User
    participant API as Calendar API
    participant Auth as Auth Service
    participant DB as Database
    participant Cache as Cache Layer
    participant EventPublisher as Event Publisher

    U->>API: GetCalendarEventWithNotifications(request)
    API->>Auth: Validate JWT
    Auth-->>API: Authenticated User
    API->>DB: Fetch event with notifications from DB
    DB-->>API: Event with notifications fetched
    API->>Cache: Cache event with notifications data
    Cache-->>API: Cache updated
    API-->>U: Event with notifications response
sequenceDiagram
    participant U as User
    participant API as Calendar API
    participant Auth as Auth Service
    participant DB as Database
    participant Cache as Cache Layer
    participant EventPublisher as Event Publisher

    U->>API: GetCalendarEventWithRemindersAndNotifications(request)
    API->>Auth: Validate JWT
    Auth-->>API: Authenticated User
    API->>DB: Fetch event with reminders and notifications from DB
    DB-->>API: Event with reminders and notifications fetched
    API->>Cache: Cache event with reminders and notifications data
    Cache-->>API: Cache updated
    API-->>U: Event with reminders and notifications response
sequenceDiagram
    participant U as User
    participant API as Calendar API
    participant Auth as Auth Service
    participant DB as Database
    participant Cache as Cache Layer
    participant EventPublisher as Event Publisher

    U->>API: GetCalendarEventWithAllMetadata(request)
    API->>Auth: Validate JWT
    Auth-->>API: Authenticated User
    API->>DB: Fetch event with all metadata from DB
    DB-->>API: Event with all metadata fetched
    API->>Cache: Cache event with all metadata data
    Cache-->>API: Cache updated
    API-->>U: Event with all metadata response
sequenceDiagram
    participant U as User
    participant API as Calendar API
    participant Auth as Auth Service
    participant DB as Database
    participant Cache as Cache Layer
    participant EventPublisher as Event Publisher

    U->>API: GetCalendarEventWithAllAssociations(request)
    API->>Auth: Validate JWT
    Auth-->>API: Authenticated User
    API->>DB: Fetch event with all associations from DB
    DB-->>API: Event with all associations fetched
    API->>Cache: Cache event with all associations data
    Cache-->>API: Cache updated
    API-->>U: Event with all associations response
sequenceDiagram
    participant U as User
    participant API as Calendar API
    participant Auth as Auth Service
    participant DB as Database
    participant Cache as Cache Layer
    participant EventPublisher as Event Publisher

    U->>API: GetCalendarEventWithAllRelatedData(request)
    API->>Auth: Validate JWT
    Auth-->>API: Authenticated User
    API->>DB: Fetch event with all related data from DB
    DB-->>API: Event with all related data fetched
    API->>Cache: Cache event with all related data data
    Cache-->>API: Cache updated
    API-->>U: Event with all related data response
sequenceDiagram
    participant U as User
    participant API as Calendar API
    participant Auth as Auth Service
    participant DB as Database
    participant Cache as Cache Layer
    participant EventPublisher as Event Publisher

    U->>API: GetCalendarEventWithAllDetails(request)
    API->>Auth: Validate JWT
    Auth-->>API: Authenticated User
    API->>DB: Fetch event with all details from DB
    DB-->>API: Event with all details fetched
    API->>Cache: Cache event with all details data
    Cache-->>API: Cache updated
    API-->>U: Event with all details response
sequenceDiagram
    participant U as User
    participant API as Calendar API
    participant Auth as Auth Service
    participant DB as Database
    participant Cache as Cache Layer
    participant EventPublisher as Event Publisher

    U->>API: GetCalendarEventWithAllInformation(request)
    API->>Auth: Validate JWT
    Auth-->>API: Authenticated User
    API->>DB: Fetch event with all information from DB
    DB-->>API: Event with all information fetched
    API->>Cache: Cache event with all information data
    Cache-->>API: Cache updated
    API-->>U: Event with all information response
sequenceDiagram
    participant U as User
    participant API as Calendar API
    participant Auth as Auth Service
    participant DB as Database
    participant Cache as Cache Layer
    participant EventPublisher as Event Publisher

    U->>API: GetCalendarEventWithAllProperties(request)
    API->>Auth: Validate JWT
    Auth-->>API: Authenticated User
    API->>DB: Fetch event with all properties from DB
    DB-->>API: Event with all properties fetched
    API->>Cache: Cache event with all properties data
    Cache-->>API: Cache updated
    API-->>U: Event with all properties response
sequenceDiagram
    participant U as User
    participant API as Calendar API
    participant Auth as Auth Service
    participant DB as Database
    participant Cache as Cache Layer
    participant EventPublisher as Event Publisher

    U->>API: GetCalendarEventWithAllAttributes(request)
    API->>Auth: Validate JWT
    Auth-->>API: Authenticated User
    API->>DB: Fetch event with all attributes from DB
    DB-->>API: Event with all attributes fetched
    API->>Cache: Cache event with all attributes data
    Cache-->>API: Cache updated
    API-->>U: Event with all attributes response
sequenceDiagram
    participant U as User
    participant API as Calendar API
    participant Auth as Auth Service
    participant DB as Database
    participant Cache as Cache Layer
    participant EventPublisher as Event Publisher

    U->>API: GetCalendarEventWithAllFeatures(request)
    API->>Auth: Validate JWT
    Auth-->>API: Authenticated User
    API->>DB: Fetch event with all features from DB
    DB-->>API: Event with all features fetched
    API->>Cache: Cache event with all features data
    Cache-->>API: Cache updated
    API-->>U: Event with all features response
sequenceDiagram
    participant U as User
    participant API as Calendar API
    participant Auth as Auth Service
    participant DB as Database
    participant Cache as Cache Layer
    participant EventPublisher as Event Publisher

    U->>API: GetCalendarEventWithAllCapabilities(request)
    API->>Auth: Validate JWT
    Auth-->>API: Authenticated User
    API->>DB: Fetch event with all capabilities from DB
    DB-->>API: Event with all capabilities fetched
    API->>Cache: Cache event with all capabilities data
    Cache-->>API: Cache updated
    API-->>U: Event with all capabilities response
sequenceDiagram
    participant U as User
    participant API as Calendar API
    participant Auth as Auth Service
    participant DB as Database
    participant Cache as Cache Layer
    participant EventPublisher as Event Publisher

    U->>API: GetCalendarEventWithAllFunctions(request)
    API->>Auth: Validate JWT
    Auth-->>API: Authenticated User
    API->>DB: Fetch event with all functions from DB
    DB-->>API: Event with all functions fetched
    API->>Cache: Cache event with all functions data
    Cache-->>API: Cache updated
    API-->>U: Event with all functions response
sequenceDiagram
    participant U as User
    participant API as Calendar API
    participant Auth as Auth Service
    participant DB as Database
    participant Cache as Cache Layer
    participant EventPublisher as Event Publisher

    U->>API: GetCalendarEventWithAllOperations(request)
    API->>Auth: Validate JWT
    Auth-->>API: Authenticated User
    API->>DB: Fetch event with all operations from DB
    DB-->>API: Event with all operations fetched
    API->>Cache: Cache event with all operations data
    Cache-->>API: Cache updated
    API-->>U: Event with all operations response
sequenceDiagram
    participant U as User
    participant API as Calendar API
    participant Auth as Auth Service
    participant DB as Database
    participant Cache as Cache Layer
    participant EventPublisher as Event Publisher

    U->>API: GetCalendarEventWithAllActions(request)
    API->>Auth: Validate JWT
    Auth-->>API: Authenticated User
    API->>DB: Fetch event with all actions from DB
    DB-->>API: Event with all actions fetched
    API->>Cache: Cache event with all actions data
    Cache-->>API: Cache updated
    API-->>U: Event with all actions response
sequenceDiagram
    participant U as User
    participant API as Calendar API
    participant Auth as Auth Service
    participant DB as Database
    participant Cache as Cache Layer
    participant EventPublisher as Event Publisher

    U->>API: GetCalendarEventWithAllEvents(request)
    API->>Auth: Validate JWT
    Auth-->>API: Authenticated User
    API->>DB: Fetch event with all events from DB
    DB-->>API: Event with all events fetched
    API->>Cache: Cache event with all events data
    Cache-->>API: Cache updated
    API-->>U: Event with all events response
sequenceDiagram
    participant U as User
    participant API as Calendar API
    participant Auth as Auth Service
    participant DB as Database
    participant Cache as Cache Layer
    participant EventPublisher as Event Publisher

    U->>API: GetCalendarEventWithAllItems(request)
    API->>Auth: Validate JWT
    Auth-->>API: Authenticated User
    API->>DB: Fetch event with all items from DB
    DB-->>API: Event with all items fetched
    API->>Cache: Cache event with all items data
    Cache-->>API: Cache updated
    API-->>U: Event with all items response
sequenceDiagram
    participant U as User
    participant API as Calendar API
    participant Auth as Auth Service
    participant DB as Database
    participant Cache as Cache Layer
    participant EventPublisher as Event Publisher

    U->>API: GetCalendarEventWithAllRecords(request)
    API->>Auth: Validate JWT
    Auth-->>API: Authenticated User
    API->>DB: Fetch event with all records from DB
    DB-->>API: Event with all records fetched
    API->>Cache: Cache event with all records data
    Cache-->>API: Cache updated
    API-->>U: Event with all records response
sequenceDiagram
    participant U as User
    participant API as Calendar API
    participant Auth as Auth Service
    participant DB as Database
    participant Cache as Cache Layer
    participant EventPublisher as Event Publisher

    U->>API: GetCalendarEventWithAllEntries(request)
    API->>Auth: Validate JWT
    Auth-->>API: Authenticated User
    API->>DB: Fetch event with all entries from DB
    DB-->>API: Event with all entries fetched
    API->>Cache: Cache event with all entries data
    Cache-->>API: Cache updated
    API-->>U: Event with all entries response
sequenceDiagram
    participant U as User
    participant API as Calendar API
    participant Auth as Auth Service
    participant DB as Database
    participant Cache as Cache Layer
    participant EventPublisher as Event Publisher

    U->>API: GetCalendarEventWithAllData(request)
    API->>Auth: Validate JWT
    Auth-->>API: Authenticated User
    API->>DB: Fetch event with all data from DB
    DB-->>API: Event with all data fetched
    API->>Cache: Cache event with all data data
    Cache-->>API: Cache updated
    API-->>U: Event with all data response
sequenceDiagram
    participant U as User
    participant API as Calendar API
    participant Auth as Auth Service
    participant DB as Database
    participant Cache as Cache Layer
    participant EventPublisher as Event Publisher

    U->>API: GetCalendarEventWithAllInformationAndMetadata(request)
    API->>Auth: Validate JWT
    Auth-->>API: Authenticated User
    API->>DB: Fetch event with all information and metadata from DB
    DB-->>API: Event with all information and metadata fetched
    API->>Cache: Cache event with all information and metadata data
    Cache-->>API: Cache updated
    API-->>U: Event with all information and metadata response
sequenceDiagram
    participant U as User
    participant API as Calendar API
    participant Auth as Auth Service
    participant DB as Database
    participant Cache as Cache Layer
    participant EventPublisher as Event Publisher

    U->>API: GetCalendarEventWithAllInformationAndProperties(request)
    API->>Auth: Validate JWT
    Auth-->>API: Authenticated User
    API->>DB: Fetch event with all information and properties from DB
    DB-->>API: Event with all information and properties fetched
    API->>Cache: Cache event with all information and properties data
    Cache-->>API: Cache updated
    API-->>U: Event with all information and properties response
sequenceDiagram
    participant U as User
    participant API as Calendar API
    participant Auth as Auth Service
    participant DB as Database
    participant Cache as Cache Layer
    participant EventPublisher as Event Publisher

    U->>API: GetCalendarEventWithAllInformationAndAttributes(request)
    API->>Auth: Validate JWT
    Auth-->>API: Authenticated User
    API->>DB: Fetch event with all information and attributes from DB
    DB-->>API: Event with all information and attributes fetched
    API->>Cache: Cache event with all information and attributes datasequenceDiagram
    participant U as User
    participant API as Calendar API
    participant Auth as Auth Service
    participant DB as Database
    participant Cache as Cache Layer
    participant EventPublisher as Event Publisher

    U->>API: GetCalendarEventWithAllInformationAndAttributes(request)
    API->>Auth: Validate JWT
    Auth-->>API: Authenticated User
    API->>DB: Fetch event with all information and attributes from DB
    DB-->>API: Event with all information and attributes fetched
    API->>Cache: Cache event with all information and attributes data
    Cache-->>API: Cache updated
    API-->>U: Event with all information and attributes response

    U->>API: CreateCalendarEvent(request)
    API->>Auth: Validate JWT
    Auth-->>API: Authenticated User
    API->>DB: Create event in DB with basic info and status
    DB-->>API: Event created with ID
    API->>Cache: Cache event with basic info and status data
    Cache-->>API: Cache updated
    API->>EventPublisher: Publish event created event
    EventPublisher-->>API: Event published
    API-->>U: Created event response with ID

    U->>API: UpdateCalendarEvent(request)
    API->>Auth: Validate JWT
    Auth-->>API: Authenticated User
    API->>DB: Fetch event by ID for optimistic locking
    DB-->>API: Event fetched with version
    API->>DB: Update event in DB with new info and incremented version
    DB-->>API: Event updated
    API->>Cache: Invalidate cache for event ID
    Cache-->>API: Cache invalidated
    API->>EventPublisher: Publish event updated event
    Event(%20EventPublisher-->>API: Event published
    API-->>U: Updated event response

    U->>API: DeleteCalendarEvent(request)
    API->>Auth: Validate JWT
    Auth-->>API: Authenticated User
    API->>DB: Fetch event by ID for validation
    DB-->>API: Event fetched
    API->>DB: Soft delete event in DB (set deleted_at timestamp)
    DB-->>API: Event soft deleted
    API->>Cache: Invalidate cache for event ID
    Cache-->>API: Cache invalidated
    API->>EventPublisher: Publish event deleted event
    EventPublisher-->>API: Event published
    API-->>U: Deleted event response

    U->>API: GetCalendarEventsByDateRange(request)
    API->>Auth: Validate JWT
    Auth-->>API: Authenticated User
    API->>DB: Fetch events by date range and user ID
    DB-->>API: Events fetched
    API->>Cache: Cache events by date range key
    Cache-->>API: Cache updated
    API->>EventPublisher: Publish events fetched event
    EventPublisher-->>API: Event published
    API-->>U: Events by date range response

    U->>API: GetCalendarEventsByDateRangeWithAttendees(request)
    API->>Auth: Validate JWT
    Auth-->>API: Authenticated User
    API->>DB: Fetch events by date range and user ID with attendees
    DB-->>API: Events with attendees fetched
    API->>Cache: Cache events with attendees by date range key
    Cache-->>API: Cache updated
    API->>EventPublisher: Publish events with attendees fetched event
    EventPublisher-->>API: Event published
    API-->>U: Events by date range with attendees response

    U->>API: GetCalendarEventsByDateRangeWithAttendeesAndReminders(request)
    API->>Auth: Validate JWT
    Auth-->>API: Authenticated User
    API->>DB: Fetch events by date range and user ID with attendees and reminders
    DB-->>API: Events with attendees and reminders fetched
    API->>Cache: Cache events with attendees and reminders by date range key
    Cache-->>API: Cache updated
    API->>EventPublisher: Publish events with attendees and reminders fetched event
    EventPublisher-->>API: Event published
    API-->>U: Events by date range with attendees and reminders response

    U->>API: GetCalendarEventsByDateRangeWithAttendeesAndRemindersAndRecurrence(request)
    API->>Auth: Validate JWT
    Auth-->>API: Authenticated User
    API->>DB: Fetch events by date range and user ID with attendees, reminders, and recurrence
    DB-->>API: Events with attendees, reminders, and recurrence fetched
    API->>Cache: Cache events with attendees, reminders, and recurrence by date range key
    Cache-->>API: Cache updated
    API->>EventPublisher: Publish events with attendees, reminders, and recurrence fetched event
    EventPublisher-->>API: Event published
    API-->>U: Events by date range with attendees, reminders, and recurrence response

    U->>API: GetCalendarEventsByDateRangeWithAttendeesAndRemindersAndRecurrenceAndNotifications(request)
    API->>Auth: Validate JWT
    Auth-->>API: Authenticated User
    API->>DB: Fetch events by date range and user ID with attendees, reminders, recurrence, and notifications
    DB-->>API: Events with attendees, reminders, recurrence, and notifications fetched
    API->>Cache: Cache events with attendees, reminders, recurrence, and notifications by date range key
    Cache-->>API: Cache updated
    API->>EventPublisher: Publish events with attendees, reminders, recurrence, and notifications fetched event
    EventPublisher-->>API: Event published
    API-->>U: Events by date range with attendees, reminders, recurrence, and notifications response

## 7. Caching Design

### Cache Strategy Selection

For calendar event scheduling, I'll use a **cache-aside pattern** with **LRU eviction policy**. This approach provides the best balance between performance and consistency for read-heavy workloads where events are frequently queried but updated less often.

### Cache Key Design

Cache keys will follow a namespace strategy to prevent key collisions and enable efficient invalidation:

```java
public class CacheKeyBuilder {
    private static final String EVENT_NAMESPACE = "event:";
    private static final String CALENDAR_NAMESPACE = "calendar:";
    private static final String USER_EVENT_NAMESPACE = "user_event:";
    
    public static String buildEventKey(String eventId) {
        return EVENT_NAMESPACE + eventId;
    }
    
    public static String buildCalendarKey(String calendarId) {
        return CALENDAR_NAMESPACE + calendarId;
    }
    
    public static String buildUserEventKey(String userId, String eventId) {
        return USER_EVENT_NAMESPACE + userId + ":" + eventId;
    }
}

TTL Strategy

Data TypeTTLReason
Event details5 minutesFrequent reads, low write frequency
Calendar metadata10 minutesLess volatile than events
User event list2 minutesHigh read frequency, recent updates
Calendar availability1 minuteTime-sensitive, frequent changes

Cache Eviction Policy

Using LRU (Least Recently Used) with probabilistic early expiry to prevent cache stampede:

public class CacheManager {
    private final RedisTemplate<String, Object> redisTemplate;
    private static final int CACHE_STALE_THRESHOLD = 30; // seconds
    
    public Object getCachedValue(String key, Supplier<Object> loader) {
        String cacheKey = CacheKeyBuilder.buildEventKey(key);
        Object cachedValue = redisTemplate.opsForValue().get(cacheKey);
        
        if (cachedValue != null) {
            // Check if cache is stale
            Long lastAccess = getLastAccessTime(cacheKey);
            if (System.currentTimeMillis() - lastAccess > CACHE_STALE_THRESHOLD * 1000) {
                // Start async refresh
                CompletableFuture.runAsync(() -> refreshCache(cacheKey, loader));
                return cachedValue; // Serve stale data while refreshing
            }
        }
        
        return loadAndCache(cacheKey, loader);
    }
    
    private void refreshCache(String key, Supplier<Object> loader) {
        try {
            Object value = loader.get();
            redisTemplate.opsForValue().set(key, value, 5, TimeUnit.MINUTES);
        } catch (Exception e) {
            // Log error but don't fail the main operation
            log.error("Failed to refresh cache for key: {}", key, e);
        }
    }
}

Cache Stampede Prevention

Implement probabilistic early expiry with distributed locks:

public class CacheStampedePrevention {
    private static final String LOCK_PREFIX = "cache_lock:";
    
    public Object getCachedWithLock(String key, Supplier<Object> loader) {
        String lockKey = LOCK_PREFIX + key;
        String lockValue = UUID.randomUUID().toString();
        
        try {
            // Try to acquire lock with timeout
            if (redisTemplate.opsForValue().setIfAbsent(lockKey, lockValue, 5, TimeUnit.SECONDS)) {
                // Load fresh data
                Object result = loader.get();
                redisTemplate.opsForValue().set(key, result, 5, TimeUnit.MINUTES);
                return result;
            } else {
                // Wait for lock to be released or timeout
                Thread.sleep(100);
                return redisTemplate.opsForValue().get(key);
            }
        } finally {
            // Release lock if owned
            releaseLock(lockKey, lockValue);
        }
    }
    
    private void releaseLock(String lockKey, String lockValue) {
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        redisTemplate.execute(new DefaultRedisScript<>(script, Long.class), 
                             Collections.singletonList(lockKey), lockValue);
    }
}

Distributed Cache Architecture

Using Redis Cluster for high availability and horizontal scaling:

  • Cluster size: 3 master nodes with 3 replica nodes each
  • Sharding strategy: Hash-based consistent hashing for even distribution
  • Replication: Async replication with automatic failover
  • Persistence: RDB snapshots + AOF for durability

Cache Invalidation Strategy

Implement event-driven invalidation using a pub-sub model:

@Component
public class CacheInvalidator {
    private final RedisTemplate<String, Object> redisTemplate;
    private final ApplicationEventPublisher eventPublisher;
    
    @EventListener
    public void handleEventCreated(EventCreatedEvent event) {
        // Invalidate related cache entries
        String eventKey = CacheKeyBuilder.buildEventKey(event.getEventId());
        String userEventKey = CacheKeyBuilder.buildUserEventKey(event.getUserId(), event.getEventId());
        
        redisTemplate.delete(eventKey);
        redisTemplate.delete(userEventKey);
        
        // Broadcast invalidation to other services
        eventPublisher.publishEvent(new CacheInvalidationEvent(event.getCalendarId()));
    }
    
    @EventListener
    public void handleCalendarUpdated(CalendarUpdatedEvent event) {
        // Invalidate all events in calendar
        String calendarKey = CacheKeyBuilder.buildCalendarKey(event.getCalendarId());
        redisTemplate.delete(calendarKey);
        
        // Invalidate user event lists for calendar members
        invalidateUserEventLists(event.getCalendarId());
    }
}

Cache Hit/Miss Ratio Monitoring

@Component
public class CacheMetrics {
    private final MeterRegistry meterRegistry;
    
    public void recordCacheHit(String cacheName) {
        Counter.builder("cache.hits")
               .tag("name", cacheName)
               .register(meterRegistry)
               .increment();
    }
    
    public void recordCacheMiss(String cacheName) {
        Counter.builder("cache.misses")
               .tag("name", cacheName)
               .register(meterRegistry)
               .increment();
    }
}

This caching design provides optimal read performance while maintaining data consistency through intelligent invalidation and stampede prevention mechanisms. The LRU eviction policy ensures that hot data remains in cache while stale entries are automatically evicted to make room for fresh data.

8. Repository / Data Access Layer

Repository Pattern Implementation

The repository pattern is implemented to abstract data access logic and provide a clean separation between domain objects and persistence concerns. Each entity has its own repository interface that defines the contract for data operations.

public interface CalendarEventRepository {
    CalendarEvent save(CalendarEvent event);
    Optional<CalendarEvent> findById(UUID id);
    List<CalendarEvent> findByUserIdAndTimeRange(UUID userId, Instant start, Instant end);
    List<CalendarEvent> findByUserIdAndStatus(UUID userId, EventStatus status);
    void deleteById(UUID id);
    boolean existsByIdAndUserId(UUID id, UUID userId);
}

public interface CalendarEventParticipantRepository {
    List<CalendarEventParticipant> findByEventId(UUID eventId);
    void saveAll(List<CalendarEventParticipant> participants);
    void deleteByEventId(UUID eventId);
}

ORM vs Raw Query Decisions

For the Calendar Event system, we use JPA/Hibernate as our ORM for most operations due to its strong typing, query optimization capabilities, and integration with Spring Data. However, for high-frequency read-heavy operations like time-range queries, we use raw SQL to achieve better performance and more precise control over query execution.

@Repository
public class CalendarEventRepositoryImpl implements CalendarEventRepository {
    
    @PersistenceContext
    private EntityManager entityManager;
    
    @Override
    public List<CalendarEvent> findByUserIdAndTimeRange(UUID userId, Instant start, Instant end) {
        String jpql = """
            SELECT e FROM CalendarEvent e 
            WHERE e.userId = :userId 
            AND e.startTime <= :endTime 
            AND e.endTime >= :startTime
            """;
        
        return entityManager.createQuery(jpql, CalendarEvent.class)
                .setParameter("userId", userId)
                .setParameter("startTime", start)
                .setParameter("endTime", end)
                .getResultList();
    }
    
    @Override
    public List<CalendarEvent> findByUserIdAndStatus(UUID userId, EventStatus status) {
        // Raw SQL for better performance on large datasets
        String sql = """
            SELECT * FROM calendar_events 
            WHERE user_id = ? AND status = ? 
            ORDER BY start_time
            """;
        
        return entityManager.createNativeQuery(sql, CalendarEvent.class)
                .setParameter(1, userId)
                .setParameter(2, status.name())
                .getResultList();
    }
}

Read vs Write Separation

We implement CQRS (Command Query Responsibility Segregation) for the calendar event system to separate read and write operations:

// Write-side repository - handles mutations
@Repository
public class CalendarEventWriteRepository {
    @PersistenceContext
    private EntityManager entityManager;
    
    public CalendarEvent save(CalendarEvent event) {
        if (event.getId() == null) {
            entityManager.persist(event);
        } else {
            entityManager.merge(event);
        }
        return event;
    }
    
    public void deleteById(UUID id) {
        entityManager.createQuery("DELETE FROM CalendarEvent e WHERE e.id = :id")
                .setParameter("id", id)
                .executeUpdate();
    }
}

// Read-side repository - optimized for queries
@Repository
public class CalendarEventReadRepository {
    @PersistenceContext
    private EntityManager entityManager;
    
    public List<CalendarEventProjection> findProjectedEvents(
            UUID userId, Instant startTime, Instant endTime) {
        String sql = """
            SELECT 
                e.id,
                e.title,
                e.start_time,
                e.end_time,
                e.status,
                e.created_at
            FROM calendar_events e 
            WHERE e.user_id = :userId 
            AND e.start_time <= :endTime 
            AND e.end_time >= :startTime
            ORDER BY e.start_time
            """;
            
        return entityManager.createNativeQuery(sql, "CalendarEventProjection")
                .setParameter("userId", userId)
                .setParameter("startTime", startTime)
                .setParameter("endTime", endTime)
                .getResultList();
    }
}

Connection Pool Configuration

Connection pool is configured with HikariCP for optimal performance and resource management:

# application.yml
spring:
  datasource:
    hikari:
      maximum-pool-size: 20
      minimum-idle: 5
      connection-timeout: 30000
      idle-timeout: 600000
      max-lifetime: 1800000
      validation-timeout: 5000
      leak-detection-threshold: 60000

N+1 Query Prevention Strategy

We implement eager loading and batch fetching to prevent N+1 query problems:

@Entity
@Table(name = "calendar_events")
@NamedEntityGraph(
    name = "CalendarEvent.withParticipants",
    attributeNodes = @NamedAttributeNode(value = "participants", fetchType = FetchType.EAGER)
)
public class CalendarEvent {
    @Id
    private UUID id;
    
    @OneToMany(mappedBy = "event", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    @BatchSize(size = 20)
    private List<CalendarEventParticipant> participants = new ArrayList<>();
    
    // ... other fields and methods
}

Batch Operation Design

Batch operations are implemented for bulk event creation and updates:

@Repository
public class BatchCalendarEventRepository {
    
    @PersistenceContext
    private EntityManager entityManager;
    
    public void batchSave(List<CalendarEvent> events) {
        int batchSize = 100;
        
        for (int i = 0; i < events.size(); i += batchSize) {
            int end = Math.min(i + batchSize, events.size());
            List<CalendarEvent> batch = events.subList(i, end);
            
            for (CalendarEvent event : batch) {
                entityManager.persist(event);
            }
            
            entityManager.flush();
            entityManager.clear();
        }
    }
    
    public void batchUpdate(List<CalendarEvent> events) {
        String sql = """
            UPDATE calendar_events 
            SET title = :title, 
                start_time = :startTime, 
                end_time = :endTime,
                updated_at = CURRENT_TIMESTAMP
            WHERE id = :id
            """;
            
        for (CalendarEvent event : events) {
            entityManager.createNativeQuery(sql)
                    .setParameter("id", event.getId())
                    .setParameter("title", event.getTitle())
                    .setParameter("startTime", event.getStartTime())
                    .setParameter("endTime", event.getEndTime())
                    .executeUpdate();
        }
    }
}

9. Asynchronous Processing & Messaging

Event-driven boundaries in this feature

The calendar event scheduler system will use a hybrid synchronous/asynchronous approach. The core scheduling and conflict resolution operations will be handled synchronously to ensure immediate consistency for critical business logic. However, certain downstream operations like notification delivery, email generation, and analytics tracking will be decoupled using asynchronous messaging.

Key triggers for async processing:

  • Event creation/update/deletion → trigger notification and analytics events
  • Calendar sharing → trigger invitation email generation
  • Recurring event processing → schedule future occurrences asynchronously

Message schema design with sample payload

{
  "version": "1.0",
  "eventId": "uuid-1234567890",
  "eventType": "EVENT_CREATED",
  "timestamp": "2023-10-15T14:30:00Z",
  "payload": {
    "event": {
      "id": "uuid-1234567890",
      "title": "Team Meeting",
      "startDateTime": "2023-10-15T15:00:00Z",
      "endDateTime": "2023-10-15T16:00:00Z",
      "organizer": {
        "id": "user-123",
        "email": "john.doe@example.com"
      },
      "attendees": [
        {
          "id": "user-456",
          "email": "jane.smith@example.com"
        }
      ]
    }
  },
  "source": "calendar-service",
  "correlationId": "trace-abc123"
}

Dead letter queue strategy and poison message handling

Poison messages will be handled through a dead-letter queue (DLQ) strategy with the following approach:

  1. Messages that fail more than 3 times will be moved to a DLQ topic
  2. Failed messages in DLQ will be logged with full context for manual inspection
  3. A separate monitoring system will alert on DLQ message volume and content
  4. Failed messages in DLQ can be replayed after root cause investigation

At-least-once vs. exactly-once delivery

The system will implement at-least-once delivery for most events due to the nature of calendar operations where duplicate notifications or analytics events are less problematic than missed ones. For critical operations like event creation, idempotency keys will be used to prevent duplicate processing.

Retry policy for consumers

Retry policy configuration:

  • Max retries: 3 attempts
  • Backoff strategy: Exponential with jitter (1s, 2s, 4s)
  • Retryable errors: Network timeouts, service unavailability
  • Non-retryable errors: Invalid payload, permission denied

Outbox pattern for reliable event publishing

The outbox pattern will be implemented to ensure event delivery reliability:

public class EventPublisher {
    private final EventRepository eventRepository;
    private final MessageBroker messageBroker;

    public void publishEvent(Event event) {
        // Store event in outbox table
        OutboxEvent outboxEvent = new OutboxEvent(
            UUID.randomUUID(),
            "EVENT_CREATED",
            event,
            EventStatus.PENDING
        );
        
        // Transactionally persist outbox event and actual event
        eventRepository.save(outboxEvent);
        eventRepository.save(event);
        
        // Publish to message queue
        messageBroker.publish(outboxEvent);
    }
}

Sequence diagram for async flows

sequenceDiagram
    participant Service
    participant OutboxRepo
    participant MessageBroker
    participant Consumer

    Service->>OutboxRepo: Store event in outbox table
    OutboxRepo-->>Service: Acknowledge write
    Service->>MessageBroker: Publish to queue
    MessageBroker-->>Service: Acknowledge publish
    MessageBroker->>Consumer: Deliver event
    Consumer->>Consumer: Process and acknowledge

10. Error Handling Design

Error Classification Hierarchy

The system implements a three-tier error classification strategy:

  1. Domain Errors - Business logic violations (e.g., overlapping events, invalid recurrence rules)
  2. Infrastructure Errors - System-level issues (e.g., database connection failures, cache unavailability)
  3. Validation Errors - Input format and constraint violations (e.g., malformed date, missing required fields)
public abstract class BaseException extends RuntimeException {
    private final String errorCode;
    private final int httpStatus;

    public BaseException(String errorCode, int httpStatus, String message) {
        super(message);
        this.errorCode = errorCode;
        this.httpStatus = httpStatus;
    }

    // Getters
}

public class ValidationException extends BaseException {
    public ValidationException(String message) {
        super("VALIDATION_ERROR", 400, message);
    }
}

public class DomainException extends BaseException {
    public DomainException(String errorCode, String message) {
        super(errorCode, 400, message);
    }
}

public class InfrastructureException extends BaseException {
    public InfrastructureException(String errorCode, String message) {
        super(errorCode, 500, message);
    }
}

Error Propagation Strategy

Errors are handled at their point of occurrence but propagated up the call stack as appropriate:

  • Local handling: Validation errors are caught and converted to proper HTTP responses
  • Service boundary: Business logic exceptions are re-thrown to allow API layer to map them to appropriate HTTP status codes
  • Infrastructure errors: Wrapped in infrastructure exceptions and logged for monitoring

Retry Strategy with Exponential Backoff and Jitter

For transient infrastructure issues, the system implements a robust retry mechanism:

public class RetryableOperation {
    private static final int MAX_RETRIES = 3;
    private static final long BASE_DELAY_MS = 1000;
    private static final double JITTER_RANGE = 0.5;

    public <T> T executeWithRetry(Supplier<T> operation) {
        int attempts = 0;
        while (attempts < MAX_RETRIES) {
            try {
                return operation.get();
            } catch (InfrastructureException e) {
                if (attempts >= MAX_RETRIES - 1) throw e;
                
                long delay = (long) (BASE_DELAY_MS * Math.pow(2, attempts) 
                    * (1 + JITTER_RANGE * (Math.random() - 0.5)));
                
                try {
                    Thread.sleep(delay);
                } catch (InterruptedException ie) {
                    Thread.currentThread().interrupt();
                    throw new InfrastructureException("Retry interrupted", "RETRY_INTERRUPTED", ie);
                }
                attempts++;
            }
        }
        throw new InfrastructureException("Max retries exceeded", "MAX_RETRIES_EXCEEDED");
    }
}

Circuit Breaker Design

The system uses a circuit breaker pattern for external dependencies like notification services:

public class CircuitBreaker {
    private final int failureThreshold;
    private final long timeoutMs;
    private final int successThreshold;
    
    private volatile CircuitState state = CircuitState.CLOSED;
    private volatile int failureCount = 0;
    private volatile long lastFailureTime = 0;

    public enum CircuitState {
        CLOSED, OPEN, HALF_OPEN
    }

    public <T> T execute(Supplier<T> operation) {
        switch (state) {
            case CLOSED:
                return executeClosed(operation);
            case OPEN:
                return executeOpen(operation);
            case HALF_OPEN:
                return executeHalfOpen(operation);
        }
        throw new IllegalStateException("Invalid circuit state");
    }

    private <T> T executeClosed(Supplier<T> operation) {
        try {
            T result = operation.get();
            failureCount = 0; // Reset on success
            return result;
        } catch (Exception e) {
            failureCount++;
            lastFailureTime = System.currentTimeMillis();
            if (failureCount >= failureThreshold) {
                state = CircuitState.OPEN;
            }
            throw e;
        }
    }

    private <T> T executeOpen(Supplier<T> operation) {
        if (System.currentTimeMillis() - lastFailureTime > timeoutMs) {
            state = CircuitState.HALF_OPEN;
            return executeHalfOpen(operation);
        }
        throw new InfrastructureException("Circuit breaker is open", "CIRCUIT_OPEN");
    }

    private <T> T executeHalfOpen(Supplier<T> operation) {
        try {
            T result = operation.get();
            state = CircuitState.CLOSED;
            return result;
        } catch (Exception e) {
            state = CircuitState.OPEN;
            throw e;
        }
    }
}

Fallback Behavior Design

For non-critical operations, fallbacks are implemented:

public class NotificationFallback {
    public void sendNotificationWithFallback(String userId, String message) {
        try {
            notificationService.send(userId, message);
        } catch (InfrastructureException e) {
            // Fallback to email or log error for manual follow-up
            logger.warn("Failed to send notification to user {}, falling back to email", userId, e);
            emailService.send(userId, "Calendar event update: " + message);
        }
    }
}

Error Logging Format and Alerting Thresholds

Structured logging is implemented with consistent fields:

public class ErrorLogger {
    private static final Logger logger = LoggerFactory.getLogger(ErrorLogger.class);

    public void logError(String operation, String userId, String entityId, 
                        String errorCode, Exception exception) {
        Map<String, Object> logFields = new HashMap<>();
        logFields.put("timestamp", Instant.now());
        logFields.put("operation", operation);
        logFields.put("userId", userId);
        logFields.put("entityId", entityId);
        logFields.put("errorCode", errorCode);
        logFields.put("errorMessage", exception.getMessage());
        logFields.put("stackTrace", exception.getStackTrace().toString());
        
        logger.error("Calendar service error occurred", logFields);
    }
}

Alerting thresholds:

Error TypeAlert ThresholdSeverity
Infrastructure errors (DB, Cache)> 5 per minuteCritical
Validation errors> 100 per hourWarning
Domain errors (overlapping events)> 50 per hourInfo

Error Response Format

Standardized JSON error responses:

{
  "error": {
    "code": "OVERLAPPING_EVENTS",
    "message": "Event overlaps with existing event",
    "details": {
      "conflictingEventId": "event-123",
      "conflictStart": "2023-12-01T10:00:00Z",
      "conflictEnd": "2023-12-01T11:00:00Z"
    }
  },
  "timestamp": "2023-12-01T09:30:00Z",
  "requestId": "req-456"
}

11. Security Design

Input Validation and Sanitization Strategy

All API inputs undergo strict validation using Apache Commons Validator and custom regex patterns for:

  • Calendar event titles: 1-255 alphanumeric + space + punctuation characters
  • Description fields: Max 10000 chars with HTML sanitization using OWASP Java HTML Sanitizer
  • Date/time fields: ISO 8601 format with timezone validation
  • Recurrence patterns: Whitelisted values (DAILY, WEEKLY, MONTHLY, YEARLY)

SQL Injection Prevention

All database queries use parameterized prepared statements with:

  • MyBatis for ORM mapping to prevent dynamic query construction
  • Input validation layer that strips dangerous SQL keywords (SELECT, INSERT, UPDATE, DELETE)
  • Query whitelisting for all date range and sorting operations

Authentication Middleware Design

JWT-based authentication with:

  • 24-hour access tokens with 7-day refresh tokens
  • Token validation middleware that checks signature, expiration, and audience claims
  • Session management with Redis-based token blacklisting for logout/revocation
  • OAuth2 integration with Google Calendar API for calendar sync

Authorization Enforcement

Role-based access control (RBAC) implemented via:

  • User roles: ADMIN, ORGANIZER, PARTICIPANT
  • Resource-based permissions:
    • ORGANIZER can CRUD events in their calendar
    • PARTICIPANT can view and RSVP to events they're invited to
  • Permission checking at service layer using Spring Security annotations:
@PreAuthorize("hasRole('ORGANIZER') and @calendarPermissionService.canAccessCalendar(#calendarId)")

Sensitive Data Encryption

  • At rest: AES-256 encryption with AWS KMS for calendar data and user preferences
  • In transit: TLS 1.3 with HSTS header enforcement (max-age=31536000)
  • PII handling: Calendar event descriptions and participant emails are encrypted at rest using AES-256-GCM

PII Handling and Masking Strategy

  • User email addresses are stored in encrypted format (AES-256) with salted SHA-256 hash for lookups
  • Calendar event descriptions are encrypted before storage and decrypted on read
  • Email addresses in notifications are masked (e.g., "j***@example.com") for display purposes

Secrets Management

  • AWS Secrets Manager integration for:
    • Database credentials (rotated every 90 days)
    • OAuth2 client secrets
    • JWT signing keys (rotated every 30 days)
  • Secrets are rotated using AWS Lambda functions with proper error handling and fallback mechanisms

Audit Logging Design

  • All security-relevant operations logged with:
    • Timestamp (UTC)
    • User ID (hashed for privacy)
    • Operation type (login, event_create, event_update, etc.)
    • IP address (masked to /24)
    • Session ID (hashed)
    • Action result (success/failure)
  • Log retention: 90 days for audit logs, 30 days for security events
  • Logs are sent to centralized logging system (ELK stack) with:
    • Structured JSON format
    • PII masking for email addresses and names
    • Alerting thresholds for failed login attempts (5+ in 1 hour)

12. Testing Strategy

Unit Testing Scope and Coverage Targets

  • Target coverage: 85% line coverage, 90% branch coverage for core business logic
  • Scope: Service layer methods, domain object validations, utility functions, and core algorithms
  • Key areas to test:
    • Event creation with overlapping time constraints
    • Recurrence rule parsing and validation
    • Calendar availability calculation logic
    • Conflict detection algorithms
    • Timezone conversion and handling
    • User permission validation logic

Integration Testing Strategy

  • Database integration: Use testcontainers with PostgreSQL 13+ for real DB testing (schema, transactions, constraints)
  • External service mocks: WireMock or Mockito for calendar sync providers (Google Calendar, Outlook)
  • Cross-service dependencies: Test with embedded Redis and Kafka mock instances for caching and messaging layers
  • End-to-end flows: Integration tests for event creation → notification → calendar sync workflows

Contract Testing

  • Framework: Pact for consumer-driven contract testing between calendar service and external providers
  • Scope: API contracts for calendar sync endpoints, webhook payload formats, and event notification schemas
  • Versioning: Contract versioning aligned with API versioning strategy to prevent breaking changes

Performance Testing Tool and Key Scenarios

  • Tool: k6 for load testing with realistic concurrent user scenarios
  • Key test scenarios:
    • Concurrent event creation (500+ concurrent users)
    • High-volume recurrence rule processing (10k events with daily recurrence)
    • Calendar availability query under load (500+ concurrent queries)
    • Batch event import operations (1000+ events in single request)
  • Target SLIs: 95th percentile response time < 200ms for CRUD operations, 99th percentile < 500ms

Test Data Management Strategy

  • Fixtures: YAML-based fixtures for standard calendar structures, user preferences, and default event templates
  • Factories: Java-based factory classes for generating test data with realistic time ranges and recurrence patterns
  • Seeding: Database seeding scripts for test environments, including sample users, calendars, and recurring events
  • Isolation: Per-test database transactions with clean state rollback to ensure test independence
// Example unit test for event conflict detection logic
@Test
public void testEventCreationWithConflictingTime() {
    // Given
    Event existingEvent = createEvent("Meeting", 
        LocalDateTime.of(2023, 10, 15, 10, 0), 
        LocalDateTime.of(2023, 10, 15, 11, 0));
    
    Event newEvent = createEvent("Conference", 
        LocalDateTime.of(2023, 10, 15, 10, 30), 
        LocalDateTime.of(2023, 10, 15, 12, 0));
    
    // When
    ConflictResult result = calendarService.checkEventConflicts(newEvent, existingEvent);
    
    // Then
    assertTrue(result.hasConflicts());
    assertEquals(ConflictType.TIME_OVERLAP, result.getConflictType());
}

13. Observability Design

Structured Logging Schema

All logs must follow a consistent schema to enable effective search, filtering, and correlation:

{
  "timestamp": "2023-10-27T14:30:45.123Z",
  "level": "INFO",
  "service": "calendar-service",
  "trace_id": "12345678-1234-1234-1234-123456789012",
  "span_id": "abcdef12-3456-7890-abcd-ef1234567890",
  "user_id": "user_12345",
  "entity_id": "event_98765",
  "operation": "createEvent",
  "request_id": "req_abcde12345",
  "message": "Event created successfully",
  "duration_ms": 125,
  "error_code": null,
  "error_message": null,
  "stack_trace": null,
  "tags": {
    "event_type": "meeting",
    "calendar_id": "cal_54321"
  }
}

PII masking rules:

  • User email addresses are masked as user***@domain.com
  • Full names are truncated to first character: J***n Doe
  • Phone numbers are masked as +1 (555) ***-****

Distributed Tracing Instrumentation

OpenTelemetry is used for distributed tracing with custom spans:

Key Operations to Trace:

  • calendar.event.create - span for event creation flow
  • calendar.event.update - span for event update flow
  • calendar.event.conflict.check - span for conflict detection
  • calendar.event.notification.send - span for notification publishing

Span Attributes:

// Example in Java using OpenTelemetry SDK
Span span = tracer.spanBuilder("calendar.event.create")
    .setAttribute("event.id", event.getId())
    .setAttribute("user.id", userId)
    .setAttribute("calendar.id", calendarId)
    .setAttribute("event.duration.minutes", durationMinutes)
    .startSpan();

Metrics Design

RED Method Metrics (Rate, Errors, Duration):

Metric NameTypeLabelsDescription
calendar.event.createdCounterevent_type, user_roleTotal events created
calendar.event.update.failedCountererror_codeFailed event updates
calendar.event.conflict.durationHistogramevent_typeConflict check latency
calendar.event.notification.sentCounternotification_type, statusNotification send success/failure
calendar.event.processing.durationHistogramoperation, statusEnd-to-end event processing time
calendar.event.concurrent.requestsGaugeoperationCurrent concurrent request count

Example Histogram Configuration:

// Java Micrometer metrics example
private final Timer processingTimer = Timer.builder("calendar.event.processing.duration")
    .tag("operation", "create")
    .tag("status", "success")
    .register(meterRegistry);

private final Counter conflictCounter = Counter.builder("calendar.event.conflict.check")
    .tag("event_type", "meeting")
    .register(meterRegistry);

Health Check Endpoint Design

Endpoints with their checks:

EndpointCheck PerformedExpected Response
/healthService readiness, DB connectivity, cache availability{"status":"healthy","details":{"db":"ok","cache":"ok"}}
/readyAll dependencies ready, DB migrations applied, queue consumers healthy{"status":"ready","components":{"db":"healthy","message_queue":"healthy"}}
/liveProcess is alive, memory usage under threshold{"status":"alive","uptime_seconds":3600}
/startupService has completed startup sequence{"status":"complete","startup_time_ms":1250}

Alerting Thresholds

Critical Alerts (require on-call escalation):

MetricThresholdAlert LevelEscalation
calendar.event.update.failed rate (5m)> 10/minuteCriticalOn-call engineer
calendar.event.processing.duration p99> 5000msCriticalOn-call engineer
calendar.event.conflict.duration p99> 1000msWarningTeam alerting
calendar.event.notification.sent rate (1h)< 50% successWarningTeam alerting

Warning Alerts (team notification):

MetricThresholdAlert Level
calendar.event.processing.duration p95> 2000msWarning
calendar.event.concurrent.requests> 1000 concurrentWarning
calendar.event.conflict.check rate (5m)> 100/minuteWarning

Alerting Configuration Example:

# Prometheus alert rules example
groups:
- name: calendar-service-alerts
  rules:
  - alert: CalendarEventUpdateFailureRate
    expr: rate(calendar_event_update_failed[5m]) > 0.1
    for: 2m
    labels:
      severity: critical
    annotations:
      summary: "High event update failure rate"
      description: "Event updates failing at {{ value }} per minute"

14. Open Questions, Risks & Trade-offs

Decisions requiring product / engineering alignment

  • Timezone handling strategy: Should we store all times in UTC and convert to user timezone at display, or should we store user's local time with timezone offset? This decision affects data model complexity and API surface.
  • Event recurrence pattern complexity: How granular should recurrence patterns be? Should we support complex patterns like "last Monday of month" or keep it simple with basic daily/weekly/monthly patterns?
  • Resource booking vs. time slot allocation: Should we support multiple resource types (meeting rooms, equipment) or keep it simple with single-user time slots?
  • Notification delivery strategy: Should we implement push notifications, email, or SMS, and how should we handle notification failures?

Known trade-offs and justifications

Trade-offOption 1Option 2Justification
Event Duration vs. RecurrenceFixed duration eventsFlexible duration with recurrenceFixed duration simplifies scheduling logic and reduces complexity in conflict detection, but limits flexibility for recurring meetings
Database Schema vs. DenormalizationNormalized schema with joinsDenormalized for performanceNormalized provides data consistency and reduces storage overhead, but denormalization improves read performance for common queries
Real-time vs. Batch UpdatesReal-time updates via WebSocketsBatch updates with pollingWebSocket provides better UX but increases infrastructure complexity and memory usage
Event Ownership ModelSingle owner per eventMultiple owners with permissionsSingle owner simplifies access control but limits collaboration features

Risks and mitigation strategies

Risk: Calendar conflicts and scheduling collisions

  • Mitigation: Implement optimistic locking with conflict detection, provide user-friendly conflict resolution UI, and maintain a global event conflict cache

Risk: High concurrent event creation/deletion

  • Mitigation: Use distributed locks for critical sections, implement batch operations for bulk scheduling, and utilize read-replicas for high-volume reads

Risk: Data consistency in distributed systems

  • Mitigation: Implement Saga pattern for multi-step operations, use outbox pattern for reliable event publishing, and maintain strong consistency at the transaction boundary

Risk: Performance degradation under high load

  • Mitigation: Implement comprehensive caching strategy, use database connection pooling, and design with horizontal scaling in mind

Technical debt acknowledged

  • Event recurrence engine complexity: The current design assumes simple recurrence patterns, but complex patterns like "last Friday of the month" will require significant refactoring and additional computational overhead
  • Timezone handling limitations: The current implementation assumes all times are in UTC, but proper timezone support requires extensive testing and handling of edge cases like DST transitions
  • Notification system limitations: The current design doesn't account for notification delivery failures or retry mechanisms, which could lead to missed notifications in production
  • Event search and filtering complexity: As the system scales, complex search queries (e.g., "all events in the next 30 days with specific attendees") may require additional indexing strategies and potentially introduce performance bottlenecks

Was this article helpful?

Topics in this article

More from Backend LLD

Stay in the Loop

Get curated DSA patterns, coding problems, frontend and backend LLD/HLD, and AI/ML System Design insights delivered straight to your inbox. No spam, just focused interview and engineering content.