# RPC Services

Das RPC-Service-System in `surf-rabbitmq` erlaubt es, normale Kotlin-Interfaces als entfernte Services über RabbitMQ aufzurufen. Statt manuell Request- und Response-Packets zu bauen, definierst du ein gemeinsames Service-Interface, registrierst serverseitig eine Implementierung und erzeugst clientseitig einen Proxy.

### Grundidee

Ein RPC-Service ist ein Interface, das mit `@RpcService` markiert wird:

```kotlin
@RpcService
interface UserService {
    suspend fun findUserName(userId: UUID): String?
    suspend fun updateUserName(userId: UUID, name: String)
}
```

Der KSP-Prozessor von `surf-rabbitmq` verarbeitet diese Interfaces und generiert daraus den Service-Descriptor sowie die clientseitige Proxy-Implementierung. Diese werden von `ClientRabbitMQApi.createRpcService` und `ServerRabbitMQApi.registerRpcService` verwendet.

### Anforderungen

Ein RPC-Service muss folgende Regeln erfüllen:

* Das Service-Contract muss ein Interface sein.
* Das Interface muss mit `@RpcService` annotiert sein.
* Die RPC-Methoden müssen `suspend`-Funktionen sein.
* Das gleiche Interface muss auf Client- und Server-Seite im Classpath liegen (am besten in einem common Modul).
* Methodennamen, Parameter, Rückgabetypen und Serializer müssen auf beiden Seiten kompatibel sein.
* Parameter- und Rückgabetypen müssen über `kotlinx.serialization` serialisierbar sein.
* Methodennamen müssen einzigartig sein. Methoden, die den gleichen Namen und unterschiedliche Parameter haben, sind also nicht erlaubt.

Erlaubt sind zum Beispiel primitive Typen, `@Serializable`-Klassen, kontextuelle Serializer aus dem `SerializersModule` oder Typen mit explizitem Serializer über `@Serializable(with = ...)`.

### Server-Seite

Auf der Server-Seite implementierst du das Interface normal:

```kotlin
class UserServiceImpl : UserService { // objects sind auch erlaubt
    override suspend fun findUserName(userId: UUID): String? {
        return userRepository.findName(userId)
    }

    override suspend fun updateUserName(userId: UUID, name: String) {
        userRepository.updateName(userId, name)
    }
}
```

Danach wird die Implementierung an der `ServerRabbitMQApi` registriert:

```kotlin
serverApi.registerRpcService<UserService>(UserServiceImpl())
serverApi.freezeAndConnect()
```

Services sollten vor `freeze()` oder `freezeAndConnect()` registriert werden, weil die RabbitMQ-Handler beim Einfrieren finalisiert werden.

Ein Service kann auch wieder entfernt werden:

```kotlin
serverApi.unregisterRpcService<UserService>()
```

### Client-Seite

Auf der Client-Seite erzeugst du einen Proxy:

```kotlin
val userService = clientApi.createRpcService<UserService>()
```

Danach kannst du die Methoden so aufrufen, als wäre der Service lokal:

```kotlin
val name = userService.findUserName(userId)
userService.updateUserName(userId, "Alex")
```

Intern wandelt der generierte Proxy den Methodenaufruf in einen RabbitMQ-RPC-Call um.

### Was wird generiert?

Für jedes `@RpcService`-Interface erzeugt KSP:

* einen Descriptor,
* eine clientseitige Implementierung.

Der Descriptor beschreibt den Service mit Namen, Fully Qualified Name und verfügbaren Callables. Ein Callable steht für eine RPC-Methode und enthält unter anderem Methodenname, Return-Type, Parameter und Invoker.

### Serialisierung

Alle Parameter und Rückgabewerte laufen über die `kotlinx.serialization`-Konfiguration der jeweiligen API. Client und Server müssen kompatible Serializer verwenden.

```kotlin
@Serializable
data class UserDto(val id: @Contextual UUID, val name: String)
```

Mit contextual serializer:

```kotlin
@RpcService
interface UserService {
    suspend fun findUser(userId: @Contextual UserId): @Contextual UserDto // @Contextual ist nicht zwingend nötig. Es wird das serializer module aus der rabbit api verwendet
}
```

Mit explizitem Serializer:

```kotlin
@RpcService
interface UserService {
    suspend fun findUser(
        userId: @Serializable(with = UserIdSerializer::class) UserId
    ): @Serializable(with = UserDtoSerializer::class) UserDto
}
```


---

# 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.slne.dev/surf-rabbitmq/core-concepts/rpc-services.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.
