Operations & platform
Multi-store with shared admin
Indicative price, not an offer: €99–299/month per additional store
Generated from docs/plan/paid/multi-store-with-shared-admin/PLAN.md in the AstroBaaS repository. Nothing described below is implemented — it is the written plan for it.
Currently, one AstroBaaS instance = one site. Merchants running 3 brands must deploy 3 instances, maintain 3 databases, and keep 3 admins in sync. This feature enables multiple stores/brands in one instance with shared admin and per-store configuration.
The problem
I run 3 brands but I’m forced to maintain 3 separate storefronts and 3 databases. I want one admin dashboard that manages all 3, shared products catalog, shared customer base (with per-brand orders).
What it does
- Store entity: {id, name, slug, domain, default_currency, language, branding (logo, colors)}, created_at, updated_at
- Scope propagation: every entity (Product, Order, Customer, Post, etc.) belongs to a store_id; queries filter by store unless global
- Global entities: Customers, Posts are global (shared across stores); Products and Orders are per-store
- Admin store selector: staff can switch stores via dropdown; admin dashboard shows data only for selected store
- Per-store branding: each store has its own logo, color scheme, email signature (applied when serving that store’s frontend)
- Per-store product catalog: products tagged with store_id; a product can be assigned to multiple stores with different prices
- Shared customer base: customer is global; each order is tagged with store_id; customer sees orders from all stores they’ve purchased from
- API endpoints: CRUD stores, scope filtering on all list endpoints
What it deliberately does NOT do
Each boundary carries its reason. A boundary without a reason gets crossed by the next person who reads this.
- Store-specific staff permissions (one admin controls all stores) — permissions are global; per-store access control is post-1.0
- Cross-store inventory (shared stock pool) — each store has its own inventory; no stock sharing
- Store-specific workflows — workflows are global; conditional routing by store is not implemented
- Store transfer (moving order/customer between stores) — stores are isolated; transfers would require data migration
Data model
New entity: Store {id, name, slug (unique), domain, default_currency, language, branding (JSON), created_at, updated_at}. Schema migration: add store_id (FK) to Product, Order, OrderLineItem, Category, Cart, etc. Add unique index (store_id, slug) on products. Global entities (Customer, Post, User) do NOT have store_id. All three drivers must support this migration.
API
- POST /api/stores — create store (admin only)
- GET /api/stores — list all stores (admin only)
- GET /api/stores/:id — fetch store detail
- PATCH /api/stores/:id — update store (admin only)
- GET /api/products?store_id=:id — list products for store (public)
- GET /api/orders?store_id=:id — list orders for store (admin only)
- GET /api/customers/:id/orders?store_id=:id — orders for customer in specific store
Every route added here must also appear in src/pages/openapi.json.ts — a test
fails the build if it does not.
Admin
Store manager: list of stores, create/edit store form (name, slug, domain, currency, language, branding). Admin UI header: store selector dropdown (top-left), switches context for all views. All product/order/customer views filter by selected store automatically. Per-store product pricing: product can be assigned to store A at €50, store B at €60.
The seam — why this is paid
Core owns the store model, scope propagation, and multi-store entity schema. This is an architectural change — every entity must be reviewed for store_id propagation. Paid layer owns advanced features: store-specific staff roles, inventory sharing, and cross-store analytics.
Substantial feature requiring scope propagation across all entities. Architectural decision change + support commitment.
Dependencies
- Schema migration infrastructure (all three storage drivers must support adding store_id FK to dozens of tables)
- Query builder must support store_id filtering (every list query must append WHERE store_id = ?)
- Assume existing entity models (Product, Order, Customer, etc.) are well-designed and can accommodate store_id without breaking
Acceptance checks
Each of these must be able to fail. Before claiming this is done, break the code deliberately and watch each one go red.
- A store can be created with name ‘Brand B’, slug ‘brand-b’, currency EUR, domain brand-b.example.com
- A product can be created in store ‘Brand A’ with price €50; another product with same name in ‘Brand B’ with price €60
- Admin selects store ‘Brand A’ from dropdown; product list shows only products in Brand A
- A customer places order in store A and order in store B; customer can view orders from both stores in their account
- API query GET /api/products?store_id=123 returns only products for that store
- A customer created in store A can place order in store B; customer is global, not store-specific
Risks
Schema migration is large and error-prone across three storage drivers; must be tested on all three before shipping. Query filtering is now mandatory everywhere; missing a filter (query that forgets WHERE store_id) could expose one store’s data to another store. Products with the same slug in different stores are allowed but confusing (product URL structure must include store indicator or be per-store). Performance degrades if store_id is not indexed; missing indexes must be caught in testing. Staff access control is global; all admins can see all stores (limitation documented as a known issue for paid tier).
Commercial context
| Suggested price | €99–299/month per additional store |
| Rival anchor | Magento Open Source, free: multiple websites, stores, store views with per-scope pricing, catalogue, currency, tax. |
The anchor is what the nearest equivalent charges on Shopify or Magento today. It is context for a pricing decision, not the decision.
Generated from the commerce plan. See docs/COMMERCE-PLAN.md for the full
catalogue and ../../AI-GUIDE.md for how to work on this repository.