Skip to content

Topology

Deployment shape is what your wire entry file says. Nwire doesn't ship a "topology manifest" primitive. Each deployable is a small TypeScript file under apps/<name>/__wires__/ (or wires/<name>.ts) that calls httpInterface() (or queueInterface(), cronInterface(), …), wires the apps it should host, and calls .run(). Monolith vs split is a question of how many wire files you write — same modules, same code.

Monolith — one wire, all apps

ts
// apps/main/__wires__/all.ts
import { httpInterface } from "@nwire/http"
import { lmsApp }         from "@amit/lms"
import { lxApp }          from "@amit/lx"
import { competencyApp }  from "@amit/competency"

await httpInterface({
  port:    Number(process.env.PORT ?? 3000),
  inspect: true,
  openapi: { info: { title: "AMIT", version: "1.0.0" } },
})
  .wire(lmsApp)
  .wire(lxApp)
  .wire(competencyApp)
  .run()

All three apps share one HTTP server, one bus, one container. The default for nwire dev.

Split — one wire per service

ts
// apps/lms/__wires__/main.ts
import { httpInterface } from "@nwire/http"
import { natsBus }       from "@nwire/bus-nats"
import { lmsApp }        from "@amit/lms"

await httpInterface({
  port: Number(process.env.PORT ?? 3001),
  bus:  natsBus({ servers: process.env.NATS_URL }),
})
  .wire(lmsApp)
  .run()
ts
// apps/lx/__wires__/main.ts — same shape, different port + app
import { httpInterface } from "@nwire/http"
import { natsBus }       from "@nwire/bus-nats"
import { lxApp }         from "@amit/lx"

await httpInterface({
  port: Number(process.env.PORT ?? 3002),
  bus:  natsBus({ servers: process.env.NATS_URL }),
})
  .wire(lxApp)
  .run()

Cross-app events go through the bus. Same modules. Deploy each wire as its own container.

Adding a new deployment

Add one file under __wires__/. Wire it in your package.json scripts (or let the CLI auto-discover it). No central manifest to update.

jsonc
// apps/main/package.json
{
  "scripts": {
    "dev:all":  "vite-node __wires__/all.ts",
    "dev:lms":  "PORT=3001 vite-node __wires__/lms.ts",
    "dev:lx":   "PORT=3002 vite-node __wires__/lx.ts"
  }
}

Provider swapping

The bus, actor store, projection store, logger — everything that ships an adapter — is swapped at the wire layer:

ts
import { mongoActorStore }    from "@nwire/store-mongo"
import { mongoProjectionStore } from "@nwire/store-mongo"
import { pinoLogger }         from "@nwire/logger-pino"

await httpInterface({
  port:           3000,
  actorStore:     mongoActorStore({ uri: process.env.MONGO_URL! }),
  projectionStore: mongoProjectionStore({ uri: process.env.MONGO_URL! }),
  logger:         pinoLogger({ level: "info" }),
})
  .wire(app)
  .run()

In dev, omit them — InMemory defaults are inline in every contract package.

See also

MIT licensed.