# Service

A **service** is an outbound integration — a client for an external system (a REST API, email, payments, a message broker). Like a repository, it sits behind a **port**: your use cases depend on the tech-free contract, and a concrete adapter makes the call. Swap providers by swapping the adapter; the domain never changes.

### Port — the contract + DI token

Define the port as an abstract class with a static `Token`, so the type and the DI token share one name and handlers never import the adapter:

```typescript
import { Result } from '@soapjs/soap';

export abstract class WeatherService {
  static readonly Token = 'WeatherService';
  abstract getByCity(city: string): Promise<Result<Weather>>;
}
```

### Adapter — the implementation

The adapter performs the actual call and maps the response into your domain, returning a `Result`. It lives in the feature's `data/` folder and is the only place that knows the external API:

```typescript
import { Result, Failure } from '@soapjs/soap';

export class OpenWeatherService implements WeatherService {
  constructor(private readonly apiKey: string) {}

  async getByCity(city: string): Promise<Result<Weather>> {
    try {
      const res = await fetch(`https://api.example.com/weather?q=${city}&key=${this.apiKey}`);
      if (!res.ok) return Result.withFailure(Failure.fromError(new Error(`Weather API ${res.status}`)));
      const data = await res.json();
      return Result.withSuccess(new Weather(data.city, data.tempC));
    } catch (error) {
      return Result.withFailure(Failure.fromError(error));
    }
  }
}
```

### Wire it in the composition root

```typescript
container.bindValue(WeatherService.Token, new OpenWeatherService(config.weatherApiKey));
```

Inject the **port** wherever you need it — a use case or a command handler:

```typescript
constructor(@Inject(WeatherService.Token) private readonly weather: WeatherService) {}

const result = await this.weather.getByCity('Gotham');
```

### Service vs Repository

Both are outbound ports + adapters; the difference is intent:

|          | Repository                       | Service                       |
| -------- | -------------------------------- | ----------------------------- |
| Talks to | your own data store              | an external system / API      |
| Returns  | your entities                    | data mapped into your domain  |
| Built on | `ReadWriteRepository` + a Source | a plain class (or any client) |

Because a service is just a port, it's trivial to stub in tests — bind an in-memory fake under the same `Token`.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.soapjs.com/components/service.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
