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

# Service

> Encapsulate business logic in injectable service classes registered with and resolved from the DI container.

The `@ooneex/service` component is the service layer foundation for Ooneex applications. A service is an injectable unit of business logic — order processing, payment handling, email dispatch — that you register with a decorator and resolve from the container. Each service implements the `IService` interface, exposing a single `execute(data?)` entry point, and is wired into `@ooneex/container` so dependencies resolve automatically.

## Why this component

* **Business logic in one place.** Keep domain operations out of controllers and routes; controllers stay thin and delegate to services.
* **Container-managed.** Register a service with `@decorator.service()` and resolve it anywhere with `container.get()` — no manual wiring.
* **One contract.** Every service implements `IService`, so they all share the same `execute(data?)` shape and compose predictably.
* **Scope control.** Choose singleton, transient, or request scope per service to match its lifecycle.
* **Composable.** Services resolve and call other services, building larger operations from smaller units.

## How it works

A service is a plain class decorated with `@decorator.service()`. The decorator calls `container.add(target, scope)`, registering the class with `@ooneex/container`. From then on, `container.get(MyService)` returns an instance with its scope honored — the container constructs it, caches it (for singletons), and resolves any container-backed dependencies it pulls in.

The `IService` contract is intentionally minimal:

| Member           | Purpose                                                                                    |
| ---------------- | ------------------------------------------------------------------------------------------ |
| `execute(data?)` | The service's single entry point. Accepts optional input data and runs the business logic. |

```typescript theme={null}
export interface IService {
  execute: (data?: any) => Promise<any> | any;
}
```

The decorator defaults to the singleton scope, but accepts any `EContainerScope`:

| Scope                       | Lifecycle                                                   |
| --------------------------- | ----------------------------------------------------------- |
| `EContainerScope.Singleton` | One instance shared across the whole application (default). |
| `EContainerScope.Transient` | A new instance on every `container.get()` resolution.       |
| `EContainerScope.Request`   | A new instance per request context.                         |

## Decorator and usage

### `@decorator.service(scope?)`

Registers a service class with the container. It takes an optional `scope` (defaults to `EContainerScope.Singleton`) and returns a class decorator that adds the target to the container.

```typescript theme={null}
import { decorator, type IService } from "@ooneex/service";

interface InvoiceData {
  items: Array<{ price: number; quantity: number }>;
  taxRate: number;
}

@decorator.service()
export class InvoiceService implements IService {
  public async execute(data?: InvoiceData): Promise<number> {
    if (!data) return 0;

    const subtotal = data.items.reduce(
      (sum, item) => sum + item.price * item.quantity,
      0,
    );

    return subtotal * (1 + data.taxRate);
  }
}
```

Resolve it from the container wherever you need it — the decorator already registered it:

```typescript theme={null}
import { container } from "@ooneex/container";
import { InvoiceService } from "./services/InvoiceService";

const invoiceService = container.get(InvoiceService);

const total = await invoiceService.execute({
  items: [{ price: 19.99, quantity: 2 }],
  taxRate: 0.2,
});
```

Pass a scope to change the lifecycle — for example, a fresh instance per resolution:

```typescript theme={null}
import { decorator, type IService } from "@ooneex/service";
import { EContainerScope } from "@ooneex/container";

@decorator.service(EContainerScope.Transient)
export class RequestContextService implements IService {
  private readonly requestId = crypto.randomUUID();

  public async execute(): Promise<string> {
    return this.requestId;
  }
}
```

Services compose by resolving one another from the container, letting a higher-level operation orchestrate smaller services:

```typescript theme={null}
import { container } from "@ooneex/container";
import { decorator, type IService } from "@ooneex/service";
import { InvoiceService } from "./InvoiceService";

interface CheckoutData {
  items: Array<{ price: number; quantity: number }>;
  taxRate: number;
}

@decorator.service()
export class CheckoutService implements IService {
  private readonly invoiceService = container.get(InvoiceService);

  public async execute(data?: CheckoutData): Promise<void> {
    if (!data) return;

    const total = await this.invoiceService.execute(data);
    // ...charge the customer, persist the order, etc.
  }
}
```

## Best practices

* **One responsibility per service.** A service handles a single business operation; compose services rather than growing one into a god class.
* **Keep `execute()` the public entry point.** Put orchestration in `execute()` and break the steps into private methods.
* **Type the input.** Replace the default `Record<string, unknown>` data shape with a precise interface for the service's input.
* **Handle the empty case.** `data` is optional — guard for `undefined` before using it.
* **Resolve, don't construct.** Get services from the container with `container.get()` so scopes and dependencies are honored; avoid `new`.
* **Pick the right scope.** Default to singleton for stateless logic; use transient or request scope only when per-call or per-request state matters.
* **Let controllers stay thin.** Keep routing and HTTP concerns in controllers and delegate the work to services.

## CLI command

Scaffold a service class and its test file with the generator. It writes the class under `modules/<module>/src/services/<Name>Service.ts`, adds a matching test, and installs `@ooneex/service` if it is missing.

```bash theme={null}
# Interactive: prompts for the name
ooneex service:create

# Provide the name
ooneex service:create --name=Invoice

# Target a module and overwrite
ooneex service:create --name=Invoice --module=billing --override
```

| Option       | Description                                                         | Default             |
| ------------ | ------------------------------------------------------------------- | ------------------- |
| `--name`     | Service class name. The `Service` suffix is appended automatically. | Prompted if omitted |
| `--module`   | Target module the class is generated into.                          | `shared`            |
| `--override` | Overwrite an existing class without prompting.                      | `false`             |

The generated class is a ready-to-fill `IService` stub:

```typescript theme={null}
import { decorator } from "@ooneex/service";
import type { IService } from "@ooneex/service";

type ServiceDataType = Record<string, unknown>;

@decorator.service()
export class InvoiceService implements IService {
  // biome-ignore lint/suspicious/noExplicitAny: trust me
  public async execute(data?: ServiceDataType): Promise<any> {
    // TODO: Implement service logic
  }
}
```

See [service:create](/cli/commands/service-create) for the full command reference.

## Use with Claude and Codex

The generator ships a matching `service:create` skill. It runs the scaffold and then guides your AI agent through completing the service — defining a real `ServiceDataType`, implementing `execute()` with the business logic, and injecting dependencies. Initialize the skills once for your agent:

<Tabs>
  <Tab title="Claude">
    ```bash theme={null}
    ooneex claude:init
    ```

    Then ask Claude in natural language — it maps the request to the generator, runs it, and fills in the implementation:

    ```text Prompt icon="terminal" wrap theme={null}
    Create a service that calculates invoice totals.
    ```
  </Tab>

  <Tab title="Codex">
    ```bash theme={null}
    ooneex codex:init
    ```

    Then ask Codex in natural language — it maps the request to the generator, runs it, and fills in the implementation:

    ```text Prompt icon="terminal" wrap theme={null}
    Create a service that calculates invoice totals.
    ```
  </Tab>
</Tabs>

For example, the prompt above maps to `service:create --name=Invoice`, then implements `execute()` to compute the totals.
