> ## Documentation Index
> Fetch the complete documentation index at: https://docs.devinenterprise.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Suddividere un router monolitico di Express in route di dominio

export const UseCaseHero = ({title, description, prompt, category, features, devinUrl, agent, intent, playbookId, type}) => {
  const encodedPrompt = encodeURIComponent(prompt || '');
  const tag = 'docs-use-case-gallery';
  const utm = 'utm_source=docs&utm_medium=use-case-gallery&utm_campaign=hero-cta';
  const agentParams = (agent ? '&agent=' + agent : '') + (intent ? '&intent=' + intent : '') + (playbookId ? '&playbookId=' + playbookId : '');
  const devinHref = type === 'schedule' ? 'https://app.devin.ai/settings/schedules/create?' + utm + agentParams + (prompt ? '&prompt=' + encodedPrompt : '') : type === 'review' ? 'https://app.devin.ai/review?' + utm : agent === 'ada' ? 'https://app.devin.ai/search?' + utm + '&noSubmit=true' + (prompt ? '&prompt=' + encodedPrompt : '') : devinUrl ? devinUrl.includes('?') ? devinUrl + '&' + utm + agentParams : devinUrl + '?' + utm + agentParams : prompt ? 'https://app.devin.ai/?tags=' + tag + '&' + utm + agentParams + '&prompt=' + encodedPrompt : 'https://app.devin.ai/?' + utm + agentParams;
  const buttonLabel = type === 'schedule' ? 'Schedule in Devin ↗' : type === 'review' ? 'Set Up Devin Review ↗' : agent === 'advanced' ? 'Try in Devin ↗' : agent === 'dana' ? 'Try in Dana ↗' : agent === 'ada' ? 'Try in Ask Devin ↗' : 'Try in Devin ↗';
  const featureList = features ? features.split(',').map(f => f.trim()) : [];
  return <div className="uc-hero">
      <div className="uc-hero-inner">
        <div className="uc-hero-left">
          <h1 className="uc-hero-title">{title}</h1>
          <p className="uc-hero-desc">{description}</p>
          <div>
            <a href={devinHref} target="_blank" rel="noopener noreferrer" className="try-in-devin-btn">
              {buttonLabel}
            </a>
          </div>
        </div>
        <div className="uc-hero-meta">
          <div className="uc-meta-item">
            <span className="uc-meta-label">Author</span>
            <span className="uc-meta-value">Cognition</span>
          </div>
          <div className="uc-meta-item">
            <span className="uc-meta-label">Category</span>
            <span className="uc-meta-value">{category}</span>
          </div>
          {featureList.length > 0 && <div className="uc-meta-item">
              <span className="uc-meta-label">Features</span>
              <span className="uc-meta-value">{featureList.join(', ')}</span>
            </div>}
        </div>
      </div>
    </div>;
};

export const PromptBlock = ({children, type, agent, intent, playbookId}) => {
  var utm = 'utm_source=docs&utm_medium=use-case-gallery&utm_campaign=prompt-block';
  var tag = 'docs-use-case-gallery';
  var agentParams = (agent ? '&agent=' + agent : '') + (intent ? '&intent=' + intent : '') + (playbookId ? '&playbookId=' + playbookId : '');
  var label = type === 'schedule' ? 'Schedule in Devin' : type === 'playbook' ? 'Create Playbook' : type === 'knowledge' ? 'Add to Knowledge' : agent === 'advanced' ? 'Try in Devin' : agent === 'dana' ? 'Try in Dana' : agent === 'ada' ? 'Try in Ask Devin' : 'Try in Devin';
  var buildUrl = function (text) {
    var encoded = encodeURIComponent(text);
    if (type === 'schedule') return 'https://app.devin.ai/settings/schedules/create?' + utm + agentParams + '&prompt=' + encoded;
    if (type === 'playbook') return 'https://app.devin.ai/settings/playbooks/create?' + utm + '&body=' + encoded;
    if (type === 'knowledge') return 'https://app.devin.ai/knowledge?' + utm + '&body=' + encoded;
    if (agent === 'ada') return 'https://app.devin.ai/search?' + utm + '&noSubmit=true&prompt=' + encoded;
    return 'https://app.devin.ai/?tags=' + tag + '&' + utm + agentParams + '&prompt=' + encoded;
  };
  const ref = React.useRef(null);
  const [href, setHref] = React.useState('#');
  React.useEffect(() => {
    if (!ref.current) return;
    var codeEl = ref.current.querySelector('pre code');
    if (codeEl) {
      var text = codeEl.textContent.trim();
      if (text) setHref(buildUrl(text));
    }
    var header = ref.current.querySelector('[data-component-part="code-block-header"]');
    if (header && !header.querySelector('.prompt-block-devin-link')) {
      var link = document.createElement('a');
      link.href = href;
      link.target = '_blank';
      link.rel = 'noopener noreferrer';
      link.className = 'prompt-block-devin-link';
      link.style.cssText = 'display:inline-flex;align-items:center;gap:6px;text-decoration:none;color:#fff;font-size:11px;font-weight:500;padding:4px 10px;border-radius:6px;white-space:nowrap;background:#317CFF;transition:background 0.2s;margin-left:8px;';
      link.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg> ' + label;
      link.onmouseenter = function () {
        link.style.background = '#2968D9';
      };
      link.onmouseleave = function () {
        link.style.background = '#317CFF';
      };
      header.appendChild(link);
    }
    var existingLink = ref.current.querySelector('.prompt-block-devin-link');
    if (existingLink && href !== '#') existingLink.href = href;
  });
  return <div className="prompt-block" ref={ref}>{children}</div>;
};

<UseCaseHero title="Suddividere un router Express monolitico in route di dominio" description="Devin suddivide un router Express di 2.000 righe in file di route specifici per dominio con middleware condiviso — quindi aggiorna tutti gli import e verifica che tutti i test vengano superati." prompt="Esegui il refactoring di src/routes/index.ts — è un router Express monolitico di 2.000 righe che gestisce utenti, ordini, prodotti e pagamenti. Suddividilo in file di route specifici per dominio in src/routes/, estrai il middleware condiviso in src/middleware/ e rimonta tutto in un router principale. Tutti i 112 test delle API devono continuare a passare." category="Qualità del codice" features="" />

<div className="uc-detail-wrapper">
  <Tip>Non vuoi configurarlo manualmente? Incolla un collegamento a questa pagina in una sessione di Devin e chiedigli di impostare tutto per te.</Tip>

  <Steps>
    <Step title="Mostra il monolite a Devin">
      Conosci il file — un unico router Express cresciuto per diciotto mesi. Ogni endpoint per ogni dominio vive in `src/routes/index.ts`: la registrazione utente accanto ai webhook di pagamento accanto alla ricerca dei prodotti. I controlli di autenticazione inline sono copiati e incollati in 40 handler diversi. Nessuno vuole toccarlo perché una modifica alla logica degli ordini potrebbe compromettere gli endpoint utente trecento righe più sopra.

      Ecco come di solito si presenta l'inizio del file:

      ```typescript src/routes/index.ts (before — 2,000 lines) theme={null}
      import { Router } from "express";
      import { db } from "../db";
      import { stripe } from "../lib/stripe";
      import { sendEmail } from "../lib/email";
      import { logger } from "../lib/logger";

      const router = Router();

      // ---- Middleware di autenticazione (copiato ovunque) ----
      const requireAuth = (req, res, next) => {
        const token = req.headers.authorization?.split(" ")[1];
        if (!token) return res.status(401).json({ error: "Unauthorized" });
        try {
          req.user = jwt.verify(token, process.env.JWT_SECRET);
          next();
        } catch {
          res.status(401).json({ error: "Invalid token" });
        }
      };

      // ---- Route utenti ----
      router.post("/users/register", async (req, res) => { /* 45 lines */ });
      router.post("/users/login", async (req, res) => { /* 30 lines */ });
      router.get("/users/:id", requireAuth, async (req, res) => { /* 25 lines */ });
      router.put("/users/:id", requireAuth, async (req, res) => { /* 40 lines */ });

      // ---- Route prodotti ----
      router.get("/products", async (req, res) => { /* 60 lines */ });
      router.get("/products/:id", async (req, res) => { /* 35 lines */ });
      router.post("/products", requireAuth, async (req, res) => { /* 50 lines */ });

      // ---- Route ordini ----
      router.post("/orders", requireAuth, async (req, res) => { /* 80 lines */ });
      router.get("/orders/:id", requireAuth, async (req, res) => { /* 40 lines */ });
      router.post("/orders/:id/refund", requireAuth, async (req, res) => { /* 55 lines */ });

      // ---- Route pagamenti ----
      router.post("/payments/webhook", async (req, res) => { /* 90 lines */ });
      router.get("/payments/:id/status", requireAuth, async (req, res) => { /* 30 lines */ });

      // ... altre 1.400 righe di handler misti, validazione inline,
      //     e controlli di autenticazione duplicati

      export default router;
      ```

      Dì a Devin esattamente come vuoi che sia la struttura finale.

      <PromptBlock>
        ```txt Suddividi il router monolitico in router per dominio theme={null}
        Effettua il refactoring di src/routes/index.ts — è un router Express
        monolitico da 2.000 righe che gestisce utenti, ordini, prodotti e pagamenti in un unico file.

        Problemi:
        - Il middleware di autenticazione è copiato in ogni handler invece di essere condiviso
        - Validazione delle richieste inline (nessuno schema, solo if sparsi)
        - Domini mescolati: registrazione utenti accanto ai webhook di pagamento
        - Nessuna separazione tra definizione delle route e logica degli handler

        Struttura target:
        - src/routes/users.ts      — tutti gli endpoint /users/*
        - src/routes/products.ts   — tutti gli endpoint /products/*
        - src/routes/orders.ts     — tutti gli endpoint /orders/*
        - src/routes/payments.ts   — tutti gli endpoint /payments/*
        - src/routes/index.ts      — router root che monta ciascun dominio
        - src/middleware/auth.ts    — requireAuth e requireAdmin condivisi
        - src/middleware/validate.ts — middleware di validazione delle richieste condiviso

        Requisiti:
        - Estrai il middleware di autenticazione in src/middleware/auth.ts e importalo
        - Raggruppa gli handler per dominio in file separati
        - Mantieni invariati tutti i percorsi delle route (nessuna modifica breaking agli URL)
        - Tutti i 112 test API esistenti in tests/api/ devono passare senza modifiche
        ```
      </PromptBlock>
    </Step>

    <Step title="Guida Devin con le convenzioni">
      Devin legge la tua codebase per individuare i pattern, ma è nel refactoring che le voci in [Knowledge](/it/product-guides/knowledge) sono più utili. Aggiungi voci per le convenzioni che Devin dovrebbe seguire:

      * **Router patterns** — "Ogni domain router utilizza `Router()` ed è montato con `app.use('/domain', domainRouter)` nella root"
      * **Middleware** — "Il middleware di autenticazione si trova in `src/middleware/` ed è sempre importato, mai definito inline"
      * **Error handling** — "Tutti i route handler usano il nostro wrapper `asyncHandler` da `src/lib/asyncHandler.ts` — mai un try/catch diretto"

      Far lavorare Devin su un router già ben strutturato nella tua codebase spesso produce risultati migliori rispetto a descrivere le convenzioni da zero. Aggiungi una riga come "Segui il pattern in `src/routes/admin.ts`, che è già ben separato" al tuo prompt.

      Puoi anche chiedere a Devin di generare voci di Knowledge per te — ti basta descrivere le tue convenzioni e creerà voci ben strutturate che puoi rivedere e salvare.
    </Step>

    <Step title="Rivedi la PR di Devin">
      Devin mappa ogni endpoint, traccia il grafo degli import, estrae la logica condivisa, crea i file di dominio, riconfigura il router root ed esegue la tua suite di test. Ecco come si presenta in genere una PR:

      ```
      refactor: Split monolithic router into domain-specific route files

      Files changed (8):
        src/routes/users.ts        — 4 endpoints, auth middleware imported
        src/routes/products.ts     — 3 endpoints, public + auth-protected
        src/routes/orders.ts       — 3 endpoints, all auth-protected
        src/routes/payments.ts     — 2 endpoints, webhook + status check
        src/routes/index.ts        — root router mounting all domains
        src/middleware/auth.ts      — requireAuth, requireAdmin (extracted)
        src/middleware/validate.ts  — validateBody schema middleware
        Old src/routes/index.ts     — 2,000-line monolith replaced

      All 112 API tests pass. No URL changes.
      ```

      Ecco come si presenta il router root pulito dopo la suddivisione:

      ```typescript src/routes/index.ts (after — 15 lines) theme={null}
      import { Router } from "express";
      import usersRouter from "./users";
      import productsRouter from "./products";
      import ordersRouter from "./orders";
      import paymentsRouter from "./payments";

      const router = Router();

      router.use("/users", usersRouter);
      router.use("/products", productsRouter);
      router.use("/orders", ordersRouter);
      router.use("/payments", paymentsRouter);

      export default router;
      ```

      E un file di route per il dominio, con il middleware condiviso correttamente importato:

      ```typescript src/routes/orders.ts (after — excerpt) theme={null}
      import { Router } from "express";
      import { requireAuth } from "../middleware/auth";
      import { validateBody } from "../middleware/validate";
      import { createOrderSchema, refundSchema } from "../schemas/orders";
      import { db } from "../db";

      const router = Router();

      router.post("/", requireAuth, validateBody(createOrderSchema),
        async (req, res) => {
          const order = await db.orders.create({
            userId: req.user.id,
            items: req.body.items,
            total: req.body.total,
          });
          res.status(201).json(order);
        }
      );

      router.get("/:id", requireAuth, async (req, res) => {
        const order = await db.orders.findByPk(req.params.id);
        if (!order) return res.status(404).json({ error: "Order not found" });
        res.json(order);
      });

      router.post("/:id/refund", requireAuth, validateBody(refundSchema),
        async (req, res) => {
          // logica di rimborso estratta in modo pulito dal monolite
        }
      );

      export default router;
      ```

      Ogni percorso URL rimane invariato — `/orders` è ora gestito da `ordersRouter` montato su `/orders`, quindi i client e i test esistenti continuano a funzionare senza modifiche.
    </Step>

    <Step title="(Facoltativo) Esegui il checkout del branch e testa in locale">
      Per un refactor strutturale di questo tipo, vale la pena fare pull del branch e verificare in locale prima di effettuare il merge. Aprilo in [Devin Desktop](https://windsurf.com) o nel tuo IDE preferito, avvia l'app e prova alcuni endpoint per confermare che routing, middleware e gestione degli errori si comportino esattamente come prima.

      ```bash theme={null}
      git fetch origin && git checkout devin/refactor-router-split
      npm install && npm test
      npm run dev
      # Testa alcuni endpoint: curl http://localhost:3000/users, /orders, /payments
      ```

      Se qualcosa non va, lascia un commento sulla PR — Devin se ne occuperà e invierà una soluzione.
    </Step>

    <Step title="Prosegui la pulizia">
      Una volta che il router è stato suddiviso, utilizza prompt successivi per ampliare il refactoring:

      <PromptBlock>
        ```txt Estrai la validazione inline in schemi theme={null}
        I route handler hanno ancora istruzioni if sparse per la validazione
        dell'input. Estraile in schemi Zod in src/schemas/ — un file di schema
        per dominio (users.ts, orders.ts, ecc.). Usa il middleware validateBody
        che abbiamo appena creato. Tutti i 112 test devono passare.
        ```
      </PromptBlock>

      <PromptBlock>
        ```txt Aggiungi test di integrazione per i nuovi file di route theme={null}
        Ogni router di dominio ora risiede nel proprio file ma condivide la
        suite di test originale. Scrivi test di integrazione mirati per
        src/routes/orders.ts che coprano create, GET e refund — includendo
        errori di autenticazione, errori di validazione e casi limite come
        il tentativo di rimborsare un ordine già rimborsato.
        ```
      </PromptBlock>
    </Step>
  </Steps>
</div>
