npm package: @rvettori/service-pattern
Description: Service pattern implementation with Result type for TypeScript - Single Responsibility Principle
Author: Rafael Vettori
Latest version: 1.0.1

# @rvettori/service-pattern

Service pattern implementation with Result type for TypeScript. Implements Single Responsibility Principle with type-safe error handling.

## 🚀 Features

- ✅ **Result Pattern**: Handle errors without exceptions
- ✅ **Service Interface**: Single responsibility pattern for services
- ✅ **Type-Safe**: Full TypeScript support with generics
- ✅ **Zero Dependencies**: Lightweight and fast
- ✅ **Functional Helpers**: map, flatMap, unwrap, combine, and more
- ✅ **Error Types**: Structured error handling with ErrorDetail

## 📦 Installation

```bash
npm install @rvettori/service-pattern
# or
bun add @rvettori/service-pattern
```

## 🎯 Quick Start

### Basic Service

```typescript
import { IService, Result, Ok, Err, ErrorDetail } from '@rvettori/service-pattern';

interface CreateUserInput {
  email: string;
  password: string;
}

interface User {
  id: number;
  email: string;
}

class CreateUserService implements IService<CreateUserInput, User, ErrorDetail> {
  async execute(input: CreateUserInput): Promise<Result<User, ErrorDetail>> {
    // Validate email
    if (!input.email.includes('@')) {
      return Err({
        code: 'INVALID_EMAIL',
        message: 'Email must be valid'
      });
    }

    // Create user
    const user = { id: 1, email: input.email };
    return Ok(user);
  }
}
```

### Using the Service

```typescript
const service = new CreateUserService();
const result = await service.execute({ 
  email: 'user@example.com', 
  password: 'secret' 
});

if (result.success) {
  console.log('User created:', result.value);
} else {
  console.error('Error:', result.error.message);
}
```

## 📚 API Reference

### Result Pattern

#### `Result<T, E>`

Type representing success or failure:

```typescript
type Result<T, E> = 
  | { success: true; value: T }
  | { success: false; error: E };
```

#### `Ok(value)`

Creates a successful Result:

```typescript
const result = Ok({ id: 1, name: 'John' });
// { success: true, value: { id: 1, name: 'John' } }
```

#### `Err(error)`

Creates an error Result:

```typescript
const result = Err('User not found');
// { success: false, error: 'User not found' }
```

#### `isOk(result)` / `isErr(result)`

Type guards for checking Result state:

```typescript
if (isOk(result)) {
  console.log(result.value); // TypeScript knows it's success
}

if (isErr(result)) {
  console.log(result.error); // TypeScript knows it's error
}
```

#### `map(result, fn)`

Transform the value of a successful Result:

```typescript
const result = Ok(5);
const doubled = map(result, x => x * 2); // Ok(10)
```

#### `flatMap(result, fn)`

Chain operations that return Results:

```typescript
const divide = (x: number) => 
  x !== 0 ? Ok(10 / x) : Err('Division by zero');

const result = Ok(2);
const divided = flatMap(result, divide); // Ok(5)
```

#### `unwrap(result)`

Extract value or throw:

```typescript
const value = unwrap(Ok(42)); // 42
unwrap(Err('error')); // throws Error
```

#### `unwrapOr(result, defaultValue)`

Extract value or return default:

```typescript
unwrapOr(Ok(42), 0); // 42
unwrapOr(Err('error'), 0); // 0
```

#### `combine(results)`

Combine multiple Results into one:

```typescript
const results = [Ok(1), Ok(2), Ok(3)];
combine(results); // Ok([1, 2, 3])

const withError = [Ok(1), Err('error'), Ok(3)];
combine(withError); // Err('error')
```

### Service Interfaces

#### `IService<Input, Output, Error>`

Base interface for all services:

```typescript
interface IService<Input, Output, Error = string> {
  execute(input: Input): Promise<Result<Output, Error>>;
}
```

#### `IServiceNoInput<Output, Error>`

For services without input parameters:

```typescript
class ListUsersService implements IServiceNoInput<User[], ErrorDetail> {
  async execute(): Promise<Result<User[], ErrorDetail>> {
    // implementation
  }
}
```

#### `IServiceSync<Input, Output, Error>`

For synchronous services:

```typescript
class ValidateEmailService implements IServiceSync<string, boolean, ErrorDetail> {
  execute(email: string): Result<boolean, ErrorDetail> {
    // synchronous validation
  }
}
```

### Error Types

#### `ErrorDetail<Code>`

Structured error with code and message:

```typescript
interface ErrorDetail<Code extends string = string> {
  code: Code;
  message: string;
  details?: any;
}
```

#### `createError(code, message, details?)`

Factory function for creating errors:

```typescript
const error = createError('USER_NOT_FOUND', 'User not found', { id: 123 });
```

## 🏗️ Architecture Example

### Domain Layer

```typescript
// services/domain/HashPasswordService.ts
import { IService, Result, Ok, Err, ErrorDetail } from '@rvettori/service-pattern';

export class HashPasswordService implements IService<string, string, ErrorDetail> {
  async execute(password: string): Promise<Result<string, ErrorDetail>> {
    if (password.length < 8) {
      return Err({
        code: 'WEAK_PASSWORD',
        message: 'Password must be at least 8 characters'
      });
    }

    const hashed = await someHashFunction(password);
    return Ok(hashed);
  }
}
```

### Application Layer

```typescript
// services/application/RegisterUserService.ts
import { IService, Result, Ok, Err, ErrorDetail } from '@vettori/service-pattern';

export class RegisterUserService implements IService<RegisterDTO, User, ErrorDetail> {
  constructor(
    private hashPasswordService: HashPasswordService,
    private userRepository: UserRepository
  ) {}

  async execute(input: RegisterDTO): Promise<Result<User, ErrorDetail>> {
    // Hash password using domain service
    const hashResult = await this.hashPasswordService.execute(input.password);
    if (!hashResult.success) {
      return hashResult;
    }

    // Create user
    const user = await this.userRepository.create({
      email: input.email,
      password: hashResult.value
    });

    return Ok(user);
  }
}
```

## 🧪 Testing

Services are easy to test with mocks:

```typescript
describe('RegisterUserService', () => {
  it('should return error if password is weak', async () => {
    const mockHashService = {
      execute: jest.fn().mockResolvedValue(
        Err({ code: 'WEAK_PASSWORD', message: 'Too weak' })
      )
    };

    const service = new RegisterUserService(mockHashService, mockRepo);
    const result = await service.execute({ email: 'test@test.com', password: '123' });

    expect(result.success).toBe(false);
    if (!result.success) {
      expect(result.error.code).toBe('WEAK_PASSWORD');
    }
  });
});
```

## 📖 Best Practices

1. **One Service, One Responsibility**: Each service should do only one thing
2. **Use Result Pattern**: Never throw exceptions, always return Result
3. **Type Your Errors**: Use ErrorDetail with specific error codes
4. **Compose Services**: Application services orchestrate domain services
5. **Test Services Independently**: Mock dependencies, test logic

## 🤝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

## 📄 License

MIT © Rafael Vettori

## 🔗 Links

- [GitHub Repository](https://github.com/rvettori/service-pattern)
- [Issues](https://github.com/rvettori/service-pattern/issues)