Skip to content

0009. Storefront category grouping uses the V1 model; publish projects sections into it

  • Status: Accepted
  • Date: 2026-06-12
  • Deciders: POS Integration team

Context

The consumer storefront (store-service) groups a store's menu into categories. It reads a V1 category model spread across three tenant tables:

  • item_categories — the category definitions.
  • item_item_category — product ↔ category (read by GetProductByStoreId into each product's categories, and the source of items_flat.categories).
  • menu_item_category — per-menu (menu_id, item_id, item_category_id) grouping (read by GetMenuItemCategories).

The category catalog (GetStoreCatalog, /v1/store/category) is keyed by category — an item with no category is dropped from it entirely.

pos-integration-service's Menu Manager V2 publish writes the menu structure as menu_sections + menu_section_items. But store-service does not read those tables at all — they are the not-yet-consumed "V2" structure. The result: a freshly published menu had items but no category grouping, so the storefront showed nothing (items without a category are dropped). Verified during the "menu doesn't load on mobile" debugging.

Decision

Until store-service is taught to read the V2 section structure, the publish flow projects each menu section into the V1 category tables so the storefront can group and display items. A section maps 1:1 to an item_category by name.

At the end of publishDraftInTx (in the publish transaction), projectSectionCategories:

  • get-or-creates an item_categories row per section name (provider-scoped: (name, provider), reusing the same get/revive/insert pattern as the product-sync category path);
  • carries the section's display_type onto category_view_type, normalised to uppercase (list/gridLIST/GRID);
  • writes a menu_item_category row per section item (the per-menu grouping the storefront reads) and upserts an item_item_category row (product-level, fills items_flat.categories);
  • on republish, soft-deletes menu_item_category for the replaced menus so stale groupings don't accumulate.

menu_item_category is added to the SQLC-only schema in this repo (012-categories-and-items-flat.sql), mirroring store-service's definition; the real migration lives in dashboard-app/store-service.

Consequences

Positive

  • The storefront groups and shows published items by category — for all adapters, not just those that happened to set a product POSCategory.
  • Reuses the existing category upsert/revive logic and the publish transaction; no new service or cross-service call.

Negative / costs

  • Categories are provider-scoped, so a name that already exists under another provider (e.g. a CATA "Coffee") gets a second, provider-specific row. This is accepted (the storefront groups by the row the menu links to); it is a known duplication, not a bug.
  • We now maintain two representations of menu structure (V2 menu_sections for the editor, projected V1 categories for the storefront) until store-service reads sections directly. This projection is the bridge and should be retired once that happens.

Alternatives considered

  • Teach store-service to read menu_sections/menu_section_items — the proper long-term "V2" path, but a larger cross-repo change; deferred. This ADR is the interim bridge.
  • Reuse categories by name across providers (not provider-scoped) — rejected: inconsistent with how item_categories.provider is used elsewhere; we kept provider scoping and accept the duplicate-name rows.
  • Populate only menu_item_category — rejected: GetProductByStoreId / items_flat.categories read item_item_category, so both are needed for the product-level and menu-level reads.

References

  • PRs #131 (project sections → V1 categories), #132 (section display type → category view type).
  • Code: internal/repository/mysql_menu_draft_repository.go (projectSectionCategories), database/queries/menus.sql (InsertMenuItemCategory, SoftDeleteMenuItemCategoryByDeletedMenus).
  • store-service reads: database/queries/menu.sql (GetMenuItemCategories), database/queries/product.sql (GetProductByStoreId), service/MainService/Store.go (GetStoreCatalog, collectCategoriesAndProducts).
  • Diagnostic: script/check-store-menu.sh (Query 4 renders the projected category → product hierarchy).
  • Related: ADR 0008 (the sibling publish-time projection into items_flat).