# Service Traits — Defining Interfaces
The first step in defining a service is the trait — the contract between the service and its callers.
## Define the Interface
```rust
use id_effect::{Effect};
// The interface contract
trait UserRepository: Send + Sync {
fn get_user(&self, id: u64) -> Effect<User, DbError, ()>;
fn save_user(&self, user: &User) -> Effect<(), DbError, ()>;
fn list_users(&self) -> Effect<Vec<User>, DbError, ()>;
}
```
A few conventions:
- Methods return `Effect<_, _, ()>` — the service itself has `R = ()` because it doesn't need additional context. The `R` of callers (who access the service through the environment) is what carries the requirement.
- `Send + Sync` on the trait means implementations can be stored in `Arc<dyn Trait>` and shared across fibers.
- Method names are verb-oriented: `get_user`, `save_user`, not `user` or `users`.
## Define the Tag
Each service needs a tag that identifies it in the environment:
```rust
use id_effect::service_key;
// Associates UserRepositoryTag with Arc<dyn UserRepository>
service_key!(UserRepositoryTag: Arc<dyn UserRepository>);
```
`service_key!` generates:
- A zero-sized `UserRepositoryTag` type
- A `Tag` implementation that maps `UserRepositoryTag` → `Arc<dyn UserRepository>`
- A `NeedsUserRepository` supertrait that functions can use in bounds
## Define the Needs Supertrait
```rust
use id_effect::{Get};
// Generated by service_key! or defined manually
pub trait NeedsUserRepository: Get<UserRepositoryTag> {}
impl<R: Get<UserRepositoryTag>> NeedsUserRepository for R {}
```
Now any function that uses the user repository declares this:
```rust
fn get_user_profile(id: u64) -> Effect<UserProfile, AppError, impl NeedsUserRepository> {
effect! {
let repo = ~ UserRepositoryTag; // get the service
let user = ~ repo.get_user(id);
UserProfile::from(user)
}
}
```
The compiler checks that `NeedsUserRepository` is satisfied before the function can run.
## Keeping Traits Focused
A common mistake is defining one massive `AppService` trait with everything in it. Prefer small, focused traits:
```rust
// BAD — one God trait
trait AppService {
fn get_user(&self, id: u64) -> Effect<User, AppError, ()>;
fn send_email(&self, to: &str, body: &str) -> Effect<(), AppError, ()>;
fn charge_card(&self, amount: u64) -> Effect<(), AppError, ()>;
}
// GOOD — separate concerns
trait UserRepository { fn get_user(&self, id: u64) -> Effect<User, DbError, ()>; }
trait EmailService { fn send_email(&self, to: &str, body: &str) -> Effect<(), EmailError, ()>; }
trait PaymentGateway { fn charge(&self, amount: u64) -> Effect<(), PaymentError, ()>; }
```
Small traits are composable. Functions declare exactly which services they need — `NeedsUserRepository + NeedsEmailService` — and the compiler enforces it.