Service Layer (Сервисный уровень)
Паттерн проектирования Service Layer
Описание Service Layer
Определяет границу между приложением и слоем сервисов, который образует набор доступных операций и управляет ответом приложения в каждой операции.
Бизнес-приложения обычно нуждаются в различных интерфейсах к данным, которые они хранят и логике, которую реализуют: загрузчики данных, пользовательские интерфейсы, интеграционные шлюзы и другое. Вопреки различным целям, эти интерфейсы часто нуждаются во взаимодействии с приложением для доступа и управления его данными и исполнения логики. Эти взаимодействия могут быть сложными, использующими транзакции на нескольких ресурсах и управление несколькими ответами на действие. Программирование логики взаимодействия для каждого интерфейса вызовет больше количество дублирования.
Паттерн Service Layer определяет для приложения границу и набор допустимых операций с точки зрения взаимодействующих с ним клиентских. Он инкапсулирует бизнес-логику приложения, управляя транзакциями и управляя ответами в реализации этих операций.
Примеры реализации
// Service Layer Pattern in JavaScript
class UserService {
constructor(userRepository, emailService, logger) {
this.userRepository = userRepository;
this.emailService = emailService;
this.logger = logger;
}
async createUser(userData) {
try {
this.logger.info('Creating new user', { email: userData.email });
// Validate user data
this.validateUserData(userData);
// Check if user already exists
const existingUser = await this.userRepository.findByEmail(userData.email);
if (existingUser) {
throw new Error('User with this email already exists');
}
// Create user
const user = await this.userRepository.create({
...userData,
createdAt: new Date(),
isActive: true
});
// Send welcome email
await this.emailService.sendWelcomeEmail(user.email, user.name);
this.logger.info('User created successfully', { userId: user.id });
return user;
} catch (error) {
this.logger.error('Failed to create user', { error: error.message });
throw error;
}
}
validateUserData(userData) {
if (!userData.email || !userData.name) {
throw new Error('Email and name are required');
}
if (!this.isValidEmail(userData.email)) {
throw new Error('Invalid email format');
}
}
isValidEmail(email) {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email);
}
}
// Usage
const userService = new UserService(userRepository, emailService, logger);
const user = await userService.createUser({
name: 'John Doe',
email: 'john@example.com'
});
console.log('Service Layer pattern implemented');
<?php
// Service Layer Pattern in PHP
class UserService {
private $userRepository;
private $emailService;
private $logger;
public function __construct($userRepository, $emailService, $logger) {
$this->userRepository = $userRepository;
$this->emailService = $emailService;
$this->logger = $logger;
}
public function createUser($userData) {
try {
$this->logger->info('Creating new user', ['email' => $userData['email']]);
// Validate user data
$this->validateUserData($userData);
// Check if user already exists
$existingUser = $this->userRepository->findByEmail($userData['email']);
if ($existingUser) {
throw new Exception('User with this email already exists');
}
// Create user
$user = $this->userRepository->create([
'name' => $userData['name'],
'email' => $userData['email'],
'created_at' => date('Y-m-d H:i:s'),
'is_active' => true
]);
// Send welcome email
$this->emailService->sendWelcomeEmail($user['email'], $user['name']);
$this->logger->info('User created successfully', ['user_id' => $user['id']]);
return $user;
} catch (Exception $e) {
$this->logger->error('Failed to create user', ['error' => $e->getMessage()]);
throw $e;
}
}
private function validateUserData($userData) {
if (empty($userData['email']) || empty($userData['name'])) {
throw new Exception('Email and name are required');
}
if (!$this->isValidEmail($userData['email'])) {
throw new Exception('Invalid email format');
}
}
private function isValidEmail($email) {
return filter_var($email, FILTER_VALIDATE_EMAIL) !== false;
}
}
// Usage
$userService = new UserService($userRepository, $emailService, $logger);
$user = $userService->createUser([
'name' => 'John Doe',
'email' => 'john@example.com'
]);
echo "Service Layer pattern implemented\n";
?>
// Service Layer Pattern in Go
package main
import (
"fmt"
"time"
)
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
IsActive bool `json:"is_active"`
CreatedAt time.Time `json:"created_at"`
}
type UserService struct {
userRepo UserRepository
emailService EmailService
logger Logger
}
func NewUserService(userRepo UserRepository, emailService EmailService, logger Logger) *UserService {
return &UserService{
userRepo: userRepo,
emailService: emailService,
logger: logger,
}
}
func (us *UserService) CreateUser(userData map[string]interface{}) (*User, error) {
us.logger.Info("Creating new user", map[string]interface{}{
"email": userData["email"],
})
// Validate user data
if err := us.validateUserData(userData); err != nil {
return nil, err
}
// Check if user already exists
existingUser, err := us.userRepo.FindByEmail(userData["email"].(string))
if err == nil && existingUser != nil {
return nil, fmt.Errorf("user with this email already exists")
}
// Create user
user := &User{
Name: userData["name"].(string),
Email: userData["email"].(string),
IsActive: true,
CreatedAt: time.Now(),
}
createdUser, err := us.userRepo.Create(user)
if err != nil {
us.logger.Error("Failed to create user", map[string]interface{}{
"error": err.Error(),
})
return nil, err
}
// Send welcome email
if err := us.emailService.SendWelcomeEmail(createdUser.Email, createdUser.Name); err != nil {
us.logger.Error("Failed to send welcome email", map[string]interface{}{
"error": err.Error(),
})
}
us.logger.Info("User created successfully", map[string]interface{}{
"user_id": createdUser.ID,
})
return createdUser, nil
}
func (us *UserService) validateUserData(userData map[string]interface{}) error {
email, emailOk := userData["email"].(string)
name, nameOk := userData["name"].(string)
if !emailOk || !nameOk || email == "" || name == "" {
return fmt.Errorf("email and name are required")
}
if !us.isValidEmail(email) {
return fmt.Errorf("invalid email format")
}
return nil
}
func (us *UserService) isValidEmail(email string) bool {
return len(email) > 0 && len(email) < 255
}
func main() {
fmt.Println("Service Layer pattern implemented")
fmt.Println("Key benefits:")
fmt.Println("1. Encapsulates business logic")
fmt.Println("2. Manages transactions")
fmt.Println("3. Coordinates between repositories")
fmt.Println("4. Provides clear API boundaries")
fmt.Println("5. Handles cross-cutting concerns")
}
Использована иллюстрация с сайта Мартина Фаулера.