54 lines
2.7 KiB
Markdown
54 lines
2.7 KiB
Markdown
|
|
---
|
||
|
|
id: ADR-0002
|
||
|
|
title: Media Manager backend contract and mock storage adapter
|
||
|
|
status: active
|
||
|
|
date: 2026-07-15
|
||
|
|
tags: ["architecture", "media", "backend-gap", "repository-pattern"]
|
||
|
|
---
|
||
|
|
|
||
|
|
## Context
|
||
|
|
|
||
|
|
Sprint 4 (Media Manager) needs a media library: upload, browse, delete, and pick
|
||
|
|
images/files for use across Product Editor, Static Pages (CMS), and Branding.
|
||
|
|
No media backend exists yet — `/media` currently routes to a "coming soon"
|
||
|
|
placeholder (`BackofficeComingSoonPageComponent`), and `docs/BACKEND.md` does
|
||
|
|
not document any upload/storage endpoint. This mirrors the already-documented
|
||
|
|
draft-publish-flow gap in the Project Editor (see `PE-20260713T010000Z-0003`):
|
||
|
|
build the real contract, then implement a client-side mock adapter behind the
|
||
|
|
same interface so the UI never needs to change when the backend ships.
|
||
|
|
|
||
|
|
## Decision
|
||
|
|
|
||
|
|
- **Domain model** `MediaAsset`: `{ id, url, thumbnailUrl?, filename, mimeType,
|
||
|
|
size, width?, height?, altText?: Record<locale, string>, tags?: string[],
|
||
|
|
createdAt }`. `altText` follows the platform's `translations.{lang}` rule
|
||
|
|
(ADR-0001) — never a flat string.
|
||
|
|
- **Repository contract** (future backend, to be implemented server-side):
|
||
|
|
- `GET /media?page=&pageSize=&search=` → paginated `MediaAsset[]`
|
||
|
|
- `POST /media/upload` (multipart) → `MediaAsset`
|
||
|
|
- `DELETE /media/:id` → 204
|
||
|
|
- `PATCH /media/:id` (altText/tags only) → `MediaAsset`
|
||
|
|
- **Frontend abstraction**: a `MediaRepository` interface (Repository pattern,
|
||
|
|
per `docs/context/features/*` conventions) with two implementations selected
|
||
|
|
via DI token:
|
||
|
|
- `MockMediaRepository` — stores assets in IndexedDB (not localStorage: binary
|
||
|
|
blobs need it) as an interim store until the backend exists. Data URLs are
|
||
|
|
generated for rendering; the shape returned matches `MediaAsset` exactly.
|
||
|
|
- `HttpMediaRepository` — thin wrapper over the endpoints above, added when
|
||
|
|
the backend ships. Swapping providers is the only change required.
|
||
|
|
- **Media never enters the Bootstrap model.** Like products/orders/users, media
|
||
|
|
assets are runtime admin data, not tenant configuration — consistent with
|
||
|
|
ADR-0001's rule that Bootstrap contains only what's needed before the app
|
||
|
|
starts.
|
||
|
|
- **Media Picker** is a standalone, reusable dialog (built on the existing
|
||
|
|
`app-dialog` Design System primitive) so Product Editor and CMS editors
|
||
|
|
consume the same selection UI instead of each building their own.
|
||
|
|
|
||
|
|
## Consequences
|
||
|
|
|
||
|
|
- Any feature needing to reference an image (product gallery, static page
|
||
|
|
hero, branding logo) does so via `MediaAsset.url`/`id`, obtained through the
|
||
|
|
shared Media Picker — never a raw file input duplicated per feature.
|
||
|
|
- When the backend ships, only `MediaRepository`'s DI provider changes; no
|
||
|
|
component or facade code should need to change.
|