5.5 KiB
5.5 KiB
Architecture & Quick Wins (Maintainability First)
Last updated: 2026-03-13 (implemented baseline quick wins + P1 tooling + duplicate-route cleanup)
Product North Star
Maintainability work should support the product mission, not drift away from it.
Tilbudgivern's core mission is:
- let carpenters create a strong quote draft in the field with very few inputs
- reuse as much real history as possible from Tilbudgivern and Ordrestyring
- use Smart Packages as editable operational templates
- use AI to improve wording and structure, not to invent facts
This means architecture decisions should favor:
- structured project data over free-text-only AI flows
- reusable local historical data over one-off API calls
- mobile-friendly, low-friction request/response shapes
- clear persistence of package choice, suggestion source, and quote learning signals
1) Current Architecture Snapshot
Runtime topology
- Frontend: React app in
frontend/src, built tofrontend/build. - Backend: Unified Express + Socket.IO server in
backend/unified-server.js. - Database: MariaDB/MySQL via
mysql2pools. - Service layer: Domain modules in
backend/src/services(pricing, geometry, OpenAI, Ordrestyring, PDF, imports). - Route layer: Mixed model:
- many inline handlers inside
backend/unified-server.js - mounted routers from
backend/src/routesandbackend/routes.
- many inline handlers inside
Request/data flow
- Browser UI (React) issues Axios API calls.
- Express middleware applies CORS, security headers, request/session logging.
- Route handler executes inline logic or delegates to a service.
- Service calls DB/external systems (Ordrestyring/OpenAI/etc.).
- Response returns JSON to UI.
- Socket.IO optionally emits project updates for real-time collaboration.
- Non-API requests are served from
frontend/build/index.html.
2) Key Findings (Maintainability Risks)
- Monolithic API surface
backend/unified-server.jsis still ~11,634 lines with ~200+/apiroute registrations.- High change collision risk and difficult ownership boundaries.
- Mixed route ownership
- Same domains are handled inline and via mounted routers.
- Example:
analytics,customer-projects,calendar, andordrestyringnow have dedicated routers, while adjacent domain logic still lives inline inbackend/unified-server.js. - The active duplicate registrations called out in the first pass have now been removed, but ownership is still split across
backend/unified-server.js,backend/src/routes, andbackend/routes.
- Runtime loading patterns
- Multiple handler-time
require(...)calls increase hidden coupling and make dependency flow harder to reason about. - A bootstrap service container now centralizes core startup wiring, but legacy handler-level dependencies still exist in parts of
backend/unified-server.js.
- Script drift in root package
- The immediate script drift has been fixed, but root scripts still proxy into nested packages and can drift again if backend/frontend package commands change without updating the root wrapper.
- CI lint gate is weak
- CI now runs a real lint gate, but the frontend stage is still warning-tolerant.
- The repo has 25 existing frontend lint warnings that should be cleared before tightening enforcement to fail on warnings.
3) Prioritized Quick Wins
P0 (Do first)
- Fix root scripts (high impact, low effort)
- Update root
package.jsonscripts to valid entrypoints/commands. - Definition of done:
npm run dev,npm start, andnpm testall execute real targets.
- Consolidate health route ownership (high impact, low effort)
- Choose one owner (
backend/src/routes/healthDashboardrouter or inline block) and remove duplication. - Definition of done: one route source of truth; no endpoint behavior regressions.
- Start modular extraction from unified server (high impact, medium effort)
- Extract one domain first (recommended:
health+analytics), preserving existing API contracts. - Definition of done: extracted router passes existing tests and route smoke checks.
P1 (Next)
- Introduce service bootstrap/container
- Initialize dependencies once at startup, then inject/use references instead of ad-hoc lazy
require. - Definition of done: startup wiring is explicit; handlers stop resolving core services dynamically.
- Status: baseline implemented via
backend/src/services/bootstrapServiceContainer.js.
- Add API surface inventory check
- Add a script that lists all registered
/api/*endpoints and flags duplicates. - Definition of done: inventory output is generated in CI or as a local validation step.
- Status: implemented via
scripts/api-inventory.js; usenpm run api:inventoryfor summary andnpm run api:inventory:fullfor the full route table. Current baseline: 275 endpoints, no duplicate method+path registrations detected.
- Strengthen CI quality gate
- Add actual lint scripts in frontend/backend and make lint a required CI signal.
- Keep security audits informative but separate from core merge-blocking checks.
- Status: implemented as a baseline gate. Backend uses syntax linting, frontend uses ESLint, and CI runs
npm run lint.
4) Guardrails for Refactoring
- Preserve current external API contracts (
/api/*paths and response shapes) during quick-win refactors. - Prefer “extract without redesign” in first pass.
- Validate each extraction with:
cd backend && npm test -- --coverage --passWithNoTestscd tests && PLAYWRIGHT_BASE_URL=http://localhost:4032 npx playwright test(smoke subset acceptable initially)