> For the complete documentation index, see [llms.txt](https://docs.soapjs.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.soapjs.com/components/service.md).

# 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`.
