← Назад к вопросам
Какая была архитектура у микросервисов, которые проектировал?
2.0 Middle🔥 151 комментариев
#REST API и микросервисы
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Архитектура микросервисов, которые я проектировал
Разработал несколько систем на микросервисной архитектуре. Расскажу о наиболее удачном проекте.
Проект: Платформа для управления учебными заведениями
Стек: Java 17, Spring Boot, PostgreSQL, RabbitMQ, Docker, Kubernetes
Масштаб: 8-10 микросервисов, 2000+ одновременных пользователей
Архитектура на уровне сервисов
┌─────────────────────────────────────────────────────┐
│ API Gateway │ (Spring Cloud Gateway)
│ (маршрутизация, rate limiting) │
└────────┬────────────────────────────────────────────┘
│
┌───┴────┬─────────────┬─────────────┬────────────┐
│ │ │ │ │
┌──▼──┐ ┌──▼──┐ ┌──▼──┐ ┌──▼──┐ ┌───▼──┐
│Auth │ │User │ │Post │ │File │ │Notify│
│Svc │ │Svc │ │Svc │ │Svc │ │Svc │
└──┬──┘ └──┬──┘ └──┬──┘ └──┬──┘ └───┬──┘
│ │ │ │ │
┌──▼─────┬──▼────┬──────▼┬───────┬───▼──┬─────────▼───┐
│JWT │User │Post │File │Search│Notification│
│Redis │DB │DB │S3/Min │ES │Queue RabbitMQ
│ │ │ │IO │ │
└────────┴───────┴───────┴───────┴──────┴──────────────┘
1. Разделение ответственности (Domain-Driven Design)
Auth Service
Ответственность: Аутентификация и авторизация
// Выделен в отдельный сервис для:
// 1. Масштабирования (критичный сервис)
// 2. Безопасности (все проверки в одном месте)
// 3. Переиспользования (все сервисы на нём зависят)
@RestController
@RequestMapping("/api/v1/auth")
public class AuthController {
@PostMapping("/login")
public ResponseEntity<TokenResponse> login(@RequestBody LoginRequest request) {
User user = userService.authenticate(request);
String token = jwtProvider.generateToken(user);
return ResponseEntity.ok(new TokenResponse(token));
}
}
User Service
Ответственность: Управление профилями пользователей
@RestController
@RequestMapping("/api/v1/users")
public class UserController {
@GetMapping("/{id}")
public ResponseEntity<UserDTO> getUser(@PathVariable String id) {
User user = userService.findById(id);
return ResponseEntity.ok(userMapper.toDTO(user));
}
@PostMapping
public ResponseEntity<UserDTO> createUser(@RequestBody CreateUserRequest request) {
User user = userService.create(request);
// Опубликуем событие для других сервисов
eventPublisher.publishEvent(new UserCreatedEvent(user));
return ResponseEntity.status(201).body(userMapper.toDTO(user));
}
}
Post Service (основной бизнес-сервис)
Ответственность: Управление постами
@RestController
@RequestMapping("/api/v1/posts")
public class PostController {
@PostMapping
@PreAuthorize("isAuthenticated()")
public ResponseEntity<PostDTO> createPost(@RequestBody CreatePostRequest request) {
Post post = postService.create(request, getCurrentUserId());
// Асинхронная индексация в ElasticSearch
searchService.indexPost(post);
// Уведомление для подписчиков
notificationService.notifyFollowers(post.getAuthorId(), "New post");
return ResponseEntity.status(201).body(postMapper.toDTO(post));
}
}
2. Связь между сервисами
Синхронная коммуникация (REST/gRPC)
// Post Service делает запрос к User Service
@Service
public class PostService {
private final UserServiceClient userServiceClient;
public Post create(CreatePostRequest request, String authorId) {
// Валидируем пользователя
User author = userServiceClient.getUser(authorId);
if (author == null) {
throw new UserNotFoundException();
}
Post post = new Post();
post.setAuthorId(authorId);
post.setContent(request.getContent());
post.setCreatedAt(LocalDateTime.now(UTC));
return postRepository.save(post);
}
}
// Resilience4j для обработки отказов
@Configuration
public class ResilienceConfig {
@Bean
public Resilience4jCircuitBreakerFactory circuitBreakerFactory() {
return new Resilience4jCircuitBreakerFactory();
}
}
// Использование:
@Service
public class PostService {
@CircuitBreaker(name = "userService", fallback = "getUserFallback")
public User getUser(String userId) {
return userServiceClient.getUser(userId);
}
public User getUserFallback(String userId, Exception e) {
// Возвращаем закэшированные данные или fail-safe ответ
return userCache.getOrDefault(userId, null);
}
}
Асинхронная коммуникация (Message Queue)
// Notification Service слушает события
@Component
public class NotificationListener {
@RabbitListener(queues = "user.created")
public void onUserCreated(UserCreatedEvent event) {
String message = String.format("Welcome, %s!", event.getUserName());
notificationService.send(event.getUserId(), message);
}
@RabbitListener(queues = "post.created")
public void onPostCreated(PostCreatedEvent event) {
// Отправить уведомление подписчикам
List<String> followers = userService.getFollowers(event.getAuthorId());
followers.forEach(follower ->
notificationService.send(follower, "New post from someone you follow")
);
}
}
// Post Service публикует событие
@Service
public class PostService {
private final RabbitTemplate rabbitTemplate;
public Post create(CreatePostRequest request, String authorId) {
Post post = createPostInDB(request, authorId);
// Публикуем асинхронное событие
PostCreatedEvent event = new PostCreatedEvent(
post.getId(),
post.getAuthorId(),
post.getContent()
);
rabbitTemplate.convertAndSend("post.created", event);
return post;
}
}
3. Данные и консистентность
База данных per сервис
Auth Service User Service Post Service
↓ ↓ ↓
auth_db user_db post_db
(JWT tokens) (profiles, (posts,
credentials) comments)
Преимущества:
- Каждый сервис использует оптимальную БД
- Независимый масштабирование
- Слабая связанность
Вызовы:
// Проблема: транзакции между сервисами
// Решение: Saga pattern
@Service
public class UserRegistrationSaga {
@Transactional
public void registerUser(RegisterRequest request) {
// 1. Создаём пользователя в User Service
User user = userService.create(request);
try {
// 2. Создаём профиль в Profile Service
profileService.create(user.getId());
// ✓ Успех
} catch (Exception e) {
// ✗ Откатываем
userService.delete(user.getId());
throw e;
}
}
}
4. Кэширование
@Service
public class UserService {
@Cacheable(value = "users", key = "#id")
public User findById(String id) {
return userRepository.findById(id);
}
@CacheEvict(value = "users", key = "#id")
public void update(String id, UpdateUserRequest request) {
User user = userRepository.findById(id);
user.setName(request.getName());
userRepository.save(user);
}
}
// Redis для распределённого кэша
@Configuration
public class CacheConfig {
@Bean
public CacheManager cacheManager(RedisConnectionFactory factory) {
return RedisCacheManager.create(factory);
}
}
5. Мониторинг и логирование
// Spring Cloud Sleuth для трассировки
@GetMapping("/{id}")
public ResponseEntity<UserDTO> getUser(@PathVariable String id) {
// Автоматически добавляется Trace ID и Span ID
logger.info("Getting user {}", id);
User user = userService.findById(id);
return ResponseEntity.ok(userMapper.toDTO(user));
}
// Все логи связаны одним Trace ID:
// [app-1,abc123,span1] Getting user 123
// [app-2,abc123,span2] Fetching from DB
// [app-3,abc123,span3] Updating cache
// Prometheus метрики
@Metrics
@GetMapping("/{id}")
public ResponseEntity<UserDTO> getUser(@PathVariable String id) {
// Автоматически собираются метрики
// - http_requests_total
// - http_request_duration_seconds
return ResponseEntity.ok(...);
}
6. Deployment (Kubernetes)
apiVersion: apps/v1
kind: Deployment
metadata:
name: post-service
spec:
replicas: 3
selector:
matchLabels:
app: post-service
template:
metadata:
labels:
app: post-service
spec:
containers:
- name: post-service
image: registry.example.com/post-service:1.0
ports:
- containerPort: 8080
env:
- name: SPRING_DATASOURCE_URL
valueFrom:
secretKeyRef:
name: db-credentials
key: url
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
livenessProbe:
httpGet:
path: /actuator/health
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
7. API Gateway
@Configuration
public class GatewayConfig {
@Bean
public RouteLocator routes(RouteLocatorBuilder builder) {
return builder.routes()
.route("auth", r -> r
.path("/api/v1/auth/**")
.uri("http://auth-service:8080")
)
.route("users", r -> r
.path("/api/v1/users/**")
.filters(f -> f.addRequestHeader("X-Service", "user-service"))
.uri("http://user-service:8080")
)
.route("posts", r -> r
.path("/api/v1/posts/**")
.filters(f -> f.requestRateLimiter(
config -> config.setRateLimiter(redisRateLimiter())
))
.uri("http://post-service:8080")
)
.build();
}
}
Ключевые решения и проблемы
✓ Что сработало
- Event-driven архитектура — сервисы слабо связаны
- Кэширование — значительно улучшила производительность
- Circuit breakers — предотвращают cascade failures
- Kubernetes — простой deploy и масштабирование
✗ Что было сложно
- Транзакции между сервисами — Saga pattern требует хорошего дизайна
- Мониторинг — распределённые трассировки сложнее настраивать
- Data consistency — eventual consistency требует иного мышления
- Тестирование — интеграционные тесты между сервисами медленнее
Итоговая статистика
- 8-10 микросервисов с чётким разделением ответственности
- 2000+ одновременных пользователей при 99.9% uptime
- Среднее время ответа: 150-200ms (включая сетевые задержки)
- Deployment: Несколько раз в день без downtime
Эта архитектура позволила команде разрабатывать сервисы независимо и масштабировать критичные компоненты отдельно.