Skip to content

Why Nwire

The problem

Every Node framework I've used (Express, Koa, NestJS, Fastify) is built around HTTP. The framework knows what a request is, what a controller is, what a response is. It does not know what an action is. It does not know what a domain event is. It does not know what an aggregate is or what a state transition means.

So when you ship the app and want to know:

  • "Which submissions are stuck waiting on a reviewer?"
  • "What's the p95 latency of the grading flow specifically — not just any HTTP request that touches it?"
  • "How often does the auto-grader fall back to manual review?"
  • "Are all my workflows actually firing the way I declared, or has someone introduced drift?"

…you can't ask the framework. You have to bolt on a domain model in your APM tags, hope the developer remembers, and reconstruct it from spans.

The bet

If the framework's primitives are the domain — defineAction, defineActor, defineEvent, defineProjection, defineWorkflow — then the framework can answer those questions directly. And its companion tools (Studio, telemetry exporters, scaffolds) can do the same.

That's Nwire.

The six principles

(Full text: Architecture principles.)

  1. Screaming architecture. The file tree says what the system does, not what it's built with. modules/submissions/, not modules/services/.
  2. Self-identifying files. Every filename reveals role + context. submit-answer.action.ts, not submit.ts.
  3. Conservation of meaning. Names preserve the user's reality through every layer. Code is the last link in the narrative chain.
  4. One-author coherence. Every file looks like the same person wrote it.
  5. Artisan engineering. Every decision traces to "why this serves the user." Engineers AND artists.
  6. Modeling ⊥ deployment. Domains never import from wires. Same modules run as monolith, split services, or queue workers — Nwire's topology composer materializes any shape.

What you give up

  • Class-based service composition. Nwire is functions + composition. No decorators, no metadata reflection. If you love NestJS's DI, awilix is available as @nwire/container-awilix.
  • One-size-fits-all routing. HTTP, queue, CLI, cron each get their own wire. They consume the same actions; they don't share a single "controller" surface.
  • Stack traces as the primary debugging tool. Stack traces still work, but the canonical debugging tool is the telemetry stream + Studio's correlation trace tree. You read what the system did, not what this function did.

What you get

  • Studio that natively shows your domain
  • Telemetry stream that's structured by primitive, not by HTTP span
  • Deployment topology as data
  • Test harness that boots the whole app in-process
  • Reusable common modules (auth, billing, notifications — coming) on the same substrate

When NOT to use Nwire

  • You're building a static site or a thin proxy. Use Vite + the fetch handler.
  • You're building a single-purpose CLI. Use commander.
  • You're building real-time games / collaborative editing where the bottleneck is the network, not the domain. Use a CRDT framework.
  • Your team prefers classes / decorators / Java-flavored TS. NestJS will feel more natural.

For everything else — line-of-business apps, education platforms, fintech, e-commerce, support systems — Nwire's bet is that domain-native primitives beat HTTP-native primitives. The next pages walk through each primitive.

MIT licensed.