heff-shop - Saleor submodules, Docker, and the test build grind

heff-shop Saleor monorepo - platform + forked storefront submodules, Docker smoke tests, multi-channel GraphQL.

After opening the Stagea platform monorepo in late 2024, the shop problem split into two layers: community parts (catalogue, fitment, wiki cross-links) and actual checkout (inventory, channels, tax, payments). The second layer became heff0/heff-shop: a focused repo for a Saleor-powered, multi-channel storefront with the backend and frontend pinned as Git submodules instead of copy-pasted vendor trees.

Project dossier (submodule BOM, smoke-test timeline): heff-shop Saleor Monorepo.

This is a 2025 build log, not a launch announcement. The README is still one line. The value is in what the repo structure forces you to learn.

What heff-shop is

Per the repo:

Saleor powered storefront. Contains saleor/storefront + Saleor/core. Serves multi-channel.

In practice the layout is:

SubmoduleUpstreamRole
saleor-platform/saleor/saleor-platformDockerized Saleor core: API, worker, Postgres, Redis, dashboard
saleor-storefront/heff0/saleor-storefront (fork)Next.js storefront talking to Saleor over GraphQL

The fork matters. Upstream Saleor moves fast; the build needed a storefront submodule that could be bumped on its own schedule, patched for Heff channels, and pinned without surprise main breakage. Early commits in heff-shop reflect that pivot: initial submodules, then retargeting the storefront remote to the fork and bumping the pinned SHA.

Multi-channel is the business reason, not resume padding. stagea-stuff might sell Stagea-specific parts; heff.world might sell engineering services or generic merch; future Animal Garage inventory might be another channel entirely. Saleor's channel model maps cleanly onto "same platform, different catalogues and checkout rules", if you can keep the GraphQL API and storefront env vars honest.

Submodule subcontainers: the part that hurts

Calling them "subcontainers via repo" is almost polite. What you actually get is containers defined inside repos defined inside your repo:

  1. Clone heff-shop with git clone --recurse-submodules
  2. saleor-platform/ brings its own docker-compose.yml: API on :8000, dashboard, worker, databases
  3. saleor-storefront/ expects NEXT_PUBLIC_SALEOR_API_URL pointing at that GraphQL endpoint and runs its own Node dev server on :3000

The pain points, repeated every time you onboard a machine:

Submodule drift

A - in git submodule status means someone cloned without --recurse-submodules and wonders why saleor-platform/ is empty. A + means the child repo moved but the parent never committed the SHA bump. Both look like "Docker is broken" until you realize Git never fetched the child.

Double Compose namespaces

saleor-platform already binds host ports for Postgres, Redis, Mailpit, and the API. Spin up Ghost or another stack from stagea-stuff on the same laptop and you collide on 6379, 1025, or 8025. The fix is overrides and discipline (not running every submodule's default compose blindly), but that discipline has to be documented or every test build regresses.

Forked storefront wiring

The storefront is not vendored source in the parent repo; it is a submodule pointer. Changing a product card component means:

  1. Commit in heff0/saleor-storefront
  2. Bump submodule SHA in heff-shop
  3. Re-run the integration smoke test against the pinned platform stack

Skip step 2 and production deploys a storefront SHA that does not match what you tested.

Env var fragility

Saleor's contract is GraphQL. One wrong URL (http://localhost:8000 without /graphql/, or https against a dev HTTP API) and the Next storefront fails in opaque urql errors. Multi-channel adds NEXT_PUBLIC_DEFAULT_CHANNEL; seed data must actually contain that channel or the storefront renders empty shelves successfully, which is worse than a hard crash.

Packaging in Docker

heff-shop does not rebuild Saleor from scratch. The packaging model is orchestration, not authorship:

  • Platform submodule = upstream-maintained images and compose services (the "core" in README terms)
  • Storefront submodule = Next.js app containerized for dev; production packaging TBD behind the same GraphQL boundary
  • Parent repo = glue: .gitmodules, ignore rules, eventually a root compose that pins compatible SHAs and documents port maps

The challenge is version coupling. Saleor API schema changes can break storefront queries. Pinning both submodules in one parent commit is the closest thing to a reproducible "stack version" without a monolithic image. Docker gives you process isolation; Git submodules give you version isolation, when you respect the pointers.

What Docker does not solve: waiting for five services to become healthy before the storefront's first GraphQL introspection. Test builds need an explicit "API ready" gate, not docker compose up and immediate pnpm dev.

Test build-out

The 2025 goal was not feature completeness. It was a repeatable smoke path:

  1. Initialise submodules at pinned commits
  2. docker compose up in saleor-platform/: confirm GraphQL at http://localhost:8000/graphql/
  3. Seed or verify default-channel (saleor-platform's sample data path)
  4. Configure storefront .env with API URL, storefront URL, default channel
  5. pnpm install --frozen-lockfile && pnpm dev in saleor-storefront/
  6. Load homepage: products resolve, cart mutation succeeds, checkout does not 500 on channel mismatch

That sounds trivial. It ate evenings. Typical failures:

  • Stale volume data from an older Saleor major version
  • Storefront pointing at API before migrations finished
  • Channel slug typos (default-channel vs default_channel)
  • Submodule not updated after a fork fix: "works in the child repo, fails in heff-shop"

heff-shop also stayed separate from the broader stagea-stuff monorepo on purpose. Stagea-stuff's shop/ submodule tracks upstream Saleor storefront for the community site; heff-shop is the Heff commerce lab: multi-channel experiments, fork workflow, and Docker pinning without dragging NodeBB and MediaWiki into every shop debug session.

GraphQL as the real interface

Saleor is not REST-with-extra-steps. The storefront's urql client, channel queries, and cart mutations are all GraphQL. Once submodules and Compose are aligned, most debugging is:

  • Inspecting the schema in Dashboard → API playground
  • Comparing storefront query documents to the pinned API version
  • Tracing multi-channel context headers

That made GraphQL a practical boundary skill here, not trivia: the line where backend and frontend actually meet in this stack.

Lineage and what is next

WhenWhat
Dec 2024stageastuff-origin: why a parts catalogue exists
2025heff-shop: Saleor platform + forked storefront, Docker test builds
2022Django-Auto-Forum: first owned marketplace-of-ideas attempt

Next steps for heff-shop: root-level compose overrides, CI that fails if submodule SHAs drift without a changelog entry, and channel definitions that match real inventory, not demo products.

Repository: github.com/heff0/heff-shop.

© 2026 Heff.world。保留所有权利。