Permalink
Find file
ba524c2 Jul 24, 2016
executable file 142 lines (76 sloc) 14.8 KB

Задачка на создание SPA приложения

  • сложность: средняя
  • требуемые знания: JS, DOM, HTML/CSS, один из серверных языков
  • время: 2-3 месяца

SPA значит «Single Page Application», то есть приложение, работающее в браузере без перезагрузки страницы. Это позволяет сделать работу с приложением более удобной для пользователя, а также реализовать поддержку оффлайн-режима (работы при временном отсутствии соединения с интернетом).

Постановка задачи

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

Вся информация хранится на сервере, и параллельно может работать много экземпляров приложений.

Вот как выглядят основные экраны приложения (посмотреть в высоком SVG качестве):

внешний вид приложения

Архитектура

Если ты следишь за миром JS, то наверно знаешь что в последние несколько лет появилось немало фреймворков и библиотек для создания SPA:

  • Backbone - предоставляет роутер и основу для написания моделей, контроллеров и вью. Реализует события и подписку на них для моделей, но не предоставляет готовой реализации view.
  • Knockout - шаблонизатор, реализующий подход data binding
  • Angular - фреймворк для разработки SPA, содержащий в себе все нужные компоненты, начиная с роутера и заканчивая шаблонизатором с data binding.

Таким образом, у тебя есть выбор из как минимум:

  • backbone + knockout
  • angular (тебе придется ковырять исходники и документацию ангулар, уроков уровня hello world недостаточно)

Ссылки

При переходе по страницам приложения должен меняться URL. Это нужно для функционирования истории в браузере, работы кнопок «назад» и «обновить страницу», чтобы можно было отправить ссылку на конкретную страницу.

Для этого стоит использовать HTML 5 History API.

Сервер и API

Данные хранятся на сервере, я рекомендую использовать реляционную SQL базу данных для этого. Сервер также занимается аутентификацией пользователей, проверкой доступа, проверкой правильности переданной информации. Необходимо реализовать серверное API, через которое приложение будет обменивать данными с сервером. Есть как минимум такие варианты:

  • HTTP REST API
  • JSON-RPC

Для повышения скорости обмена данными можно применять трюки вроде группировки запросов.

Хорошо бы генерировать документацию для API. В случае REST это можно сделать инструментом вроде Swagger.

Реализовать серверную часть можно на любом языке. Например, на PHP с использованием микрофреймворков Silex или Slim, или на Node.JS (платформа для реализации сервера на яваскрипте).

В валидации данных возможно тебе чем-то поможет стандарт json-schema.

Мониторинг ошибок

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

Также, все произошедшие на клиенте ошибки должны логгироваться на сервере (как иначе разработчик узнает о них?). Достаточно просто писать их в лог-файл, с подробностями ошибки, стек-трейсом (при наличии), id пользователя, времени прошедшем от запуска приложения.

Нужно логгировать версию клиента, так как при частых релизах активными могут быть несколько версий приложений-клиентов.

Сохранность данных

Приложение должно приложить максимум усилий к сохранению введенных пользователем данных. Потеря связи с сервером или выключение питания устройства не должно приводить к потере введенных данных.

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

Надо подумать что делать в случае возникновения конфликтов, попытки внести 2 разными клиентами противоречащие друг другу изменения.

Оффлайн-режим

В случае отсутствия связи с сервером приложение должно сохранять введенные данные в локальном хранилище для последующей отправки их на сервер. Там же можно кешировать ранее полученные с сервера данные. Надо понимать что приложение будет использоваться на мобильных устройствах с ненадежной связью.

Приложение должно реализовать наиболее комфортную работу с данными в таких условиях.

Возможно также стоит использовать HTML5 app manifest, который позволяет браузеру загружать приложение из кеша при отсутствии сети.

Отладочная и продакшен версия

При разработке удобно хранить код в большом количестве небольших файлов. И сделать так, что при перезагрузке страницы все изменения становятся видны сразу. Но это неэффективно для боевой версии приложения, так как оно будет медленно загружаться. Потому стоит настроить систему сборки (assets pipeline) которая будет собирать все в один файл для продакшена.

Для этого удобно использовать инструмент gulp, написанный на яваскрипте.

Сторонние библиотеки нужно подключать через менеджер пакетов вроде bower.

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

  • AMD (asynchronous modules definition)
  • Common.JS

Соответственно есть сборщики и загрузчики этих модулей. Один их последних это webpack, поддерживающий по моему оба формата.

Изоморфность

Изоморфным называют код, который может выполняться и на клиенте и на сервере. При разработке SPA мы можем натолкнуться на проблему, как сделать наш код изоморфным. Вот зачем это нужно:

  • нам нужны одинаковые модели на клиенте и на сервере
  • иногда нам нужно иметь возможность генерировать страницы на сервере (в этой задаче не нужно)
  • нам нужны правила валидации данных и на модели и на сервере

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

Версионирование API, версионирование приложения, миграции

Со временем серверное API будет меняться. Но что если старый клиент попробует обратиться к такому API? Очевидно, может произойти ошибка или, что хуже, данные потеряются. Чтобы этого избежать, можно применить версионирование API, явно добавив версию API в URL:

http://api.example.com/v1/marks/

Посл выхода новой версии API можно отдавать старым клиентам уведомление о том что они устарели.

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

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

При этом стоит помнить о таких вещах:

  • миграция может быть продолжительной и прерваться (пользователь закрыл браузер, компьютер завис). Стоит писать миграции так, чтобы они не разрушали какие-то данные и их можно было без ущерба перезапустить заново. Например, вместо изменения таблицы можно создавать новую, а затем атомарно переименовывать ее, заменяя старую. Или вместо удаления данных только добавлять их.
  • пользователь может открыть несколько копий приложения в нескольких вкладках. Что если они одновременно запустят процесс миграции, не будет ли конфликтов? Бороться с этим можно взятием блокировки на время миграции.

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

Тесты

Хорошо бы покрыть приложение или его компоненты автоматическими тестами

Поддержка разной плотности пикселей

Для этого можно либо использовать векторные SVG-картинки либо генерировать иконки в нескольких разрешениях.

Анимация

Анимация при правильном использовании помогает сконцентрировать внимание пользователя на нужном элементе интерфейса или проиллюстрировать происходящее событие.

Проверка возможностей браузера

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

Ссылки