Compare commits

...

5 Commits

Author SHA1 Message Date
ben.stull 346e7f2e22 feat(slice-1): platform/config — single env-driven config surface (INV-8)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 08:35:16 -07:00
ben.stull 4acab40653 feat(slice-1): backend skeleton, deps, and import-linter layer contract
Four-layer package (main > domains > platform) per SD-0001 §6.2; GraphQL api layer
deferred. main.py is a stub so the top layer exists from the start (fleshed out in
the /healthz task). lint-imports green on the skeleton.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 08:34:38 -07:00
ben.stull d125317c00 plan(slice-1): SD-0001 walking-skeleton implementation plan
Just-in-time plan for SLICE-1 (§7.2): backend 4-layer scaffold + import-linter,
dev Postgres compose, psycopg migration runner + migration 0001, /healthz,
scripts/check.sh + dev.sh, Vite/React shell, CI, docs/BOOTSTRAP.md.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 08:32:28 -07:00
ben.stull 0078b0c6da chore(specs): SD-0001 lives only in wiggleverse-ecomm-content/specs/ (#4) 2026-06-10 14:53:13 +00:00
ben.stull 23afcf95ce Merge pull request 'SD-0001: Solution Design — MVP sign up and create a single storefront (Feature #1)' (#2) from claude/optimistic-lumiere-ef8b1e into main 2026-06-10 14:12:57 +00:00
12 changed files with 1557 additions and 1059 deletions
+3
View File
@@ -6,3 +6,6 @@ node_modules/
dist/
.env
*.local
.pytest_cache/
frontend/dist/
backend/var/
+14
View File
@@ -0,0 +1,14 @@
# Enforces SD-0001 §6.2: the four-layer contract main > domains > platform, with
# imports flowing only downward. The GraphQL `api` layer is deferred (§2), so the
# MVP contract has three layers; `app.api` is added back between main and domains
# when GraphQL arrives. Run from backend/: .venv/bin/lint-imports
[importlinter]
root_package = app
[importlinter:contract:layers]
name = App layering (main > domains > platform)
type = layers
layers =
app.main
app.domains
app.platform
View File
+6
View File
@@ -0,0 +1,6 @@
"""domains layer — bounded contexts (accounts, storefronts).
Empty in SLICE-1; the accounts domain lands in SLICE-2 and storefronts in SLICE-3.
The package exists now so the layer contract (.importlinter) has a target and later
slices add to a stable seam. Domains import only from app.platform, never upward.
"""
+5
View File
@@ -0,0 +1,5 @@
"""ecomm backend — FastAPI app factory + the REST BFF.
Stub in SLICE-1 Task 1 so the top layer of the import-linter contract exists from the
start; the FastAPI factory and /healthz are added in Task 5.
"""
+5
View File
@@ -0,0 +1,5 @@
"""platform layer — cross-cutting primitives (config, db, deps).
The bottom layer: imports nothing from app.domains or app.main. Owns the schema
lifecycle and request wiring, never business rules (SD-0001 §6.2).
"""
+19
View File
@@ -0,0 +1,19 @@
"""Configuration surface — the single place deployment/environment config is read.
INV-8: no deployment shape is baked into framework code. Every URL, credential, or
relay coordinate arrives through here from the environment (resolved from Secret
Manager in deployed environments). The localhost defaults match compose.yaml so a
clean `scripts/dev.sh` checkout needs no env setup.
"""
from __future__ import annotations
import os
# Dev default points at the single Postgres container compose.yaml brings up. In
# PPE/Prod the deployment supplies ECOMM_DATABASE_URL from Secret Manager (INV-8).
_DEFAULT_DATABASE_URL = "postgresql://ecomm:ecomm@localhost:5432/ecomm"
def database_url() -> str:
"""The psycopg DSN for the application database."""
return os.environ.get("ECOMM_DATABASE_URL") or _DEFAULT_DATABASE_URL
+7
View File
@@ -0,0 +1,7 @@
[pytest]
testpaths = tests
python_files = test_*.py
addopts = -q
# Put backend/ (this rootdir) on sys.path so tests import the `app` package under
# pytest's default prepend import mode (tests/ has no __init__.py).
pythonpath = .
+7
View File
@@ -0,0 +1,7 @@
fastapi>=0.110
uvicorn[standard]>=0.29
httpx>=0.27
psycopg[binary]>=3.1
psycopg-pool>=3.2
pytest>=8.0
import-linter>=2.0
+13
View File
@@ -0,0 +1,13 @@
import os
from app.platform import config
def test_database_url_defaults_to_local_compose(monkeypatch):
monkeypatch.delenv("ECOMM_DATABASE_URL", raising=False)
assert config.database_url() == "postgresql://ecomm:ecomm@localhost:5432/ecomm"
def test_database_url_honors_env(monkeypatch):
monkeypatch.setenv("ECOMM_DATABASE_URL", "postgresql://x:y@db:5432/z")
assert config.database_url() == "postgresql://x:y@db:5432/z"
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff