This is a work in progress
The backend should now treat the four YAML files as first-class data sources:
art.yamlgallery.yamlscoring.yamlshow.yaml
What the backend should become
The backend should do four things cleanly:
- load and save each YAML file independently
- expose focused API routes for each file
- expose one combined route for algorithm use
- normalize relationships across files without duplicating data
The key idea is this:
art.yamlowns artworks and vocabulariesgallery.yamlowns spacesscoring.yamlowns evaluator settingsshow.yamlowns arrangements and placements
Backend structure
Inside server/src/, I would restructure to this:
server/src/
app.js
server.js
routes/
art.js
gallery.js
scoring.js
show.js
data.js
uploads.js
services/
yamlFileService.js
artService.js
galleryService.js
scoringService.js
showService.js
dataService.js
uploadService.js
utils/
validation.js
filePaths.js
yamlDefaults.js
File responsibilities
yamlFileService.js
Low-level shared YAML read/write functions only.
Responsibilities:
- read a YAML file
- write a YAML file
- create backups
- validate file existence
- return parsed JS object
This file should know nothing about artworks, spaces, or arrangements.
artService.js
Owns all artwork catalog logic.
Responsibilities:
- load
art.yaml - get all artworks
- get one artwork by ID
- create artwork
- update artwork
- delete artwork if you want
- return vocabularies from
art.yaml - validate uniqueness of artwork IDs
galleryService.js
Owns all gallery space logic.
Responsibilities:
- load
gallery.yaml - get all spaces
- get one space by ID
- create/update spaces
- validate geometry fields
scoringService.js
Owns scoring profile logic.
Responsibilities:
- load
scoring.yaml - get scoring profile
- update scoring profile
- validate criteria structure
showService.js
Owns arrangements and placements.
Responsibilities:
- load
show.yaml - get all arrangements
- get arrangement by ID
- create arrangement
- update arrangement
- add/update/remove placements
- validate references to artwork IDs and space IDs
dataService.js
The integration layer.
Responsibilities:
- load all four files together
- resolve references as needed
- build combined algorithm-friendly data
- provide
getAllData()and maybegetResolvedArrangement(arrangementId)
This is the file Python will eventually depend on through the API.
Data files
In server/data/, I would now have:
server/data/ art.yaml gallery.yaml scoring.yaml show.yaml backups/
And I would move backups into backups/, not spray .bak files into the main data directory.
API routes
Here is the contract I would use.
Art routes
GET /api/art GET /api/art/:id POST /api/art PUT /api/art/:id DELETE /api/art/:id GET /api/art-vocabularies
Gallery routes
GET /api/gallery GET /api/gallery/spaces GET /api/gallery/spaces/:id POST /api/gallery/spaces PUT /api/gallery/spaces/:id
Scoring routes
GET /api/scoring PUT /api/scoring
For Version 1 of scoring editing, a single profile is fine.
Show routes
GET /api/show GET /api/show/arrangements GET /api/show/arrangements/:id POST /api/show/arrangements PUT /api/show/arrangements/:id POST /api/show/arrangements/:id/placements PUT /api/show/arrangements/:id/placements/:artworkId DELETE /api/show/arrangements/:id/placements/:artworkId
Combined data routes
GET /api/data/all GET /api/data/resolved-arrangements/:id
/api/data/all should return something like:
{
"art": { ... },
"gallery": { ... },
"scoring": { ... },
"show": { ... }
}
That is the future Python bridge.
What should change in the current artwork page
The current artwork page should no longer read show.yaml. It should read art.yaml only.
So the art UI should use:
GET /api/artGET /api/art-vocabulariesPOST /api/artPUT /api/art/:id
That should be your first working milestone.
Important validation rules
This refactor is where you should tighten validation.
In artService
Validate:
- required fields
- numeric conversion for
year,width_ft,height_ft - arrays are arrays
- artwork ID uniqueness
primary_themeexists in vocabulariestheme_tags,palette_tags,mood_tagsare known values if strict mode is on
In galleryService
Validate:
- unique space IDs
- positive dimensions
- usable ranges inside wall bounds
centerline_ftmakes sense
In showService
Validate:
- arrangement IDs unique
space_idexists ingallery.yaml- every
artwork_idexists inart.yaml - placements have numeric
x_ft,y_ft
In scoringService
Validate:
- criteria object exists
- required fields per criterion exist
- weights/tolerances are numeric
- pairwise tables are correctly shaped
The biggest architectural rule
Do not let routes directly manipulate raw YAML objects.
Bad pattern:
router.post('/', async (req, res) => {
const data = readYaml(...);
data.art.artworks.push(req.body);
writeYaml(...);
});
That will rot quickly.
Instead:
router.post('/', async (req, res) => {
const artwork = await artService.createArtwork(req.body);
res.status(201).json(artwork);
});
Routes should stay thin. Services should own logic.
Migration strategy
Do this in order.
Step 1
Create the new YAML files:
art.yamlgallery.yamlscoring.yamlshow.yaml
Step 2
Build the low-level YAML service.
Step 3
Build artService and switch the existing artwork UI to use it.
Step 4
Add galleryService, scoringService, and showService.
Step 5
Add the combined /api/data/all route.
Only after that should you build new UI pages.
That order matters because once the data contract is stable, the UI becomes much easier.
The Python bridge
Later, Python should not read YAML directly from arbitrary files if it can avoid it. It can, but the cleaner path for the class is:
- backend reads and validates YAML
- backend exposes normalized JSON
- Python consumes JSON as dicts/lists
So the eventual Python workflow is:
- React calls backend
- backend returns combined JSON
- Python generator/scorer consumes that
- Python returns candidate arrangements and scores
- backend stores or forwards results
- React visualizes them
That is much cleaner than letting every student reinvent YAML parsing and validation.
What I would do right now
The immediate backend milestone should be:
- current artwork page still works
- but now it reads from
art.yaml - and the backend already knows about the other three files
That gives you a stable base without trying to build walls, scoring, placements, and Python in one leap.
Suggested first implementation target
I would start by creating these files first:
server/src/services/yamlFileService.js server/src/services/artService.js server/src/services/dataService.js server/src/routes/art.js server/src/routes/data.js
and wire them into app.js.
That is the minimum clean cut.
If you want, next I’ll write the actual contents of those backend files in a form you can drop into the project.
