Skip to content
RELKIT
Esc
↑↓navigate↵open⌘Jpreview
On this page

Services and domains

Understand the service layer that organizes your app's functions, events, resources, tools, and agents by domain.

A service groups related application behavior and exposes the operations other parts of the application can call. Start with the part of the product you are building—orders, receipts, accounts—not with the technology you will use to build it. Give that area a domain folder, keep its behavior and resources together, and expose its public operations through one service.ts.

Pre-setup

Start with a minimal app and retain its defineApp configuration and defineEnv contract. A plain service needs no provider profile or Docker service. When adding a database, bucket, cache, job, event, or agent to the domain, complete that capability’s provider setup first. Local Docker is the default for capabilities that require an external service; registering a service does not configure them automatically.

Domain, service, and function

These three terms describe different things:

Term Meaning Example
Domain A cohesive area of application responsibility, including its private implementation. src/services/orders/ owns order creation, pricing, order events, and order-support tools.
Service The domain’s public boundary, assembled in one service.ts. orders.createOrder and orders.orderCreated are selected public members.
Function A validated operation that does the work. create-order.function.ts calculates a total and publishes an event.

When we say a service contains tools, agents, caches, tasks, or jobs, we mean they belong to its domain. defineService accepts public functions, events, tasks, and jobs; it is not a registry of every file or a container that injects resources. A function remains the immediate execution unit, while a task owns durable execution.

The application architecture

relkit.config.ts              application settings and provider bindings
src/
├── platform/                 shared environment and infrastructure support
├── services/
│   ├── orders/               an application domain
│   │   ├── service.ts        public functions, events, tasks, and jobs
│   │   ├── functions/        business operations and event subscribers
│   │   ├── events/           facts this domain publishes
│   │   ├── tools/            model-callable operations
│   │   ├── agents/           bounded model workflows
│   │   └── cache/            domain-owned cached data
│   └── receipts/             another domain, with its own service.ts
│       ├── service.ts
│       ├── functions/
│       ├── tasks/
│       └── jobs/
└── routes/                   URL paths, HTTP methods, and request policy

This is an ownership map, not a checklist of folders to create. Start with a service and one useful function. Add the other directories only when the domain needs those capabilities. routes and platform are special layers, not application domains, and do not need a domain service.ts.

The compiler discovers exported descriptors, checks their relationships, and produces an application graph and runtime manifest. The runtime, Inspector, and deployment tooling use that checked model. Application authors use ordinary TypeScript descriptors; no Effect imports are required.

Existing flat src/<name>/service.ts apps remain supported. Scaffolding preserves their detected layout, while empty projects default to grouped. Keep one boundary shape throughout the app; src/services/service.ts is not an aggregate boundary. See optional migration.

Follow an operation through the layers

In the commerce example, an HTTP route selects orders.createOrder. The function validates domain input and reads the Orders price cache. The event examples show how a producing service can publish a fact and a receiving service can own its reaction; jobs describe durable work.

Each part has a different job:

  • The route handles HTTP input and response mapping.
  • The service makes selected domain operations available to callers.
  • The function owns business behavior and declares the resources it needs.
  • The event contract describes what happened; subscriber functions own the reactions.
  • Tools and agents reuse domain behavior without relocating it into an AI-specific application layer.

A service does not create an HTTP endpoint automatically. Nor does adding a member grant a user permission to call it. Transport exposure and authorization remain explicit.

Read a real public boundary

This is the larger commerce reference, whose resource/provider packages are already selected. For a lean first app, use Your first service; install a capability from its linked guide before copying that capability’s members into your own domain.

/** Declares the orders service boundary without acquiring native clients. */
import { defineService } from "@relkit/app/services";
import createOrder from "./functions/create-order.function.js";
import deleteOrder from "./functions/delete-order.function.js";
import getOrder from "./functions/get-order.function.js";
import searchOrders from "./functions/search-orders.function.js";
import updateOrder from "./functions/update-order.function.js";
import authorizeOrder from "./functions/authorize-order.function.js";
import streamOrderReport from "./functions/stream-order-report.function.js";
import cleanupOrders from "./jobs/cleanup-orders.job.js";
import exportOrders from "./jobs/export-orders.job.js";
import cleanupOrdersTask from "./tasks/cleanup-orders.task.js";
import exportOrdersTask from "./tasks/export-orders.task.js";

export default defineService({
  functions: {
    createOrder,
    deleteOrder,
    getOrder,
    searchOrders,
    updateOrder,
    authorizeOrder,
    streamOrderReport,
  },
  tasks: { cleanupOrdersTask, exportOrdersTask },
  jobs: { cleanupOrders, exportOrders },
});

Notice what is absent: the Orders cache, support agent, tools, and event subscribers. They still belong to Orders and are discovered from their own files. They are not public service members.

Choose your next step

Read what a domain is and when to split one, then use where to put your code as the map for each capability. For implementation, follow define a service, expose it through routes, or the first-service example.

Reference and source