Как построить интернет-магазин на Spree: от монолита до headless
Заказчик попросил добавить акцию «купи 3, четвёртый в подарок» с проверкой по истории заказов. В Shopify это потребовало бы стороннего приложения с отдельной подпиской, в Spree — 50 строк декоратора. Spree Commerce — это Rails Engine с открытым кодом, который мы используем для проектов с глубокой кастомизацией. В статье покажем, как настроить монолитную или headless-архитектуру, и разберём реальные примеры.
Почему выбирают Spree: монолит или headless?
Spree встраивается в Rails-приложение как Engine и работает с той же базой данных. Начиная с версии 4.3 добавился headless-режим через REST API v2, что позволяет использовать Spree как backend для React/Vue фронтенда. Это даёт возможность строить как простые магазины с серверным рендерингом, так и сложные SPA. Рассмотрим оба варианта.
| Критерий | Monolith (классический) | Headless |
|---|---|---|
| Архитектура | Rails Engine внутри приложения, storefront рендерится сервером (ERB + Turbo) | Spree предоставляет API, фронтенд деплоится отдельно (Next.js, Nuxt) |
| Команда | Rails-разработчики + возможно фронтенд | Фронтенд-команда отдельно, бэкенд — Ruby |
| Производительность | SSR, легко кэшировать | Гибкость, можно использовать Edge functions |
| Время разработки | Быстрее, меньше движущихся частей | Дольше, но гибче для масштабирования |
| Когда выбирать | Небольшие команды, простые магазины, нет требований к мобильным приложениям | Сложные проекты, несколько клиентских приложений, высокие нагрузки |
Как настроить Spree под проект?
Настройка окружения включает установку необходимых гемов и запуск генераторов. Установка стандартная: добавляем гемы, запускаем генераторы, мигрируем базу. Типичные ошибки на этом этапе — неверная настройка базы данных или конфликты версий гемов.
# Gemfile gem 'spree', '~> 4.10' gem 'spree_auth_devise', '~> 4.6' gem 'spree_gateway', '~> 3.10' gem 'spree_backend', '~> 4.10' gem 'spree_sample', '~> 4.10' # тестовые данные # Для headless: gem 'spree_api', '~> 4.10' bundle install bin/rails g spree:install bin/rails g spree:auth:install bin/rails db:migrate bin/rails db:seed После установки доступны:
-
/admin— панель управления -
/api/v2/storefront— REST API для фронтенда -
/— классический storefront (если установленspree_frontend)
Модель данных Spree
База данных Spree построена вокруг ключевых сущностей: магазины (мультимагазинность), категории (вложенные через ancestry), товары с вариантами (SKU, цена, опции), заказы с элементами, оплатами и отгрузками, пользователи.
Spree::Store ├── Spree::Taxon (категории через ancestry) ├── Spree::Product │ ├── Spree::Variant │ ├── Spree::Price │ └── Spree::Image ├── Spree::Order │ ├── Spree::LineItem │ ├── Spree::Payment │ └── Spree::Shipment └── Spree::User (через spree_auth_devise) Как построить headless-магазин на Next.js и Spree API?
Для headless-проекта подключаем @spree/storefront-api-v2-sdk и работаем через REST API. В одном проекте мы заменили монолитный storefront на Next.js 14 с ISR и достигли LCP меньше 0.8 секунды, что значительно улучшило метрики Core Web Vitals. Использование React Server Components позволяет быстро рендерить страницы товаров на сервере.
// lib/spreeClient.ts import { makeClient } from "@spree/storefront-api-v2-sdk"; export const client = makeClient({ host: process.env.NEXT_PUBLIC_SPREE_URL! }); // Получить товары const products = await client.products.list( { include: "default_variant,images,taxons", filter: { taxons: taxonId } }, { sort: "name", page: 1, per_page: 24 } ); // Создать корзину const cart = await client.cart.create(); const orderToken = cart.success().data.attributes.token; await client.cart.addItem({ orderToken }, { variant_id: variantId, quantity: 1 }); Как кастомизировать бизнес-логику без форка?
Spree использует decorator pattern — мы добавляем методы и ассоциации в модуле, который подмешивается в модель. Это позволяет расширять функциональность, не трогая ядро.
# app/models/spree/product_decorator.rb module Spree module ProductDecorator def self.prepended(base) base.has_many :bundle_parts, class_name: "Spree::BundlePart", foreign_key: :bundle_product_id end def bundle? bundle_parts.any? end def effective_price_for(quantity) if quantity >= 10 then price * 0.9 elsif quantity >= 5 then price * 0.95 else price; end end end end Spree::Product.prepend(Spree::ProductDecorator) Для промоакций используем встроенную систему Spree::Promotion. Например, можно настроить правило «сумма заказа от 2000 рублей → скидка 15%»:
promotion = Spree::Promotion.create!( name: "Летняя скидка 15%", code: "SUMMER15", starts_at: Date.today, expires_at: 3.months.from_now, usage_limit: 1000 ) promotion.actions.create!( type: "Spree::Promotion::Actions::CreateAdjustment", calculator: Spree::Calculator::FlatPercentItemTotal.create!(preferred_flat_percent: 15.0) ) promotion.rules.create!( type: "Spree::Promotion::Rules::ItemTotal", preferred_operator: "gte", preferred_amount: 2000.0 ) Такая гибкость позволяет реализовать сложные акции без установки дополнительных плагинов.
Что входит в работу: полный цикл разработки
Мы предлагаем разработку под ключ. Этапы проекта:
| Этап | Описание | Срок (дней) |
|---|---|---|
| Установка и конфигурация | Rails-приложение, Spree Engine, база данных | 2–3 |
| Каталог + импорт товаров | Rake-задачи, CSV/API импорт | 4–8 |
| Кастомная бизнес-логика | Декораторы, промоции, доставка | 5–10 |
| Фронтенд (Headless) | Next.js + Spree SDK | 10–20 |
| Платёжные интеграции | 2–3 провайдера | 4–6 |
| Кастомизация админки | Дополнительные разделы, отчёты | 3–5 |
| Итого | 28–52 |
В результат входит: документация API, доступы к репозиторию и серверу, обучение команды заказчика, гарантия 30 дней на выявленные баги. Опыт наших инженеров — более 10 коммерческих проектов на Spree.
Интеграция платежей и мультивалютность
spree_gateway даёт готовые адаптеры для Stripe, Braintree, PayPal. Для YooKassa или Тинькофф пишется кастомный gateway (реализует интерфейс Spree::Gateway). Мультивалютность настраивается через атрибуты магазина: указываем supported_currencies и supported_locales. Цены привязываются к валюте варианта.
Типичные ошибки при старте на Spree
- Не использовать декораторы — править ядро Spree напрямую. Это делает обновления невозможными.
- Забывать про N+1 запросы в API — включать
includeв запросы. - Игнорировать производительность админки — для крупного каталога нужен Elasticsearch.
- Не настраивать кэширование — Redis обязателен для продакшена. Без него магазин тормозит при 1000+ товаров.
Как выбрать между monolith и headless?
Если ваша команда сильна в Rails и вы делаете типовой магазин — берите монолит. Он быстрее на старте. Если планируете мобильное приложение, сложный фронтенд или высокие нагрузки — headless даёт гибкость, но требует больше ресурсов на разработку. Закажите разработку Spree-магазина — мы свяжемся с вами в течение дня. Получить консультацию можно через форму на сайте или в мессенджерах.







