Jumentix
Distributed SaaS blueprint

Scale services without rewriting communication

Use independent workers and contract-based mediation so in-process requests can move to RabbitMQ, BullMQ, or another transport.

Jumentix open-source mascot

What this blueprint gives your team

  • Independent service ownership
  • Request/response and publish/listen patterns
  • Per-service runtimes, tests, and deployment
bun run pm2:start:dev:restapi
bun run pm2:start:staging:restapi
bun run pm2:start:prod:restapi

From zero to first MVP

A practical launch sequence for this blueprint

Use this path when the first MVP already needs independent ownership or a background capability that should not share the main application lifecycle.

Extract only one boundary

Choose one bounded context or worker behavior, such as task notification, category analytics, or async import.

  • Pick one worker behavior: notify an assignee when a task is created.
  • Freeze the versioned subject tasks.created.v1 and its payload example.
  • Derive the payload type from the contract example so producer and worker share one source.
  • Write acceptance criteria: producer and worker import only contracts.ts.

MVP output: one service boundary with a clear subject and payload contract.

export const TaskCreatedEvent = {
  subject: 'tasks.created.v1',
  example: {
    id: 'task-1',
    title: 'Notify assignee',
    categoryId: 'work'
  }
} as const;

export type TaskCreatedPayload = typeof TaskCreatedEvent.example;

Definition of done for the first MVP

  • Producer and worker import only contracts.ts — never each other.
  • The in-memory mediator proves publish/subscribe before any broker is introduced.
  • The worker runs under its own named profile and restarts without producer changes.
  • Failure is explicit: the poison message lands in the DLQ and the replay is measured.

Deliberately out of scope

  • Service mesh and distributed tracing.
  • Multiple brokers or transport migration.
  • Dead-letter topology beyond the first explicit retry.
  • Extracting additional bounded contexts.

MVP scope

Keep the first release small enough to prove

One service boundary

Extract the smallest behavior with a clear owner and message contract.

Ownership slice

One durable path

Add broker durability only after the in-memory contract proves the interaction.

Messaging slice

One independent gate

Run service-focused tests and deployment checks for the extracted boundary.

Governance slice
Proof areaQuestion to answerJumentix mechanismMVP evidence
CompatibilityCan producer and consumer evolve without importing each other?Message contract tests and subject/payload examples.The contract owns compatibility.
IsolationCan this service fail without hiding the failure?Retry, error contract, dead-letter or fallback evidence.Failure mode is explicit.
OwnershipCan one team ship the service independently?Per-service profile, tests and release evidence.Independent ownership is real, not organizational theater.

Practical implementation

Complete code for the first working slice

These examples keep Category and Task as the product vocabulary and show the controller, contract, client, worker, or state layer needed to reach a runnable MVP.

export const TaskCreatedEvent = {
  subject: 'tasks.created.v1',
  example: {
    id: 'task-1',
    title: 'Notify assignee',
    categoryId: 'work'
  }
} as const;

export type TaskCreatedPayload = typeof TaskCreatedEvent.example;

Build the product. Keep the architecture.

Explore the source, run the factory locally, and turn your next Node.js service into a repeatable platform capability.