Skip to content

Nuovo progetto compatibile con lo stack globale

Questa guida definisce il baseline tecnico per creare un nuovo repository EXPERIENCE compatibile con il workspace cs-repository.

Il modello assume un monorepo npm con API NestJS, frontend React/Vite e package condiviso locale. Se il progetto diventa parte ufficiale dello stack operativo, deve essere aggiunto anche a platform-local-stack, platform-status-service e al catalogo della wiki.

Layout standard

text
new-project/
  apps/
    api/
      src/
      prisma/
      test/
      package.json
    web/
      src/
      public/
      package.json
  packages/
    shared/
      src/
      package.json
  package.json
  tsconfig.base.json

Regole di ownership:

  • apps/web chiama solo endpoint same-origin /api/*.
  • apps/api fa da BFF e integra i servizi CORE tramite client piccoli e dedicati.
  • packages/shared contiene solo tipi, costanti e validatori riusabili dentro il progetto.
  • I contratti consumati da piu repository vanno promossi in platform-shared-packages.
  • Componenti, controller, service, client HTTP e utility devono restare piccoli, modulari e riusabili.

Bootstrap rapido

Il bootstrap puo partire dai generatori standard, poi va normalizzato sul layout e sulle regole del workspace.

bash
mkdir new-project
cd new-project

npm init -y
npm pkg set private=true
npm pkg set type=module
npm pkg set 'workspaces[0]=apps/*'
npm pkg set 'workspaces[1]=packages/*'

mkdir -p apps packages packages/shared/src
npm create vite@latest apps/web -- --template react-ts
npx @nestjs/cli@latest new apps/api --package-manager npm --skip-git
npm init -w packages/shared -y

Installa poi le dipendenze per workspace, fissando nel lockfile i major approvati:

bash
npm install -D concurrently eslint @eslint/js typescript-eslint eslint-config-prettier prettier eslint-plugin-react-hooks eslint-plugin-react-refresh globals

npm install -w apps/api @nestjs/common@^11 @nestjs/core@^11 @nestjs/platform-express@^11 @nestjs/config @nestjs/jwt @prisma/client@^7 @prisma/adapter-pg@^7 pg class-validator class-transformer argon2 helmet cookie-parser reflect-metadata rxjs dotenv
npm install -D -w apps/api prisma@^7 jest ts-jest supertest testcontainers @nestjs/cli tsx ts-node typescript

npm install -w apps/web react@^19 react-dom@^19 react-router-dom@^7 @reduxjs/toolkit react-redux react-intl react-hook-form @hookform/resolvers zod class-variance-authority clsx tailwind-merge lucide-react tailwindcss-animate @radix-ui/react-slot @radix-ui/react-dialog @radix-ui/react-dropdown-menu @radix-ui/react-tooltip @radix-ui/react-popover @radix-ui/react-tabs @radix-ui/react-checkbox @radix-ui/react-select @radix-ui/react-label
npm install -D -w apps/web vite @vitejs/plugin-react tailwindcss @tailwindcss/vite vitest jsdom @testing-library/react @testing-library/jest-dom @testing-library/user-event typescript

npm install -D -w packages/shared typescript
npm install -w packages/shared zod

Se packages/shared non espone validatori condivisi, rimuovi zod dal package.

Root workspace

Il root deve orchestrare i workspace npm e i comandi comuni.

Dipendenze:

  • Monorepo: npm workspaces, concurrently
  • Lint/format: eslint, @eslint/js, typescript-eslint, eslint-config-prettier, prettier
  • React lint: eslint-plugin-react-hooks, eslint-plugin-react-refresh
  • Globals: globals

Esempio minimo di package.json:

json
{
  "private": true,
  "type": "module",
  "workspaces": ["apps/*", "packages/*"],
  "scripts": {
    "dev": "concurrently \"npm -w apps/api run dev\" \"npm -w apps/web run dev\"",
    "lint": "npm -ws --if-present run lint",
    "test": "npm -ws --if-present run test",
    "build": "npm -ws --if-present run build"
  },
  "devDependencies": {
    "@eslint/js": "^9.0.0",
    "concurrently": "^9.0.0",
    "eslint": "^9.0.0",
    "eslint-config-prettier": "^10.0.0",
    "eslint-plugin-react-hooks": "^5.0.0",
    "eslint-plugin-react-refresh": "^0.4.0",
    "globals": "^16.0.0",
    "prettier": "^3.0.0",
    "typescript-eslint": "^8.0.0"
  }
}

apps/api

Stack approvato:

  • NestJS 11: @nestjs/common, @nestjs/core, @nestjs/platform-express, @nestjs/config, @nestjs/jwt
  • Prisma 7 + Postgres: prisma, @prisma/client, @prisma/adapter-pg, pg
  • Validazione/transform: class-validator, class-transformer
  • Sicurezza/auth: argon2, helmet, cookie-parser
  • Runtime TS: reflect-metadata, rxjs, dotenv
  • Test: jest, ts-jest, supertest, testcontainers
  • Tooling: @nestjs/cli, tsx, ts-node, typescript

Struttura consigliata:

text
src/
  main.ts
  app.module.ts
  config/
    env.schema.ts
    runtime-config.service.ts
  health/
    health.controller.ts
    health.service.ts
  prisma/
    prisma.module.ts
    prisma.service.ts
  auth/
    auth.module.ts
    guards/
  clients/
    platform-auth.client.ts
    platform-ai.client.ts
  features/
    example/
      dto/
      example.controller.ts
      example.service.ts
      example.repository.ts

Linee guida API:

  • Ogni modulo espone controller sottili, service applicativi piccoli e repository dedicati quando serve persistenza.
  • Ogni client verso servizi CORE vive in src/clients o nel modulo proprietario, con input tipizzati e risposta normalizzata.
  • GET /health deve essere stabile e non dipendere da dettagli locali come PID o processi.
  • La validazione globale usa ValidationPipe con DTO espliciti.
  • Prisma resta dietro un service dedicato; non passare PrismaClient direttamente nei controller.

Bootstrap tipico in main.ts:

ts
import cookieParser from "cookie-parser";
import helmet from "helmet";
import { ValidationPipe } from "@nestjs/common";
import { NestFactory } from "@nestjs/core";
import { AppModule } from "./app.module";

const app = await NestFactory.create(AppModule);

app.use(helmet());
app.use(cookieParser());
app.setGlobalPrefix("api");
app.useGlobalPipes(
  new ValidationPipe({
    transform: true,
    whitelist: true,
    forbidNonWhitelisted: true,
  }),
);

await app.listen(process.env.PORT ?? 3001);

Variabili minime da prevedere:

text
PORT=3001
DATABASE_URL=postgresql://...
JWT_SECRET=...
PLATFORM_INTERNAL_TOKEN=...
AUTH_SERVICE_URL=http://localhost:3100
AI_SERVICE_URL=http://localhost:3300

Usa i servizi CORE esistenti quando il requisito risulta condiviso:

  • Auth/sessioni: platform-auth-service
  • AI provider-agnostic: platform-ai-service
  • Agent asincroni: platform-agent-service
  • Salesforce e datasource esterni: platform-connectors-service
  • Pagamenti: platform-payments-service
  • Scraping: platform-scraper-service

apps/web

Stack approvato:

  • React 19: react, react-dom
  • Build/dev: vite, @vitejs/plugin-react
  • Routing: react-router-dom 7
  • UI: shadcn/ui generato localmente, Radix, lucide-react
  • Styling: Tailwind CSS 4, @tailwindcss/vite, tailwindcss-animate
  • State/data: @reduxjs/toolkit, react-redux
  • i18n: react-intl
  • Forms: react-hook-form, @hookform/resolvers, zod
  • Test: vitest, jsdom, @testing-library/react, @testing-library/jest-dom, @testing-library/user-event
  • Tooling: typescript

Dipendenze UI base:

text
class-variance-authority
clsx
tailwind-merge
lucide-react
tailwindcss-animate
@radix-ui/react-slot
@radix-ui/react-dialog
@radix-ui/react-dropdown-menu
@radix-ui/react-tooltip
@radix-ui/react-popover
@radix-ui/react-tabs
@radix-ui/react-checkbox
@radix-ui/react-select
@radix-ui/react-label

shadcn/ui non va trattato come dipendenza runtime: genera componenti locali in src/components/ui.

bash
cd apps/web
npx shadcn@latest init

Struttura consigliata:

text
src/
  app/
    router.tsx
    providers.tsx
  components/
    ui/
    domain/
    layout/
  features/
    example/
      components/
      hooks/
      state/
      api/
  i18n/
  lib/
  store/
  test/

Regole frontend:

  • src/components/ui contiene componenti generati da shadcn e piccoli wrapper generici.
  • src/components/domain contiene componenti business riusabili tra feature.
  • Le feature tengono insieme componenti, hook, slice Redux e client /api/* del proprio dominio.
  • I componenti non devono accedere direttamente ai servizi CORE o a URL cross-origin.
  • Usa lucide-react per icone di azione e Radix/shadcn per controlli accessibili.

Alias richiesto in tsconfig.json:

json
{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["./src/*"]
    }
  }
}

Alias corrispondente in vite.config.ts:

ts
import react from "@vitejs/plugin-react";
import tailwindcss from "@tailwindcss/vite";
import { fileURLToPath, URL } from "node:url";
import { defineConfig } from "vite";

export default defineConfig({
  plugins: [react(), tailwindcss()],
  resolve: {
    alias: {
      "@": fileURLToPath(new URL("./src", import.meta.url)),
    },
  },
  server: {
    proxy: {
      "/api": "http://localhost:3001",
    },
  },
});

Tailwind deve includere almeno i sorgenti Vite/React:

ts
export default {
  content: ["./index.html", "./src/**/*.{ts,tsx}"],
};

packages/shared

Stack approvato:

  • typescript
  • zod solo quando servono validatori condivisi tra apps/api e apps/web

Struttura consigliata:

text
src/
  index.ts
  constants/
  dto/
  schemas/
  types/

Linee guida:

  • Esporta tipi e funzioni pure, senza dipendenze da NestJS, React, Vite o Prisma.
  • I validatori condivisi possono stare in schemas, poi apps/api li adatta ai DTO se necessario.
  • Non usare questo package per logica applicativa, chiamate HTTP o stato UI.

Integrazione con lo stack globale

Quando il nuovo progetto entra nel workspace operativo:

  • Aggiungi repository, porte e domini in platform-local-stack/config/stack-paths.json.
  • Aggiungi il servizio ai profili interessati in platform-local-stack/config/profiles/*.yaml.
  • Definisci comandi, env runtime e dipendenze infra in platform-local-stack/scripts/stack-definitions.mjs.
  • Esponi route HTTP o WebSocket nel proxy locale in platform-local-stack/scripts/dev-proxy.mjs.
  • Registra prodotto, health target e link nel catalogo /products di platform-status-service; aggiungi smoke test solo quando serve una suite core dedicata.
  • Documenta il modulo in platform-wiki e aggiorna il catalogo moduli.
  • Aggiorna README.md e docs/architecture-current.md se cambiano boundary, dipendenze o flussi runtime.

Boundary da rispettare:

  • I servizi CORE restano product-agnostic.
  • Il nuovo progetto EXPERIENCE contiene dominio prodotto, BFF, flussi utente e UI.
  • I frontend chiamano il BFF tramite /api/*, mai i servizi CORE direttamente.
  • Gli SDK vendor restano nei servizi CORE quando esiste gia un connector condiviso.
  • I tool MCP vanno aggiunti in platform-mcp-service solo con registrar, client HTTP, schema input e risposta normalizzata dedicati.

Checklist finale

Prima di considerare pronto il nuovo progetto:

  • npm install genera un solo lockfile al root del repository.
  • npm run lint, npm run test e npm run build passano dal root.
  • apps/api espone GET /api/health o GET /health, secondo convenzione scelta e documentata.
  • apps/web usa proxy Vite verso il BFF e non contiene URL diretti ai servizi CORE.
  • packages/shared non importa framework runtime.
  • Le variabili richieste sono documentate in .env.example.
  • Le pagine wiki e i profili local-stack sono aggiornati se il repository entra in esercizio operativo.

Workspace reference: /Users/jeanpaul/projects/cs-repository