Пять вещей, которые нужно учесть, продумывая архитектуру интернет‑магазина

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

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

Давайте рассмотрим, что стоит сразу же учесть при обсуждении проекта интернет-магазина.

Предположим, что интернет-магазин вы собираетесь писать на Python/Django.

א. Хранение данных о товарах

Во многих интернет-магазинах продаются товары, значительно отличающиеся друг от друга. Например, у электрочайников задаётся один набор параметров, у фаллоимитаторов второй, а у хны для менди — третий. При этом все эти товары могут продаваться на одном и том же сайте. Или, что ещё более вероятно — на разных сайтах, использующих одну и ту же БД с товарами (клиент, возможно, хочет иметь возможность управлять всеми товарами всех своих магазинов из общего административного интерфейса).

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

Можно использовать MongoDB (или другую нереляционную СУБД, на выбор). Тут много вариантов (это часто обсуждается, например, вот одна из тем на softwaremaniacs) — некоторые из них предполагают использование языка запросов Mongo, некоторые оставляют возможность использовать ORM (впрочем, встречается мнение, что это — сомнительное решение). Разумеется, тут нужны некоторые танцы с бубном, чтобы добавить управление этими данными в админку. В целом задача не очень сложная, но может быть достаточно много своих нюансов, поэтому срок выполнения заранее предсказать довольно сложно.

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

Другой вариант — наоборот использовать всё максимально стандартное для Django. В частности, есть django‑eav. Это как раз решение, позволяющее добавлять динамический набор полей для объектов в модели (разумеется, данные при этом не хранятся в одной таблице — БД-то остаётся реляционной).

В данном случае есть и готовая интеграция с админкой.

Но не всё так гладко: во-первых, этот вариант не очень хороший с точки зрения производительности (то есть сразу усложняем масштабирование системы — так что это в большей степени подходит тогда, когда высокие нагрузки не ожидаются), а во-вторых, могут быть дополнительные сложности при реализации многоязычности.

А вообще Иван Сагалаев пишет про EAV следующее:

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

Поэтому, если вам нужен EAV, то никакое наследование не поможет. С другой стороны, пользоваться такой системой не очень удобно. Ее не поддерживает ORM в явном виде, а да и чисто по построению строить запросы к таким таблицам более сложно и работают они медленней. И вот поэтому, я давно считаю, что если ваша система требует такого подхода, то это по сути означает, что ей неудобно пользоваться реляционным хранилищем. И будет лучше посмотреть в сторону документоориентированных БД (CouchDB в качестве самого яркого примера). Хотя это требует большого усилия по изменению сознания и отказа от реляционных практик.

Были ли разговоры об использовании Django с MongoDB для этих целей? В общем, да. В ответе пользователя Alix Axel есть неплохие ссылки (например, на вот эту презентацию), которые раскрывают особенности использования MongoDB для электронной торговли. Тем не менее, конкретных решений для Django там не приводится.

Вот ещё немного про использование EAV, а также hstore (это вариант хранения динамического набора значений для PostgreSQL):

 

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

— Grigory Fateyev о hstore, отсюда.

В интернете можно найти много информации о различных способах реализации динамических полей. Например, вотещё одна дискуссия на Stackoverflow.

ב. Многоязычность

Для реализации многоязычности содержимого — в данном случае речь, в частности, о переводе на разные языки параметров, которые задаются у товаров — есть различные решения. Впрочем, в зависимости от того, какой подход для хранения данных вы собираетесь использовать, эти приложения могут не подойти (хотя можно попробовать адаптировать какое-либо из них).

ג. История правок

Во многих случаях для клиента может быть весьма полезна история правок для каждого товара (например, чтобы восстанавливать что-нибудь, если не так отредактировали).

С реляционной БД такое делается с помощью одного из готовых приложений.

Например, django‑reversion может отображать diff с помощью django‑reversion-compare (прокрутите страницу вниз — там есть скриншоты).

Ещё есть django‑history — работа разработчика-уфимца ildus. А вот обзор django‑reversion, django‑revisions и django‑simple‑history.

Если же использовать нереляционную БД, то тут, конечно, немного сложнее. Хотя вообще динамический набор полей + динамический набор переводов — весьма веские аргументы в пользу использования Mongo, мне кажется. В конце концов, она ведь для того и существует, чтобы хранить в ней подобные «вкладываемые» данные (плюс появляется возможность удобно группировать значения!). Единственный контраргумент тут, наверно — возможная сложность интеграции с существующими ecommerce-решениями. С другой стороны, с этим сложности в любом случае будут при разработке любого относительно сложного проекта, полностью их пока что не обойти.

ד. Категории товаров

В большинстве случаев для товаров требуются древовидные категории: то есть у каждой категории могут быть не только товары, но и несколько уровней вложенности (например, Аудио → Наушники → Открытые).

Кстати, вот хорошее обсуждение реализации такой иерархии объектов в Django (помимо упоминания django‑mpttтам есть ещё немного хороших советов).

ה. API

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

Наработки сообщества

Давайте посмотрим, что можно использовать, чтобы сэкономить время.

Сравнения и обзоры:

Также есть блог на эту тему.

Ещё на Хабре есть статья про Oscar.

В целом, меньше всего ругают Satchless и LFS, а значит можно предположить, что они самые приличные. Впрочем, самые приличные ≠ приличные в принципе, к сожалению: на практике и в том, и в другом случае вы с большой вероятностью в итоге получите собственный форк, и мерджить обновления из официального репозитория станет непросто (а может быть и вовсе не нужно — но тогда надо следить, нет ли в их коммитах исправлений безопасности, которые могут также затрагивать и ваше ответвление).

А ещё так уж получается, что авторы Satchless вообще не в восторге от идеи динамических полей для товаров.

С древовидной структурой категорий в Satchless, судя по всему, всё в порядке. Но это вполне ожидаемо для любого подобного решения, так как это очень востребовано.

Что касается LFS (Lightning Fast Shop) — то тут из коробки есть и древовидные категории, и своего рода EAV (но не многоязычность). Впрочем, реализовано это так, что использовать подобное на большом проекте я бы поостерёгся.

Кстати, создателям Satchless не нравится LFS, потому что они считают его не слишком модульным.

На практике, возможно, самым быстрым решением, которое ведёт к наименьшему количеству хаков, является вообще написание своего магазина на основе отдельных готовых компонентов (например, взять готовую систему оплаты, для которой уже есть несколько бэкэндов и возможность достаточно легко писать новые). В конце концов, если этих компонентов нет в виде отдельных приложений, то можно посмотреть, насколько тесно они интегрированы с Satchless или LFS, и, если будет видно, что не очень сильно, то можно попробовать вытащить их оттуда.

Читателю, я думаю, очевидно: если написать своё собственное решение, в котором решаются сразу все описанные выше проблемы, а затем выложить его в Интернете, то оно, скорее всего, быстро получит широкое распространение.