Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
22dd4af
уборка
AleksOBM Aug 21, 2026
7ca98e5
Этап 2. Разбиваем проект на микросервисы
AleksOBM Aug 26, 2026
2de98a1
Этап 2. Разбиваем проект на микросервисы
AleksOBM Aug 26, 2026
c168bee
Этап 2. Разбиваем проект на микросервисы
AleksOBM Aug 26, 2026
1278c73
Этап 2. Разбиваем проект на микросервисы
AleksOBM Aug 27, 2026
ad5ed7b
Этап 2. Разбиваем проект на микросервисы
AleksOBM Aug 28, 2026
e6784c2
Этап 2. Разбиваем проект на микросервисы
AleksOBM Aug 28, 2026
ba403ee
Этап 2. Разбиваем проект на микросервисы
AleksOBM Aug 29, 2026
4306567
Этап 2. Разбиваем проект на микросервисы
AleksOBM Aug 30, 2026
659a78a
Этап 2. Разбиваем проект на микросервисы
AleksOBM Aug 31, 2026
34b36ef
Этап 2. Разбиваем проект на микросервисы
AleksOBM Sep 1, 2026
0f288a5
Этап 2. Разбиваем проект на микросервисы
AleksOBM Sep 1, 2026
4fd03ff
Этап 2. Разбиваем проект на микросервисы
AleksOBM Sep 2, 2026
c376435
Этап 2. Разбиваем проект на микросервисы
AleksOBM Sep 2, 2026
440475d
Этап 2. Разбиваем проект на микросервисы
AleksOBM Sep 2, 2026
ac7a496
Этап 2. Разбиваем проект на микросервисы
AleksOBM Sep 2, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added .img/ewm-main-event-service.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .img/ewm-main-rating-service.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .img/ewm-main-request-service.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .img/ewm-main-user-service.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .img/ewm-stats-stats-server.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .img/external-api-2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .img/external-api.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .img/internal-api.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .img/microservices.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions .postman/feature.json → .postman/ewm-rating-service.json
Original file line number Diff line number Diff line change
Expand Up @@ -770,7 +770,7 @@
"listen": "test",
"script": {
"exec": [
"pm.test(\"Ответ должен содержать код статуса 409 и данные в формате json\", function () {\r",
"pm.test(\"Ответ должен содержать код статуса 404 и данные в формате json\", function () {\r",
" pm.response.to.have.status(404);\r",
" pm.response.to.be.withBody;\r",
" pm.response.to.be.json;\r",
Expand Down Expand Up @@ -1043,7 +1043,7 @@
"variable": [
{
"key": "baseUrl",
"value": "http://localhost:8080"
"value": ""
},
{
"key": "uid",
Expand Down
54 changes: 54 additions & 0 deletions core/aggregation-api/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>ru.practicum</groupId>
<artifactId>core</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>

<artifactId>aggregation-api</artifactId>
<packaging>jar</packaging>

<dependencies>
<dependency>
<groupId>ru.practicum</groupId>
<artifactId>stat-dto</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>

<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
</dependency>
<dependency>
<groupId>jakarta.validation</groupId>
<artifactId>jakarta.validation-api</artifactId>
</dependency>
<dependency>
<groupId>jakarta.persistence</groupId>
<artifactId>jakarta.persistence-api</artifactId>
</dependency>

<dependency>
<groupId>org.hibernate.orm</groupId>
<artifactId>hibernate-core</artifactId>
</dependency>

<!-- Jackson -->
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
</dependency>

<!-- Feign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
</dependencies>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package ru.practicum.aggregation.client;

import lombok.experimental.UtilityClass;
import org.springframework.lang.NonNull;
import ru.practicum.aggregation.error.exception.bussines.ServiceProcessingException;
import ru.practicum.aggregation.error.exception.unavailable.ServiceUnavailableException;

import java.util.function.Supplier;

@UtilityClass
public class RemoteCallExecutor {

@NonNull
public <T> RemoteCallResult<T> execute(@NonNull Supplier<T> action) {
try {
return new RemoteCallResult.Success<>(action.get());

} catch (ServiceProcessingException ex) {
return new RemoteCallResult.Failure<>(ex);

} catch (ServiceUnavailableException ex) {
return new RemoteCallResult.Degraded<>(ex);
}
}

@NonNull
public static RemoteCallResult<Void> executeVoid(@NonNull Runnable function) {
try {
function.run();
return new RemoteCallResult.Success<>(null);

} catch (ServiceProcessingException ex) {
return new RemoteCallResult.Failure<>(ex);

} catch (ServiceUnavailableException ex) {
return new RemoteCallResult.Degraded<>(ex);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package ru.practicum.aggregation.client;

import ru.practicum.aggregation.error.exception.bussines.ServiceProcessingException;

@SuppressWarnings("unused")
public sealed interface RemoteCallResult<T>
permits RemoteCallResult.Success,
RemoteCallResult.Failure,
RemoteCallResult.Degraded {

record Success<T>(T value)
implements RemoteCallResult<T> {
}

record Failure<T>(ServiceProcessingException ex)
implements RemoteCallResult<T> {
}

record Degraded<T>(Throwable cause)
implements RemoteCallResult<T> {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package ru.practicum.aggregation.client;

import feign.FeignException;
import lombok.NonNull;
import lombok.experimental.UtilityClass;
import ru.practicum.aggregation.error.exception.bussines.cause.BadRequestException;
import ru.practicum.aggregation.error.exception.bussines.cause.ConflictException;
import ru.practicum.aggregation.error.exception.bussines.cause.NotFoundException;
import ru.practicum.aggregation.error.exception.unavailable.EventServiceUnavailableException;
import ru.practicum.aggregation.error.exception.unavailable.RequestServiceUnavailableException;
import ru.practicum.aggregation.error.exception.unavailable.UserServiceUnavailableException;

@UtilityClass
public class RemoteExceptionMapper {

public RuntimeException mapUserException(@NonNull Throwable cause) {
if (cause instanceof FeignException ex) {
return switch (ex.status()) {
case 400 -> new BadRequestException(ex.getMessage());
case 404 -> new NotFoundException(ex.getMessage());
case 409 -> new ConflictException(ex.getMessage());
default -> new EventServiceUnavailableException(ex);
};
}
return new UserServiceUnavailableException(cause);
}

public RuntimeException mapEventException(@NonNull Throwable cause) {
if (cause instanceof FeignException ex) {
return switch (ex.status()) {
case 400 -> new BadRequestException(ex.getMessage());
case 404 -> new NotFoundException(ex.getMessage());
case 409 -> new ConflictException(ex.getMessage());
default -> new EventServiceUnavailableException(ex);
};
}
return new EventServiceUnavailableException(cause);
}

public RuntimeException mapRequestException(@NonNull Throwable cause) {
if (cause instanceof FeignException ex) {
return switch (ex.status()) {
case 400 -> new BadRequestException(ex.getMessage());
case 404 -> new NotFoundException(ex.getMessage());
case 409 -> new ConflictException(ex.getMessage());
default -> new EventServiceUnavailableException(ex);
};
}
return new RequestServiceUnavailableException(cause);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package ru.practicum.aggregation.client.event;

import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.*;
import ru.practicum.aggregation.dto.event.output.EventFullDto;
import ru.practicum.aggregation.dto.rating.come.update.RatingUpdateRequest;

@FeignClient(
name = "event-service",
fallbackFactory = EventFallbackFactory.class
)
public interface EventClient {

@PatchMapping("/system/events/ratings")
void systemUpdateRating(@RequestBody @NotNull @Valid RatingUpdateRequest request);

@GetMapping("/system/events/{eventId}")
EventFullDto systemFindEventById(@PathVariable Long eventId, @RequestParam Long confirmets);

@GetMapping("/system/events/{eventId}/initiator")
Long systemGetInitiatorIfPublished(@PathVariable Long eventId);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package ru.practicum.aggregation.client.event;

import org.springframework.cloud.openfeign.FallbackFactory;
import org.springframework.stereotype.Component;
import ru.practicum.aggregation.client.RemoteExceptionMapper;
import ru.practicum.aggregation.dto.event.output.EventFullDto;
import ru.practicum.aggregation.dto.rating.come.update.RatingUpdateRequest;

@Component
public class EventFallbackFactory implements FallbackFactory<EventClient> {

@Override
public EventClient create(Throwable cause) {

return new EventClient() {

@Override
public void systemUpdateRating(RatingUpdateRequest request) {
throw RemoteExceptionMapper.mapEventException(cause);
}

@Override
public EventFullDto systemFindEventById(Long eventId, Long confirmets) {
throw RemoteExceptionMapper.mapEventException(cause);
}

@Override
public Long systemGetInitiatorIfPublished(Long eventId) {
throw RemoteExceptionMapper.mapEventException(cause);
}
};
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package ru.practicum.aggregation.client.request;

import jakarta.validation.constraints.Positive;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.*;
import ru.practicum.aggregation.dto.participation.come.EventRequestStatusUpdateRequest;
import ru.practicum.aggregation.dto.participation.output.EventRequestStatusUpdateResult;
import ru.practicum.aggregation.dto.participation.output.ParticipationRequestDto;
import ru.practicum.aggregation.model.repository.EventRequestCount;

import java.util.List;

@FeignClient(
name = "request-service",
fallbackFactory = RequestFallbackFactory.class
)
public interface RequestClient {

@GetMapping("/system/requests/users/{userId}/events/{eventId}")
List<ParticipationRequestDto> findByUserIdAndEventId(@PathVariable @Positive Long userId,
@PathVariable @Positive Long eventId);

@PatchMapping("/system/requests/events/{eventId}/status")
EventRequestStatusUpdateResult updateStatusRequest(
@PathVariable Long eventId,
@RequestParam Integer participantLimit,
@RequestParam Boolean requestModeration,
@RequestBody EventRequestStatusUpdateRequest request
);

@GetMapping("/system/requests/count")
List<EventRequestCount> getConfirmedRequestsCount(@RequestParam List<Long> eventIds);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package ru.practicum.aggregation.client.request;

import org.springframework.cloud.openfeign.FallbackFactory;
import org.springframework.stereotype.Component;
import ru.practicum.aggregation.client.RemoteExceptionMapper;
import ru.practicum.aggregation.dto.participation.come.EventRequestStatusUpdateRequest;
import ru.practicum.aggregation.dto.participation.output.EventRequestStatusUpdateResult;
import ru.practicum.aggregation.dto.participation.output.ParticipationRequestDto;
import ru.practicum.aggregation.model.repository.EventRequestCount;

import java.util.List;

@Component
public class RequestFallbackFactory implements FallbackFactory<RequestClient> {

@Override
public RequestClient create(Throwable cause) {

return new RequestClient() {

@Override
public List<ParticipationRequestDto> findByUserIdAndEventId(Long userId, Long eventId) {
throw RemoteExceptionMapper.mapRequestException(cause);
}

@Override
public EventRequestStatusUpdateResult updateStatusRequest(
Long eventId,
Integer participantLimit,
Boolean requestModeration,
EventRequestStatusUpdateRequest request
) {
throw RemoteExceptionMapper.mapRequestException(cause);
}

@Override
public List<EventRequestCount> getConfirmedRequestsCount(List<Long> eventIds) {
throw RemoteExceptionMapper.mapRequestException(cause);
}
};
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package ru.practicum.aggregation.client.stats;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import ru.practicum.stat.dto.EndpointHitDto;
import ru.practicum.stat.dto.ViewStatsDto;

import java.time.LocalDateTime;
import java.util.List;

@FeignClient(
name = "stats-server",
fallbackFactory = StatsFallbackFactory.class
)
public interface StatsClient {

@PostMapping("/hit")
void hit(@RequestBody EndpointHitDto endpointHitDto);

@GetMapping("/stats")
List<ViewStatsDto> getStats(
@RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime start,
@RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime end,
@RequestParam List<String> uris,
@RequestParam boolean unique);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package ru.practicum.aggregation.client.stats;

import org.springframework.cloud.openfeign.FallbackFactory;
import org.springframework.stereotype.Component;
import ru.practicum.aggregation.error.exception.unavailable.StatsServerUnavailableException;
import ru.practicum.stat.dto.EndpointHitDto;
import ru.practicum.stat.dto.ViewStatsDto;

import java.time.LocalDateTime;
import java.util.List;

@Component
public class StatsFallbackFactory implements FallbackFactory<StatsClient> {

@Override
public StatsClient create(Throwable cause) {

return new StatsClient() {

@Override
public void hit(EndpointHitDto endpointHitDto) {
throw new StatsServerUnavailableException("", cause);
}

@Override
public List<ViewStatsDto> getStats(LocalDateTime start,
LocalDateTime end,
List<String> uris,
boolean unique) {

throw new StatsServerUnavailableException("", cause);
}
};
}

}
Loading
Loading