Введение

Проектирование интерфейсной системы — важный навык для разработчиков, стремящихся создавать надежные и масштабируемые приложения. В этой статье рассматривается дизайн внешней системы приложения, похожего на Календарь Google. Мы проведем вас через весь процесс, уделяя особое внимание сбору требований, проектированию архитектуры высокого уровня, проектированию API, управлению крайними случаями, моделированию клиентских данных и стратегиям оптимизации.

Подготовьтесь к собеседованию с FrontendLead.com

Календарь Google — это лишь один из многих вопросов по дизайну интерфейсной системы, которые вам могут задать. На сайте frontendlead.com у нас есть полный список наиболее часто задаваемых вопросов по интерфейсной части на рынке, включая видеорешения. Только в течение ограниченного времени мы предлагаем скидку 20 % на все планы с использованием кода скидки 20OFF.

Требования

Функциональные требования

  1. Просмотр событий. Пользователи должны иметь возможность просматривать события в ежедневном, еженедельном и ежемесячном форматах.
  2. Разрешение конфликтов. Приложение должно обнаруживать и предоставлять варианты разрешения конфликтующих событий.
  3. Создание новых событий. Пользователи должны иметь возможность создавать новые события.

Подробное описание разрешения конфликтов. Разрешение конфликтов включает в себя обнаружение перекрывающихся событий и предоставление пользователям возможности корректировать время событий во избежание конфликтов. Для выявления таких случаев мы разработаем алгоритм обнаружения конфликтов.

Нефункциональные требования

  1. Наблюдаемость: обеспечьте механизмы ведения журналов для отслеживания работоспособности и производительности системы, принимая во внимание финансовые последствия хранения журналов.
  2. Поддержка устройств: приложение должно быть совместимо с различными устройствами, включая настольные компьютеры, планшеты и смартфоны.
  3. Демография и доступность. Дизайн должен быть инклюзивным, ориентированным на разнообразную базу пользователей, с такими функциями, как программы чтения с экрана и навигация с помощью клавиатуры для обеспечения доступности.
  4. Производительность. Обеспечьте быструю загрузку и быстроту реагирования, чтобы обеспечить удобство работы с пользователем.

2. Проектирование архитектуры высокого уровня

Архитектура модель-представление-контроллер (MVC)

В архитектуре MVC приложение разделено на три взаимосвязанных компонента:

  1. Модель: представляет уровень данных приложения. Он взаимодействует с базой данных и обновляет представление при каждом изменении данных.
  2. Представление: представляет пользовательский интерфейс и представление данных. Он отображает данные модели пользователю и отправляет пользовательские команды контроллеру.
  3. Контроллер: взаимодействует как с моделью, так и с представлением. Он принимает вводимые пользователем данные из представления, обрабатывает их (с возможными обновлениями модели) и возвращает выходные данные.

Проектирование компонентов высокого уровня и поток данных

  • Клиентская сторона (браузер):Компоненты просмотра:Вид календаря: отображает календарь в ежедневном, еженедельном или ежемесячном представлениях.
  • Просмотр событий: отображает подробную информацию о выбранном событии.
  • Модальное создание событий: позволяет пользователям создавать новые события.
  • Серверная часть: Компоненты контроллера: Контроллер событий: управляет созданием, просмотром и редактированием событий.
  • Контроллер разрешения конфликтов: управляет обнаружением и разрешением конфликтующих событий.
  • База данных:Компоненты модели:Модель события: хранит сведения о событии, такие как название, дата, время, место и участники.
  • Модель пользователя: хранит информацию о пользователе, предпочтениях и ассоциациях событий.

Поток данных:

  1. Представление отправляет пользовательский запрос контроллеру.
  2. Контроллер взаимодействует с моделью для получения/обновления данных на основе запроса пользователя.
  3. Модель взаимодействует с базой данных для выполнения операций CRUD (создание, чтение, обновление, удаление).
  4. Модель возвращает данные контроллеру.
  5. Контроллер обновляет представление на основе данных модели.
  6. Представление отображает обновленные данные пользователю.

Диаграммы скоро появятся.

3. Дизайн API

Выбор протокола

Для приложения Календарь Google мы в первую очередь будем использовать HTTP/HTTPS в качестве протокола для связи запрос-ответ между клиентом и сервером. Вот причины и ситуации, в которых могут использоваться разные методы:

  1. HTTP/HTTPS (REST): для основных операций CRUD, таких как создание, чтение, обновление или удаление событий. Он не имеет состояния, и каждый запрос от клиента к серверу должен содержать всю информацию, необходимую серверу для выполнения этого запроса (например, данные аутентификации пользователя).
  2. WebSockets: это можно использовать для обновления календаря в реальном времени. Когда событие добавляется/обновляется одним пользователем, оно может отображаться в режиме реального времени для других пользователей, просматривающих тот же календарь (если это общий календарь).
  3. Длинный опрос: в ситуациях, когда обновления в реальном времени не являются критическими, мы могли бы использовать длинный опрос, чтобы уменьшить количество HTTP-запросов, отправляемых на сервер. Эту оптимизацию следует учитывать с учетом конкретных требований и пользовательской базы приложения.

Учитывая, что нам не приходится иметь дело с режимом реального времени, мы выберем HTTP-маршрут.

Конечные точки

  1. Получить все события
  • Конечная точка: GET /events
  • Ответ:
{
  "status": "success",
  "data": [
    {
      "eventId": "12345",
      "title": "Meeting with Bob",
      "date": "2023-09-30",
      "time": "10:00 AM",
      "location": "Room 301",
      "attendees": ["[email protected]", "[email protected]"]
    },
    {
      "eventId": "12346",
      "title": "Project Presentation",
      "date": "2023-10-01",
      "time": "02:00 PM",
      "location": "Conference Room",
      "attendees": ["[email protected]"]
    }
  ]
}
  1. Обновить существующее событие
  • Конечная точка: PUT /events/:eventId
  • Тело запроса:
{
  "title": "Meeting with Bob and Alice",
  "date": "2023-09-30",
  "time": "11:00 AM",
  "location": "Room 302",
  "attendees": ["[email protected]", "[email protected]"]
}
  • Ответ:
{
  "status": "success",
  "data": {
    "eventId": "12345",
    "title": "Meeting with Bob and Alice",
    "date": "2023-09-30",
    "time": "11:00 AM",
    "location": "Room 302",
    "attendees": ["[email protected]", "[email protected]"]
  }
}
  1. Удалить событие (вне области действия)
  • Конечная точка: DELETE /events/:eventId
  • Ответ:
{
  "status": "success",
  "message": "Event deleted successfully"
}
  1. Получить конфликтующие события
  • Конечная точка: GET /events/conflicts
  • Ответ:
{
  "status": "success",
  "data": [
    {
      "eventId": "12345",
      "conflictingEventId": "12347",
      "date": "2023-09-30",
      "time": "10:00 AM to 11:00 AM"
    }
  ]
}
  1. Разрешение конфликтующего события
  • Конечная точка: PUT /events/conflicts/:eventId
  • Тело запроса:
{
  "resolutionAction": "reschedule",
  "newDate": "2023-10-01",
  "newTime": "09:00 AM"
}
  • Ответ:
{
  "status": "success",
  "message": "Conflict resolved successfully"
}

Обнаружение конфликтов

Этап обнаружения конфликтов — решающий шаг в обеспечении бесперебойной работы календаря. Он определяет перекрывающиеся события, которые могут быть потенциальной причиной конфликтов планирования. Вот как это можно разобрать шаг за шагом с примерами:

Шаг 1: Определите детали события

Объяснение: Когда пользователь создает или обновляет мероприятие, система собирает все необходимые сведения, связанные с мероприятием, такие как дата мероприятия, время начала, время окончания и список участников.

Пример. Пользователь пытается запланировать собрание «Начало проекта» на 20 сентября с 10:00 до 11:00 с участниками A, B и C.

Шаг 2. Проверьте существующие события для участников

Объяснение: Затем система сопоставляет предложенный временной интервал с существующими событиями в календаре для всех перечисленных участников. Это помогает выявить любые перекрывающиеся события.

Пример. Система проверяет календари участников A, B и C на предмет любых событий, запланированных с 10:00 до 11:00 20 сентября.

Шаг 3: Анализ конфликта

Объяснение: Если обнаружено перекрывающееся событие, оно помечается как конфликт. Система учитывает различные параметры, такие как точное совпадение во времени, участвующие участники и характер конфликтного события (будь то одноразовое событие или часть повторяющейся серии).

Пример. Система обнаруживает, что у участника Б запланирована «Встреча с клиентом» с 10:30 до 11:30 в тот же день, что противоречит предлагаемому совещанию «Начало проекта».

Шаг 4. Детальное обнаружение конфликтов

Объяснение: В более сложных системах обнаружение конфликтов может быть более детальным, учитывая еще больше аспектов, таких как место проведения события (в случае физических встреч), можно ли перенести конфликтное событие или нет и т. д.

Пример: Система отмечает, что «Встреча с клиентом» для участника Б находится в другом месте, и ее перемещение может оказаться невозможным из-за логистических ограничений.

Шаг 5: Подготовка уведомления о конфликте

Объяснение: Как только конфликт обнаружен, система готовится уведомить пользователя о конфликте, предоставляя подробную информацию о конфликтующих событиях, в том числе о том, кто в нем участвует и каковы перекрывающиеся временные интервалы.

Пример: система подготавливает уведомление, чтобы информировать пользователя о том, что существует конфликт с «Встречей с клиентом» участника Б с 10:30 до 11:30, предлагая потенциальные решения, такие как перенос встречи «Начало проекта» или поиск другого временной интервал, подходящий всем участникам.

Пример кода:

ЯВАСКРИПТ

function sortEventsByStartTime(events) {
    // Sorts the events by start time in ascending order
    return events.sort((a, b) => new Date(a.startTime) - new Date(b.startTime));
}

function detectConflicts(events) {
    // Initialize an empty list to store conflict groups
    let conflictGroups = [];

    // Helper function to add events to conflict groups
    function addToConflictGroup(event1, event2) {
        for (let group of conflictGroups) {
            if (group.has(event1) || group.has(event2)) {
                group.add(event1);
                group.add(event2);
                return;
            }
        }
        // If neither event is in a conflict group, create a new group
        let newGroup = new Set([event1, event2]);
        conflictGroups.push(newGroup);
    }

    // Sort events by start time
    let sortedEvents = sortEventsByStartTime(events);

    // Iterate over the sorted events and check for conflicts
    for (let i = 0; i < sortedEvents.length - 1; i++) {
        let currentEvent = sortedEvents[i];
        let nextEvent = sortedEvents[i + 1];

        // Check if the current event ends after the next event starts
        if (new Date(currentEvent.endTime) > new Date(nextEvent.startTime)) {
            // A conflict is found, add the events to a conflict group
            addToConflictGroup(currentEvent, nextEvent);
        }
    }

    // Return the list of conflict groups
    return conflictGroups;
}

// Usage:
let events = [
    { startTime: '2023-09-20T09:30:00', endTime: '2023-09-20T10:30:00', attendees: ['B'] },
    { startTime: '2023-09-20T10:00:00', endTime: '2023-09-20T11:00:00', attendees: ['A', 'B', 'C'] },
    { startTime: '2023-09-20T10:30:00', endTime: '2023-09-20T11:30:00', attendees: ['C'] }
];

console.log(detectConflicts(events));

Краевые случаи

Обработка пограничных случаев является важной частью проектирования системы, обеспечивающей устойчивое и надежное приложение. Вот некоторые потенциальные крайние случаи и способы их обработки:

Одновременное создание событий:

  1. Сценарий: два или более пользователей пытаются создать события, которые перекрываются во времени и включают одних и тех же участников.
  2. Решение: реализовать оптимистичный механизм управления параллелизмом. При попытке создания события проверьте наличие конфликтов и уведомите пользователя, если они существуют.

Большое количество участников:

  1. Сценарий: создается событие с большим количеством участников, что потенциально может вызвать проблемы с производительностью при проверке конфликтов или отправке уведомлений.
  2. Решение. Внедрите пакетную обработку таких операций, как проверка конфликтов и уведомления, возможно, в фоновом задании, чтобы избежать блокировки основного потока приложения.

Разница во времени:

  1. Сценарий: участники находятся в разных часовых поясах, что может вызвать путаницу относительно времени проведения мероприятий.
  2. Решение. Сохраняйте время в универсальном формате (например, UTC) и преобразуйте его в местный часовой пояс каждого участника при отображении сведений о мероприятии или отправке уведомлений.

Неверный или неправильный ввод данных:

  1. Сценарий: пользователь вводит неверные или искаженные данные при создании или обновлении события (например, неверный формат даты).
  2. Решение. Внедрите надежную проверку ввода для выявления недействительных или искаженных данных и предоставления пользователю четких сообщений об ошибках.

Невозможность отправки уведомлений:

  1. Сценарий: Система не может отправлять уведомления (из-за проблем с сетью, простоя сторонних служб и т. д.).
  2. Решение. Внедрите механизм повтора для неудачных уведомлений, возможно, в сочетании с системой журналирования для отслеживания неудачных попыток уведомления и принятия соответствующих мер.

Модель данных клиента

Модель данных клиента — это, по сути, то, как мы структурируем данные и управляем ими на стороне клиента. Это включает в себя создание структур данных для хранения событий, управление получением данных с сервера и реализацию механизмов обновления данных при возникновении изменений. Здесь я подробно расскажу, как мы могли бы построить клиентское хранилище с учетом производительности, потенциальных механизмов кэширования и нормализации.

Структура клиентского магазина

У нас будет централизованное хранилище (например, хранилище Redux или Vuex) для хранения и управления данными приложения. Структура магазина будет выглядеть примерно так:

{
  events: [],  // Array to store event objects
  users: [],   // Array to store user objects (attendees)
  views: {     // Object to store current view settings (day, week, month)
    currentView: 'week',
  },
  filters: {}, // Object to store any active filters (like showing only certain calendars)
}

Вопросы производительности

Чтобы повысить производительность, особенно при работе с большим количеством событий, нам необходимо рассмотреть такие методы, как:

  1. Отложенная загрузка: сначала загружайте только часть событий, а затем загружайте больше по мере необходимости.
  2. Индексированный поиск: создавайте индексированные поисковые запросы по событиям на основе дат или идентификаторов пользователей, чтобы быстро находить соответствующие события.

Механизмы кэширования

Чтобы свести к минимуму количество запросов к серверу и улучшить взаимодействие с пользователем, мы могли бы рассмотреть:

  1. Service Workers: внедрение сервисных работников для локального кэширования ответов сервера.
  2. Сохранение состояния: сохранение клиентского хранилища локально (например, локальное хранилище), чтобы оно было доступно даже после перезагрузки страницы.

Нормализация

Нормализация структуры данных помогает минимизировать избыточность данных и поддерживать согласованность данных. Например:

  1. Участники событий. Вместо хранения сведений об участниках непосредственно в объектах событий мы можем хранить объекты пользователей (посетителей) отдельно и ссылаться на них по идентификатору в объектах событий.

Оптимизации

На этапе оптимизации мы изучим различные методы и стратегии для повышения производительности, доступности, интернационализации приложения и общего пользовательского опыта. Здесь я опишу потенциальные оптимизации, которые можно реализовать в приложении Календарь Google.

1 Виртуализация

Определение: Виртуализация — это метод, при котором мы отображаем только подмножество элементов списка, которые в данный момент видны в области просмотра, что значительно повышает производительность при работе с большими списками данных.

Реализация в календаре:

  1. Виртуализация списка событий. При отображении списка событий за день/неделю/месяц используйте виртуализированный список, чтобы отображать только те события, которые просматриваются в данный момент.
  2. Виртуализация сетки календаря. Аналогичным образом, при отображении сетки календаря визуализируются только те ячейки, которые в данный момент видны.

Устранение дребезга/дроссель/ограничение скорости

Определение. Это методы ограничения количества запросов или выполнения функций за определенный период, повышения производительности и предотвращения потенциальных перегрузок сервера.

Реализация в календаре:

  1. Устранение дребезжания: реализуйте устранение дребезжания в функции поиска событий, чтобы ограничить количество запросов к серверу, пока пользователь печатает.
  2. Ограничение скорости: Ограничьте частоту запросов API, чтобы предотвратить перегрузку сервера, особенно во время массовых обновлений или загрузок событий.

Производительность

Дальнейшая оптимизация производительности может включать в себя:

  1. Минимизация времени рендеринга на стороне клиента: использование таких методов, как запоминание, для предотвращения ненужного повторного рендеринга.
  2. Оптимизация ответов сервера. Уменьшение размера ответов сервера с помощью таких методов, как разбивка данных на страницы.

Доступность

Крайне важно обеспечить доступность приложения для всех пользователей. Стратегии здесь могут включать:

  1. Навигация с помощью клавиатуры: обеспечение доступа ко всем функциям с помощью клавиатуры.
  2. Поддержка программ чтения с экрана: реализация правильных атрибутов ARIA для поддержки программ чтения с экрана.

Интернационализация

Для поддержки глобальной базы пользователей мы реализуем такие функции интернационализации, как:

  1. Языковая поддержка: предоставление пользователям возможности выбирать предпочитаемый язык.
  2. Поддержка часового пояса: отображение даты и времени в соответствии с часовым поясом пользователя.

Дополнительные оптимизации

Другими потенциальными оптимизациями могут быть:

  1. Адаптивный дизайн: обеспечение хорошей работы приложения и его внешнего вида на всех типах устройств, включая мобильные телефоны и планшеты.
  2. Офлайн-поддержка: реализация сервис-воркеров для автономного использования и синхронизации после восстановления соединения.

Бонус — обработка больших объемов данных с сервера (пагинация)

Когда сервер возвращает значительное количество событий, это потенциально может привести к проблемам с производительностью, включая увеличение времени загрузки и снижение скорости реагирования. Вот некоторые стратегии и обновления нашего дизайна API для эффективного управления этим:

1. Пагинация данных

Объяснение: Разбиение на страницы данных предполагает разбиение данных на более мелкие фрагменты и загрузку их по мере необходимости, а не получение всех событий одновременно.

Реализация в API:

  • Конечная точка: GET /events?page=1&limit=100. Эта конечная точка позволит получать события на страницах с заданным количеством событий на странице.
  • На стороне клиента: реализуйте механизм отложенной загрузки в клиентском приложении, чтобы загружать больше событий по мере прокрутки пользователем, обеспечивая плавность работы пользователя.

2. Фильтрация данных

Объяснение: Внедрите параметры фильтрации в API, чтобы клиент мог запрашивать только необходимые данные на основе различных параметров, таких как диапазон времени, тип события и т. д.

Реализация в API:

  • Конечная точка: GET /events?startDate=2023-01-01&endDate=2023-01-31 – будут получены события в пределах указанного диапазона дат, что уменьшит объем данных, получаемых за один запрос.

3. Сжатие данных

Объяснение: Использование методов сжатия данных для уменьшения размера данных, передаваемых между сервером и клиентом.

Реализация в API:

  • Включите сжатие gzip или Brotli на сервере, чтобы уменьшить размер ответов API.

4. Веб-сокеты для обновлений в реальном времени

Объяснение: Для более эффективного управления обновлениями в реальном времени, особенно при работе с большим объемом данных.

Реализация в API:

  • Используйте WebSockets для отправки обновлений клиенту, избегая необходимости многократно получать весь набор данных.

Реализуя эти стратегии, мы можем гарантировать, что приложение останется отзывчивым и обеспечит бесперебойную работу пользователя даже при обработке большого объема событий.

Если эта статья оказалась для вас полезной, поставьте ей лайк и посетите frontendlead.com, где у нас есть подробные видеоролики и статьи, освещающие наиболее часто задаваемые вопросы о интерфейсах на рынке. Только в течение ограниченного времени мы предлагаем скидку 20 % на все планы с использованием кода скидки 20OFF.