Исследование подходов построения микросервисной архитектуры на примере создания музыкального стримингового приложения
Компоненты микросервисной архитектуры, её отличия от монолитной. Определение подходов коммуникации в микросервисах. Проектирование музыкального стримингового приложения как практическая реализация концепции микросервисной архитектуры и ее функционала.
Рубрика | Программирование, компьютеры и кибернетика |
Вид | дипломная работа |
Язык | русский |
Дата добавления | 10.12.2019 |
Размер файла | 2,5 M |
Отправить свою хорошую работу в базу знаний просто. Используйте форму, расположенную ниже
Студенты, аспиранты, молодые ученые, использующие базу знаний в своей учебе и работе, будут вам очень благодарны.
Рисунок 6 - Страница альбома
На странице альбома (см. рисунок 6) представлена информация об альбоме и композициях, включенных в данный альбом. Для воспроизведения композиции достаточно навести курсор мыши на номер композиции и нажать на появившийся значок воспроизведения. В специальном поле можно оставить свой комментарий, который будет следовать вслед за ранее размещенными. Размер одного комментария составляет от 1 до 256 символов.
При переходе в историю пользователя (страница «History») открывается список альбомов, к которым обращался пользователь для прослушивания (см. рисунок 7).
Рисунок 7 - История прослушанных альбомов
С данной страницы также доступен переход к списку лайков, поставленных пользователем, и оставленных им комментариев к альбомам.
Для перехода на страницу альбома достаточно щелкнуть клавишей мыши на его логотип или имя, а для перехода на страницу исполнителя, включенного в альбом, к которому обращался пользователь, выбрать наименование исполнителя и щелкнуть по нему клавишей мыши.
Страница наиболее популярных исполнителей и альбомов, отсортированных по времени, выглядит следующим образом (см. рисунок 8).
Рисунок 8- Страница наиболее популярных исполнителей и альбомов, отсортированных по времени их загрузки
На данной странице представлены исполнители и альбомы, отсортированные по времени их загрузки на данный сервис. Максимальное количество размещенных на странице исполнителей и альбомов - десять единиц.
Страница управления исполнителями состоит из списка исполнителей, кнопок создания и удаления. Удалять можно сразу несколько исполнителей (см. рисунок 9).
Рисунок 9 - Страница управления исполнителями
При нажатии на кнопку добавления или на самого исполнителя появляется всплывающее окно (pop-up), в котором можно создавать или редактировать конкретного исполнителя (см рисунок 10).
Рисунок 10 - Окно создания и редактирования на странице управления исполнителями
При нажатии на кнопку перехода на страницу управления альбомами появляется список созданных альбомов, которые относятся к данному исполнителю, а также кнопки создания и удаления альбомов. Удалять можно сразу несколько альбомов.
При нажатии на кнопку создания альбома или на один из созданных альбомов появляется всплывающее окно (pop-up), в котором можно создавать или редактировать выбранный альбом (см. рисунок 11).
Рисунок 11 - Окно создания и редактирования на странице управления альбомами
Рисунок 12 - Всплывающее окно (pop-up) для редактирования имени пользователя
Редактирование имени пользователя осуществляется в специально предназначенном для этого вплывающем окне (см. рисунок 12).
функционал микросервисный архитектура музыкальный стриминговый
Заключение
Данная работа посвящена рассмотрению основной концепции микросервисной архитектуры при детальном разборе её компонентов. В ходе работы были изучены основные элементы архитектуры, проведено её краткое сравнение с монолитной архитектурой, недостатки которой способствовали появлению микросервисов. При написании работы использованы статьи и научные труды практических архитекторов и разработчиков программного обеспечения, специализирующих на изучении практических аспектов использования различных архитектурных стилей построения приложений.
Для демонстрации практического применения архитектурного стиля микросервисов было создано музыкальное стриминговое приложение, программно-аппаратная часть которого построена на основе микросервисной архитектуры. Данное приложение позволяет его пользователю прослушивать полюбившуюся музыку, которая была загружена им самим, либо другими пользователями, редактировать исполнителей, альбомы и композиции.
С теоретической точки зрения настоящая работа будет способствовать систематизации знаний в области микросервисной архитектуры, а результаты её практической части послужат дальнейшей популяризации архитектурного стиля микросервисов как стиля, несущего в себе целый ряд технических преимуществ и показавшего себя как надежная основа при разработке различных веб-приложений.
Список использованной литературы
1. Bhatia, Siddhartha. Enterprise Approaches to Microservices: Choose Yours
Wisely. 2017
2. Fowler Martin, Lewis James. Microservices, a definition of this new architectural term.2014.
3. Newman, Sam. Building Microservices. Sebastopol, California: O'Reilly Media, 2015. -- 280 p. -- ISBN-13: 978-1491950357.
4. Richardson Chris. What are microservices? 2018.
5. Richardson Chris. Pattern: Shared database//A pattern language for microservices.2018
6. Anatomy of an HTTP Transaction.
7. Angular.
8. AWS Blog & Podcast
9. Netflix Open Source
10. RabbitMQ
11. Spotify
12. Spring Cloud
13. Spring Cloud Gateway
14. Spring WebFlux.
15. TIDEL
16. YouTubeMusic
Приложение
Artist.java
@Document(collection = "artist")
public class Artist {
@Id
private UUID id;
@Indexed(name = "name", direction = IndexDirection.DESCENDING)
private String name;
private String description;
private Date date;
@Indexed(name = "url", unique = true, direction =
IndexDirection.DESCENDING)
private String url;
private String location;
private UUID userId;
private Map<String, String> links;
}
Album.java
@Document(collection = "album")
public class Album {
@Id
private UUID id;
@Indexed(name = "artistId", direction = IndexDirection.DESCENDING)
private UUID idOfArtist;
@Indexed(name = "name", direction = IndexDirection.DESCENDING)
private String name;
@Indexed(name = "date", direction = IndexDirection.DESCENDING)
private Date date;
@Indexed(name = "genre", direction = IndexDirection.DESCENDING)
private List<String> genre;
}
Prediction.java
@Document(collection = "prediction")
public class Prediction {
@Id
private UUID id;
@Indexed
private UUID userId;
@Indexed(unique = true)
private String genre;
private long numberOfListenings;
}
Song.java
@Document(collection = "song")
public class Song {
@Id
private UUID id;
@Indexed(name = "albumId")
private UUID albumId;
@Indexed(name = "name")
private String name;
}
User.java
@Document(collection = "user")
public class User {
@Id
private UUID id;
@Indexed(name = "name", unique = true)
private String name;
private Date date;
private Date lastActivity;
private String password;
}
Like.java
@Document(collection = "like")
public class Like {
@Id
private UUID id;
@Indexed
private UUID albumId;
@Indexed
private UUID userId;
}
FileContrainer.java
public class FileContainer {
private UUID id;
private FilePart file;
}
History Element.java
@Document("history-element")
public class HistoryElement {
@Id
private UUID id;
@Indexed
private UUID userId;
@Indexed
private UUID albumId;
}
Comment.java
@Document("comment")
public class Comment {
@Id
private UUID id;
@Indexed(name = "userId")
private UUID userId;
@Indexed(name = "albumId")
private UUID albumId;
private String message;
}
ArtistCotnroller.java
@RestController
@RequestMapping("artist")
public class ArtistController {
@Autowired
@Qualifier("create")
private ValidationService createValidationService;
@Autowired
@Qualifier("edit")
private ValidationService editValidationService;
@Autowired
private ArtistCommandExecutorService artistCommandExecutorService;
@PostMapping
public Mono<ResponseEntity<ArtistResponse>>
createArtist(@RequestBody Mono<ArtistDTO> artistCommand) {
return artistCommand.subscribeOn(Schedulers.elastic())
.flatMap(artist -> createValidationService.validate(artist))
.flatMap(artist -> {
List<Pair<Field, ErrorCode>> result = artist.getErrors();
if (result != null && !result.isEmpty()) {
return
Mono.just(ResponseEntity.badRequest().body(generateErrorValidationResp
onse(result.get(0))));
} else {
return
artistCommandExecutorService.executeCreateCommand(artist.getArtistD
TO())
.flatMap(artistDTO ->
Mono.just(ResponseEntity.ok(generateCompletedResponse(artistDTO))));
}
})
.onErrorResume(ExecutingException.class, errorResult ->
Mono.just(ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERR
OR)
.body(generateErrorExecutionResponse(errorResult))))
.onErrorResume(RuntimeException.class, errorResult ->
Mono.just(ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERR
OR)
.body(generateErrorRuntimeResponse(errorResult))));
}
@PutMapping
public Mono<ResponseEntity<ArtistResponse>> editArtist(@RequestBody
Mono<ArtistDTO> artistCommand) {
return artistCommand.subscribeOn(Schedulers.elastic())
.flatMap(artist -> editValidationService.validate(artist))
.flatMap(artist -> {
Pair<Field, ErrorCode> result = artist.getErrors().get(0);
if (result != null) {
return
Mono.just(ResponseEntity.badRequest().body(generateErrorValidationResp
onse(result)));
} else {
return
artistCommandExecutorService.executeEditCommand(artist.getArtistDT
O())
.flatMap(artistDTO ->
Mono.just(ResponseEntity.ok(generateCompletedResponse(artistDTO))));
}
})
.onErrorResume(ExecutingException.class, errorResult ->
Mono.just(ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERR
OR)
.body(generateErrorExecutionResponse(errorResult))))
.onErrorResume(RuntimeException.class, errorResult ->
Mono.just(ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERR
OR)
.body(generateErrorRuntimeResponse(errorResult))));
}
@DeleteMapping("/{id}")
public Mono<ResponseEntity<ArtistResponse>>
deleteArtist(@PathVariable UUID id) {
return Mono.just(id)
.flatMap(artistId -> Mono.defer(() ->
artistCommandExecutorService.executeDeleteCommand(artistId)))
.flatMap(result -> Mono.defer(() -> {
ArtistResponse response = new ArtistResponse();
ArtistDTO artistDTO = new ArtistDTO();
artistDTO.setId(result);
response.setCommand(artistDTO);
return Mono.just(ResponseEntity.status(HttpStatus.OK).body(response));
}))
.onErrorResume(ExecutingException.class, error ->
Mono.just(ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERR
OR)
.body(generateErrorExecutionResponse(error))))
.onErrorResume(RuntimeException.class, error ->
Mono.just(ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERR
OR)
.body(generateErrorRuntimeResponse(error))));
}
private static ArtistResponse generateCompletedResponse(ArtistDTO
artistDTO) {
ArtistResponse artistResponse = new ArtistResponse();
artistResponse.setCommand(artistDTO);
return artistResponse;
}
private static ArtistResponse generateErrorValidationResponse(Pair<Field,
ErrorCode> error) {
ArtistResponse errorResponse = new ArtistResponse();
if (error != null) {
errorResponse.setCode(error.getSecond());
errorResponse.setParameters(Collections.singletonMap(ErrorParameters.FI
ELD,
error.getFirst().toString()));
}
errorResponse.setErrorType(ErrorType.VALIDATION);
return errorResponse;
}
private static ArtistResponse
generateErrorExecutionResponse(ExecutingException ex) {
ArtistResponse errorResponse = new ArtistResponse();
errorResponse.setErrorType(ex.getErrorType());
return errorResponse;
}
private static ArtistResponse
generateErrorRuntimeResponse(RuntimeException ex) {
ArtistResponse errorResponse = new ArtistResponse();
errorResponse.setErrorType(ErrorType.EXECUTING);
return errorResponse;
}
private static ResponseEntity<ArtistResponse>
generateNotFoundResponse() {
ArtistResponse errorResponse = new ArtistResponse();
errorResponse.setErrorType(ErrorType.EXECUTING);
errorResponse.setCode(ErrorCode.EXECUTING);
return new ResponseEntity<>(errorResponse, HttpStatus.NOT_FOUND);
}
}
ArtistQueryController.java
@RestController
@RequestMapping("artist")
public class ArtistQueryController {
@Autowired
private QueryService queryService;
("{id}")
public Mono<ResponseEntity<ArtistSearchResultDTO>>
getArtistById(@PathVariable UUID id) {
return queryService.getById(id)
.flatMap(artist -> Mono.defer(() -> Mono.just(ResponseEntity
.ok()
.body(artist))))
.defaultIfEmpty(ResponseEntity.notFound().build());
}
@GetMapping("name/{name}")
public Flux<ArtistSearchResultDTO> getAllByName(@PathVariable
String name) {
return queryService.getAllByName(name);
}
@GetMapping("url/{url}")
public Mono<ResponseEntity<ArtistSearchResultDTO>>
getAllByUrl(@PathVariable String url) {
return queryService.getByUrl(url)
.flatMap(artist -> Mono.defer(() -> Mono.just(ResponseEntity
.ok()
.body(artist))))
.defaultIfEmpty(ResponseEntity.notFound().build());
}
@GetMapping("all")
public Flux<ArtistSearchResultDTO> getAll() {
return queryService.getAll();
}
}
InternalArtistController.java
@RestController
@RequestMapping("internal/artist")
public class InternalArtistController {
@Autowired
private SearchService searchService;
private final QueryService queryService;
public InternalArtistController(QueryService queryService) {
this.queryService = queryService;
}
@GetMapping("{id}")
public Mono<Boolean> existsArtistById(@PathVariable UUID id) {
return queryService.existsArtistById(id);
}
}
AlbumController.java
@RestController
@RequestMapping("album")
public class AlbumController {
@Autowired
private AlbumService albumService;
@Autowired
@Qualifier("create")
private ValidationService validationCreateService;
@Autowired
@Qualifier("edit")
private ValidationService validationEditService;
private static final Function<RuntimeException, ? extends
Mono<ResponseEntity<AlbumResponse>>> ERROR_HANDLER = error
-> Mono.defer( () -> {
AlbumResponse albumResponse = new AlbumResponse();
albumResponse.setErrorType(ErrorType.EXECUTING);
albumResponse.setParameters(Collections.singletonMap(ErrorParameters.M
ESSAGE, error.getMessage()));
return
Mono.just(ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERR
OR).body(albumResponse));
});
@PostMapping
public Mono<ResponseEntity<AlbumResponse>>
efer(() -> validationCreateService.validate(albumDTO)))
.flatMap(validationResult -> Mono.defer(() ->
albumService.create(validationResult.getAlbumDTO())
.flatMap(result -> Mono.defer(() ->
Mono.just(ResponseEntity.ok(generateAlbumResponse(result)))))))
.onErrorResume(RuntimeException.class, ERROR_HANDLER);
}
@PutMapping
public Mono<ResponseEntity<AlbumResponse>>
editArtist(@RequestBody Mono<AlbumDTO> album) {
return album
.flatMap(albumDTO -> Mono.defer(() ->
validationEditService.validate(albumDTO)))
.flatMap(validationResult -> Mono.defer(() ->
albumService.edit(validationResult.getAlbumDTO())
.flatMap(result -> Mono.defer(() ->
Mono.just(ResponseEntity.ok(generateAlbumResponse(result)))))))
.onErrorResume(RuntimeException.class, ERROR_HANDLER);
}
@DeleteMapping("{id}")
public Mono<ResponseEntity<AlbumResponse>>
deleteArtist(@PathVariable UUID id) {
return Mono.just(id)
.flatMap(albumId -> Mono.defer(() -> albumService.delete(albumId)
.flatMap(result -> Mono.defer(() ->
Mono.just(ResponseEntity.ok(generateAlbumResponseById(result)))))))
.onErrorResume(RuntimeException.class, ERROR_HANDLER);
}
private static AlbumResponse generateAlbumResponse(AlbumDTO
albumDTO) {
AlbumResponse albumResponse = new AlbumResponse();
albumResponse.setAlbumDTO(albumDTO);
return albumResponse;
}
private static AlbumResponse generateAlbumResponseById(UUID id) {
AlbumResponse albumResponse = new AlbumResponse();
AlbumDTO albumDTO = new AlbumDTO();
albumDTO.setId(id);
albumResponse.setAlbumDTO(albumDTO);
return albumResponse;
}
}
AlbumQueryController.java
@RestController
@RequestMapping("album")
public class AlbumQueryController {
@Autowired
private QueryService queryService;
@GetMapping("{id}")
public Mono<AlbumDTO> getById(@PathVariable UUID id) {
return queryService.getById(id);
}
@GetMapping("name/{name}")
public Flux<AlbumDTO> getAllByName(@PathVariable String name) {
return queryService.getAllByName(name);
}
@GetMapping("artist/{id}")
public Flux<AlbumDTO> getAllByName(@PathVariable UUID id) {
roller.java
@RestController
@RequestMapping("internal/album")
public class AlbumInternalController {
Mono<Boolean> existsById(@PathVariable UUID id) {
return albumExternalService.existsAlbumById(id);
}
@GetMapping("ids")
public Flux<AlbumDTO> getAlbumsByIds(@RequestParam("ids")
List<UUID> ids) {
if (ids.isEmpty()) {
return Flux.empty();
}
return albumExternalService.getAlbumsByIds(ids);
}
@GetMapping("genre")
public Flux<AlbumDTO> getAlbumsByGenre(@RequestParam(required =
false) String genre) {
return genre != null ? albumExternalService.getAlbumsByGenre(genre) :
albumExternalService.getAlbumsRandom();
}
}
SongController.java
@RestController
@RequestMapping("song")
public class SongController {
@Autowired
private SongService songService;
@Autowired
@Qualifier("create")
private ValidationService validationCreateService;
@Autowired
@Qualifier("edit")
private ValidationService validationEditService;
@PostMapping
public Mono<ResponseEntity<SongResponse>>
createSong(@RequestBody Mono<SongDTO> songDTO) {
return songDTO
.subscribeOn(Schedulers.elastic())
.flatMap(song -> Mono.defer(() ->
validationCreateService.validate(song)))
.flatMap(result -> Mono.defer(() -> {
List<Pair<Field, ErrorCode>> errors = result.getErrors();
if (!errors.isEmpty()) {
return
Mono.just(ResponseEntity.badRequest().body(generateErrorResponse(errors
)));
} else {
return songService.create(result.getSong())
.flatMap(song -> Mono.defer(() ->
Mono.just(ResponseEntity.ok(generateSongResponse(song)))));
}
}))
.onErrorResume(RuntimeException.class,
error ->
Mono.just(ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERR
OR).body(generateInternalError(error))));
}
@PutMapping
public Mono<ResponseEntity<SongResponse>> editSong(@RequestBody
Mono<SongDTO> songDTO) {
return songDTO
.subscribeOn(Schedulers.elastic())
.flatMap(song -> Mono.defer(() -> validationEditService.validate(song)))
.flatMap(result -> Mono.defer(() -> {
List<Pair<Field, ErrorCode>> errors = result.getErrors();
if (!errors.isEmpty()) {
return
Mono.just(ResponseEntity.badRequest().body(generateErrorResponse(errors
turn songService.create(result.getSong())
.flatMap(song -> Mono.defer(() ->
Mono.just(ResponseEntity.ok(generateSongResponse(song)))));
}
}))
.onErrorResume(RuntimeException.class,
error ->
Mono.just(ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERR
OR).body(generateInternalError(error))));
}
@DeleteMapping("{id}")
public Mono<ResponseEntity<SongResponse>>
deleteSong(@PathVariable UUID id) {
return songService.delete(id)
.flatMap(result -> Mono.defer(() ->
Mono.just(ResponseEntity.ok(generateSongResponseWithId(result)))))
.onErrorResume(RuntimeException.class,
error ->
Mono.just(ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERR
OR).body(generateInternalError(error))));
}
private static SongResponse generateSongResponse(SongDTO song) {
SongResponse songResponse = new SongResponse();
songResponse.setSong(song);
return songResponse;
}
private static SongResponse generateSongResponseWithId(UUID id) {
SongResponse songResponse = new SongResponse();
SongDTO songDTO = new SongDTO();
songDTO.setId(id);
songResponse.setSong(songDTO);
return songResponse;
}
private static SongResponse generateInternalError(RuntimeException ex) {
SongResponse songResponse = new SongResponse();
songResponse.setErrorType(ErrorType.EXECUTING);
songResponse.setErrorCode(ErrorCode.EXECUTING);
songResponse.setParams(Collections.singletonMap(ErrorParameters.MESS
AGE, ex.getMessage()));
return songResponse;
}
private static SongResponse generateErrorResponse(List<Pair<Field,
ErrorCode>> errors) {
SongResponse songResponse = new SongResponse();
Pair<Field, ErrorCode> error = errors.get(0);
songResponse.setErrorCode(error.getSecond());
songResponse.setParams(Collections.singletonMap(ErrorParameters.FIELD
, error.getFirst().toString()));
songResponse.setErrorType(ErrorType.VALIDATION);
return songResponse;
}
}
SongQueryController.java
@RestController
@RequestMapping("song")
public class SongQueryController {
@Autowired
private SongQueryService songQueryService;
@GetMapping("{id}")
public Mono<SongDTO> getById(@PathVariable UUID id) {
return songQueryService.getById(id);
}
@GetMapping("album/{id}")
public Flux<SongDTO> getAllByAlbumId(@PathVariable UUID id) {
return songQueryService.getAllByAlbumId(id);
}
@GetMapping("name/{name}")
public Flux<SongDTO> getAllByName(@PathVariable String name) {
return songQueryService.getAllByName(name);
}
}
SongInternalController.java
@RestController
class SongInternalController {
@Autowired
private InternalQueryService internalQueryService;
@GetMapping("{id}")
public Mono<Boolean> existsById(@PathVariable UUID id) {
return internalQueryService.existsSongById(id);
}
}
UserController.java
@RestController
@RequestMapping("user")
public class UserController {
@Autowired
private UserService userService;
@Autowired
@Qualifier("create")
private UserValidationService userValidationCreateService;
@Autowired
@Qualifier("edit")
private UserValidationService userValidationEditService;
@PutMapping
public Mono<ResponseEntity<UserResponse>> editUser(@RequestBody
Mono<UserDTO> userDTO) {
return userDTO
.subscribeOn(Schedulers.elastic())
.flatMap(user -> Mono.defer(() ->
userValidationEditService.validate(user)))
.flatMap(result -> Mono.defer(() -> {
List<Pair<Field, ErrorCode>> errors = result.getErrors();
if (errors != null && !errors.isEmpty()) {
return
Mono.just(ResponseEntity.badRequest().body(generateValidationErrorResp
onse(errors)));
} else {
return
userService.edit(result.getUserDTO()).subscribeOn(Schedulers.elastic())
.flatMap(user -> Mono.defer(() ->
Mono.just(ResponseEntity.ok(generateUserResponse(user)))));
}
}))
.onErrorResume(RuntimeException.class,
error -> Mono.defer(() ->
Mono.just(ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERR
OR).body(generateErrorResponse(error)))));
}
@DeleteMapping("{id}")
public Mono<ResponseEntity<UserResponse>> deleteUser(@PathVariable
UUID id) {
return userService.delete(id)
.subscribeOn(Schedulers.elastic())
.flatMap(result -> Mono.defer(() ->
Mono.just(ResponseEntity.ok(generateUserResponseWithId(result)))))
.onErrorResume(RuntimeException.class,
error -> Mono.defer(() ->
Mono.just(ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERR
OR).body(generateErrorResponse(error)))));
}
private static UserResponse generateUserResponse(UserDTO userDTO) {
UserResponse userResponse = new UserResponse();
userResponse.setUser(userDTO);
return userResponse;
}
private static UserResponse generateUserResponseWithId(UUID id) {
UserResponse userResponse = new UserResponse();
UserDTO userDTO = new UserDTO();
userDTO.setId(id);
userResponse.setUser(userDTO);
return userResponse;
}
private static UserResponse
generateValidationErrorResponse(List<Pair<Field, ErrorCode>> errors) {
UserResponse userResponse = new UserResponse();
Pair<Field, ErrorCode> error = errors.get(0);
userResponse.setCode(error.getSecond());
userResponse.setErrorType(ErrorType.VALIDATION);
userResponse.setParameters(Collections.singletonMap(ErrorParameters.FIE
LD, error.getFirst().toString()));
return userResponse;
}
private static UserResponse generateErrorResponse(Exception ex) {
UserResponse userResponse = new UserResponse();
userResponse.setErrorType(ErrorType.EXECUTING);
userResponse.setParameters(Collections.singletonMap(ErrorParameters.ME
SSAGE, ex.getMessage()));
return userResponse;
}
}
UserQueryController.java
@RestController
@RequestMapping("user")
public class UserQueryController {
@Autowired
private QueryService queryService;
@GetMapping("{id}")
public Mono<UserDTO> getUserById(@PathVariable UUID id) {
return queryService.getUserById(id);
}
@GetMapping("name/{name}")
public Mono<UserDTO> getUserByName(@PathVariable String name) {
return queryService.getUserByName(name);
}
@PostMapping
public Mono<ResponseEntity<UserResponse>> createUser(@RequestBody
Mono<UserDTO> userDTO) {
return userDTO
.subscribeOn(Schedulers.elastic())
.flatMap(user -> Mono.defer(() ->
userValidationCreateService.validate(user)))
.flatMap(result -> Mono.defer(() -> {
List<Pair<Field, ErrorCode>> errors = result.getErrors();
if (errors != null && !errors.isEmpty()) {
return
Mono.just(ResponseEntity.badRequest().body(generateValidationErrorResp
onse(errors)));
} else {
return
internalService.create(result.getUserDTO()).subscribeOn(Schedulers.elastic
())
.flatMap(user -> Mono.defer(() ->
Mono.just(ResponseEntity.ok(generateUserResponse(user)))));
}
}))
.onErrorResume(RuntimeException.class,
error -> Mono.defer(() ->
Mono.just(ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERR
OR).body(generateErrorResponse(error)))));
}
}
}
InternalController.java
@RestController
@RequestMapping("internal/user")
public class InternalController {
@Autowired
private InternalService internalService;
@GetMapping("{id}")
public Mono<Boolean> userExistsById(@PathVariable UUID id) {
return internalService.userExistsById(id);
}
}
LikeController.java
@RestController
@RequestMapping("like")
public class LikeController {
private final ValidationService validationService;
private final LikeService likeService;
public LikeController(ValidationService validationService, LikeService
likeService) {
this.validationService = validationService;
this.likeService = likeService;
}
@PostMapping
public Mono<ResponseEntity<LikeResponse>> addLike(@RequestBody
Mono<LikeDTO> likeDTO) {
return likeDTO
.subscribeOn(Schedulers.elastic())
.flatMap(validationService::validate)
.flatMap(result -> {
if (result.isValid()) {
return likeService.addLike(result.getLikeDTO())
.map(LikeController::generateLikeResponse)
.map(ResponseEntity::ok);
}
return
Mono.just(ResponseEntity.badRequest().body(generateValidationErrorLike
Response(result.getErrors())));
})
.onErrorResume(error ->
Mono.just(ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERR
OR).body(generateErrorResponse(error))));
}
@DeleteMapping("{id}")
public Mono<Void> deleteLike(@PathVariable UUID id) {
return likeService.deleteLikeById(id);
}
private static LikeResponse generateLikeResponse(LikeDTO likeDTO) {
LikeResponse likeResponse = new LikeResponse();
likeResponse.setLikeDTO(likeDTO);
return likeResponse;
}
private static LikeResponse
generateValidationErrorLikeResponse(List<String> errors) {
LikeResponse likeResponse = new LikeResponse();
LikeError likeError = new LikeError();
likeError.setType(ErrorType.VALIDATION);
likeError.setError(errors.stream().collect(Collectors.toMap(o -> "FIELD",
o -> o)));
likeResponse.setError(likeError);
return likeResponse;
}
private static LikeResponse generateErrorResponse(Throwable exception)
{
LikeResponse likeResponse = new LikeResponse();
LikeError likeError = new LikeError();
likeError.setType(ErrorType.EXECUTION);
likeError.setError(Collections.singletonMap("MESSAGE",
exception.getMessage()));
likeResponse.setError(likeError);
return likeResponse;
}
}
LikeQueryController.java
@RestController
private LikeQueryService likeQueryService;
@GetMapping("user/{id}")
public Flux<LikeDTO> getLikesByUserId(@PathVariable UUID id) {
return likeQueryService.getLikesByUserId(id);
}
@GetMapping("album/{id}/count")
public Mono<Long> getCountOfLikesByAlbumId(@PathVariable UUID
id) {
return likeQueryService.countLikesByAlbumId(id);
}
@GetMapping("album/{id}")
public Flux<LikeDTO> getLikesByAlbumId(@PathVariable UUID id) {
return likeQueryService.getLikesByAlbumId(id);
}
@GetMapping("top")
public Flux<UUID> getTop() {
return likeQueryService.getTop();
}
}
HistoryController.java
@RestController
@RequestMapping("history")
public class HistoryController {
@Autowired
private HistoryService historyService;
@Autowired
private HistoryQueryService historyQueryService;
@PostMapping
public Mono<Void> addHistory(@RequestBody HistoryElementDTO
historyElementDTOMono) {
return historyService.addHistory(historyElementDTOMono);
}
@GetMapping("user/{id}")
public Flux<HistoryElementDTO> getHistoryOfUserById(@PathVariable
UUID id) {
return historyQueryService.getAllHistoryByUserId(id);
@RequestMapping("comment")
public class CommentController {
@Autowired
private CommentService commentService;
@Autowired
private UserAdapterService userAdapterService;
@PostMapping
public Mono<ResponseEntity<CommentResponse>>
create(@RequestBody Mono<CommentDTO> comment) {
return comment
.subscribeOn(Schedulers.elastic())
.flatMap(result -> Mono.defer(() ->
userAdapterService.userExistsById(result)))
.flatMap(result -> Mono.defer(() -> {
if (result.getUserExistence()) {
return commentService.create(result.getComment())
.flatMap(commentDTO -> Mono.defer(() -> {
CommentResponse commentResponse = new CommentResponse();
commentResponse.setComment(commentDTO);
return Mono.just(ResponseEntity.ok(commentResponse));
}));
} else {
return
Mono.just(ResponseEntity.badRequest().body(generateValidationErrorResp
onse(result.getComment().getUserId())));
}
}))
.onErrorResume(RuntimeException.class,
error -> Mono.defer(() ->
Mono.just(ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERR
OR).body(generateErrorResponse(error)))));
}
@DeleteMapping("{id}")
public Mono<Void> delete(@PathVariable UUID id) {
return commentService.deleteById(id);
}
private static CommentResponse generateValidationErrorResponse(UUID
id) {
CommentResponse commentResponse = new CommentResponse();
commentResponse.setCode(ErrorCode.REFERENCE_ERROR);
commentResponse.setErrorType(ErrorType.VALIDATION);
commentResponse.setParameters(Collections.singletonMap(ErrorParameter
s.FIELD, "userId"));
return commentResponse;
}
private static CommentResponse generateErrorResponse(Exception ex) {
CommentResponse commentResponse = new CommentResponse();
commentResponse.setErrorType(ErrorType.EXECUTING);
commentResponse.setParameters(Collections.singletonMap(ErrorParameter
s.MESSAGE, ex.getMessage()));
return commentResponse;
}
}
Основной контроллер для получения сущностей комментариев
(Comment):
CommentQueryController.java
@RestController
@RequestMapping("comment")
public class CommentQueryController {
private final CommentService commentService;
@GetMapping("album/{id}")
public Flux<CommentDTO> getCommentsByAlbumId(@PathVariable
UUID albumId) {
return Flux.interval(Duration.ofMillis(500))
.flatMap(element ->
commentService.getAllCommentsByAlbumId(albumId));
}
@GetMapping("user/{id}")
public Flux<CommentDTO> getCommentsByUserId(@PathVariable UUID
userId) {
return Flux.interval(Duration.ofMillis(500))
.flatMap(element -> commentService.getByUserId(userId));
}
}
ImageController.java
@RestController
@RequestMapping("image")
public class ImageController {
@Autowired
@Qualifier("artist")
private FileUploadService fileUploadArtistService;
@Autowired
@Qualifier("album")
private FileUploadService fileUploadAlbumService;
@Autowired
private ImageService imageService;
@PostMapping(value = "artist/{id}", consumes =
MediaType.MULTIPART_FORM_DATA_VALUE)
public Mono<Void> uploadArtistHandler(@PathVariable UUID id,
@RequestPart Mono<FilePart> file) {
return uploadFile(id, file)
.flatMap(fileContainer -> Mono.defer(() ->
fileUploadArtistService.saveFile(fileContainer)));
}
@PostMapping(value = "album/{id}",
consumes = MediaType.MULTIPART_FORM_DATA_VALUE,
produces = MediaType.APPLICATION_STREAM_JSON_VALUE)
") UUID id, @RequestPart("file") Mono<FilePart> file) {
return uploadFile(id, file)
.flatMap(fileContainer ->
fileUploadAlbumService.saveFile(fileContainer));
}
@GetMapping(value = "artist/{id}",
produces = MediaType.MULTIPART_FORM_DATA_VALUE)
public Flux<byte[]> getArtistHandler(@PathVariable("id") UUID id) {
return imageService.downloadArtistImage(id);
}
@GetMapping(value = "album/{id}",
produces = MediaType.MULTIPART_FORM_DATA_VALUE)
public Flux<byte[]> getAlbumHandler(@PathVariable("id") UUID id) {
return imageService.downloadAlbumImage(id);
}
private Mono<FileContainer> uploadFile(UUID id, Mono<FilePart> file) {
return file
ilePart -> Mono.defer(() -> Mono.create(result -> {
FileContainer fileContainer = new FileContainer();
fileContainer.setId(id);
fileContainer.setFile(filePart);
result.success(fileContainer);
})));
}
}
MusicController.java
@RestController
@RequestMapping("music")
public class MusicController {
@Autowired
private FileService fileService;
@PostMapping(value = "{id}", consumes =
MediaType.MULTIPART_FORM_DATA_VALUE)
public Mono<Void> uploadArtistHandler(@PathVariable UUID id,
@RequestBody Flux<Part> parts) {
return parts
.filter(part -> part instanceof FilePart)
.ofType(FilePart.class)
.map(filePart -> new FileContainer(id, filePart))
.flatMap(fileContainer -> fileService.saveFile(fileContainer))
.then();
}
@GetMapping(value = "{id}", produces =
MediaType.MULTIPART_FORM_DATA_VALUE)
public Flux<byte[]> uploadArtistHandler(@PathVariable UUID id) {
return fileService.downloadFile(id);
}
}
app-routing.module.ts
import {NgModule} from '@angular/core';
import {Routes, RouterModule} from '@angular/router';
import {WelcomePageComponent} from './welcome-page/welcome-
page/welcome-page.component';
import {MainPageComponent} from './main-page/main-page/main-
page.component';
import {HomePageComponent} from './main-page/home-page/home-
page.component';
import {ArtistPageComponent} from "./main-page/artist-page/artist-
page.component";
import {AlbumPageModule} from "./main-page/album-page/album-
page.module";
import {WelcomePageModule} from "./welcome-page/welcome-
page.module";
import {MainPageModule} from "./main-page/main-page.module";
import {ArtistPageModule} from "./main-page/artist-page/artist-
page.module";
import {AlbumPageComponent} from "./main-page/album-page/album-
page.component";
import {TopPageComponent} from "./main-page/top-page/top-
page.component";
import {HistoryComponent} from "./main-page/history/history.component";
import {HistoryModule} from "./main-page/history/history.module";
import {ArtistLabComponent} from "./main-page/artist-lab/artist-
lab.component";
import {ArtistLabModule} from "./main-page/artist-lab/artist-lab.module";
import {AlbumLabComponent} from "./main-page/artist-lab/album-
lab/album-lab.component";
import {AlbumLabModule} from "./main-page/artist-lab/album-lab/album-
lab.module";
const routes: Routes = [
{path: '', redirectTo: '/welcome', pathMatch: 'full'},
{path: 'welcome', component: WelcomePageComponent},
{
path: 'home', component: MainPageComponent, children: [
{path: 'artist/:id', component: ArtistPageComponent},
{path: 'album/:id', component: AlbumPageComponent},
{path: 'top', component: TopPageComponent},
{path: 'history', component: HistoryComponent},
{path: 'user/artist/:id', component: ArtistLabComponent},
{path: 'user/artist/:id/album', component: AlbumLabComponent},
{
path: '', component: HomePageComponent
}
]
}
];
@NgModule({
imports: [RouterModule.forRoot(routes),
WelcomePageModule,
MainPageModule,
ArtistPageModule,
AlbumPageModule,
HistoryModule,
ArtistLabModule,
AlbumLabModule
],
exports: [RouterModule]
})
export class AppRoutingModule {
}
comment.component.ts
@Component({
selector: 'app-history-comment',
templateUrl: './comment.component.html',
styleUrls: ['./comment.component.css']})
export class CommentComponent implements OnInit {
displayedColumns: string[] = ['picture', 'name', 'comment'];
dataSource = ELEMENT_DATA;
expandedElement: CommentHistoryElement | null;
constructor() { }
ngOnInit() {
}
changeComment(comment: string): string[] {
let result = new Array<string>();
if (comment.length <= limitOfCommentLine) {
result.push(comment);
return result;
}
const length = result.length + limitOfCommentLine;
for (let i = 0; i <= length; i += limitOfCommentLine) {
result.push(comment.substr(i, limitOfCommentLine).split('').filter(letter => letter).join(''));
}
return result;
}
}
artist-edit.component.ts
@Component({
selector: 'app-artist-edit',
templateUrl: './artist-edit.component.html',
styleUrls: ['./artist-edit.component.css']
})
export class ArtistEditComponent implements OnInit {
@Input() artist: ArtistLabElement;
isEdit: boolean;
@Inject(MAT_DIALOG_DATA) public dataArtist: ArtistLabModel) {
this.artist = dataArtist.artist;
this.isEdit = dataArtist.type === LabType.edit;
}
ngOnInit() {
}
save() {
this.close();
}
cancel() {
this.close();
}
private close() {
this.dialogRef.close();
}
}
artist-request-service.service.ts
@Injectable({
providedIn: 'root'
})
export class ArtistRequestServiceService{
private readonly ARTIST_GET_BY_ID_URL: string;
private readonly ARTIST_GET_TOP_URL: string;
constructor(private http: HttpClient) {
this.ARTIST_GET_BY_ID_URL = environment.url +
ARTIST_GET_BY_ID;
this.ARTIST_GET_TOP_URL = environment.url +
ARTIST_GET_BY_TOP;
}
getArtistById(id: string): Observable<Artist> {
const url = this.ARTIST_GET_BY_ID_URL + id;
return this.http.get<Artist>(url);
}
getArtistTopByLikes(): Observable<Artist[]> {
return this.http.get<Artist[]>(this.ARTIST_GET_TOP_URL);
}
}
album-request-service.service.ts
@Injectable({
providedIn: 'root'
})
export class AlbumRequestServiceService {
private readonly ALBUM_GET_BY_ID_URL: string;
private readonly ALBUM_GET_TOP_URL: string;
constructor(private http: HttpClient) {
this.ALBUM_GET_BY_ID_URL = environment.url +
ALBUM_GET_BY_ID;
this.ALBUM_GET_TOP_URL = environment.url +
ALBUM_GET_BY_TOP;
}
getAlbumById(id: string): Observable<Album> {
const url = this.ALBUM_GET_BY_ID_URL + id;
return this.http.get<Album>(url);
}
getAlbumTopByLikes(): Observable<Album[]> {
return this.http.get<Album[]>(this.ALBUM_GET_TOP_URL);
}
}
Размещено на allbest.ru
...Подобные документы
Проблема управления инфраструктурой веб-приложения с микросервисной архитектурой. Тенденции к созданию программного обеспечения. Ключевые направления в разработке веб-приложений. Архитектура спроектированной системы мониторинга. Эффективность сервиса.
статья [532,1 K], добавлен 10.12.2016Выбор и обоснование аппаратного обеспечения. Типы архитектуры веб-приложений. Шаблоны проектирования архитектуры приложения. Разработка инфологической модели базы данных. Подготовка к разработке приложения. Рассмотрение причин возникновения паттернов.
дипломная работа [3,0 M], добавлен 27.11.2022Реализация web-сервиса для сбора и анализа статистических данных по тексту, а также web-приложения, поддерживающего взаимодействие с сервисом и организующего пользовательский интерфейс. Проектирование архитектуры приложения. Язык программирования C#.
курсовая работа [417,6 K], добавлен 25.03.2015- Создание защищенного приложения для ведения учета продаж и закупок, ориентированного на малый бизнес
Проектирование модели базы данных в соответствии с предметной областью "Торговля". Разработка архитектуры системы безопасности приложения по ведению базы данных. Реализация приложения, обеспечивающего учет продаж и закупок предприятия. Способы его защиты.
дипломная работа [2,5 M], добавлен 05.02.2017 Основные концепции разработки приложения в архитектуре MVVM. Проектирование базы данных, предназначенной для сбора информации о дорожно-транспортных происшествиях. Классификация и типы архитектуры "клиент–сервер", ее основные достоинства и недостатки.
курсовая работа [4,1 M], добавлен 25.11.2015Анализ создания удобного инструмента, максимально упрощающего процесс осуществления заказа клиентом ювелирных изделий. Изучение принципов построения web-сайта, структуры базы данных, проектирования архитектуры приложения и пользовательского интерфейса.
дипломная работа [7,0 M], добавлен 11.02.2012Характеристика объекта автоматизации. Создание многоуровневой архитектуры приложения, отладка метода безошибочной идентификации пользователей системы. Разработка нестандартного метода преобразования объектов базы данных в объекты классов приложения.
курсовая работа [395,4 K], добавлен 28.04.2015Понятие архитектуры программного обеспечения (ПО). Характеристика этапов процесса проектирования и его окончательный продукт. Языки описания и виды архитектуры ПО, базовые фреймворки. Функции разработчика архитектуры ПО и необходимые ему навыки работы.
реферат [85,0 K], добавлен 15.02.2014Разработка информационной системы "Больница" на основе Java EE-технологий. Проект и реализация трехслойного enterprise-приложения, работающего с базой данных больницы, его структура. Предметная область; визуализация архитектуры с помощью UML-диаграмм.
курсовая работа [2,0 M], добавлен 22.10.2012Постановка задач и требований к проектируемому интернет-приложению. Обоснование выбора системы управления базы данных и языков программирования. Разработка архитектуры заданного интернет-приложения, технико-экономическое обоснование его эффективности.
дипломная работа [461,3 K], добавлен 24.02.2013Проблема выбора товара в Интернете. Типы и свойства онтологий как части концепции Semantic Web. Разработка web-приложения для выбора музыкального инструмента: создание иерархии онтологий для предметной области "Гитара", формирование SPARQL-запроса.
дипломная работа [2,2 M], добавлен 20.04.2012Изучение существующих подходов к использованию компьютерных игр в образовательном процессе. Разработка и реализация проекта игрового обучающего приложения на мобильной платформе. Выбор платформы и средств реализации игрового обучающего приложения.
дипломная работа [3,4 M], добавлен 12.08.2017Ознакомление с проблемами реализации сервис-ориентированной архитектуры предприятия. Анализ активных элементов бизнес-архитектуры. Рассмотрение инструментов реализации языка ArchiMate в программном средстве Archi. Исследование мотивационных концепций.
курсовая работа [2,0 M], добавлен 25.08.2017Описания программного продукта компании 1С, предназначенного для быстрой разработки прикладных решений. Исследование типов архитектур построения баз данных. Технология с сетью и файловым сервером. Анализ особенностей трехзвенной архитектуры клиент-сервер.
курсовая работа [401,4 K], добавлен 12.01.2015Обзор существующих объектных архитектур. Архитектура программного обеспечения. Создание веб-сервиса "Библиотека", предоставляющего механизмы работы с данными на стороне клиентского приложения. WEB-сервис и трехуровневая архитектура в основе приложения.
лабораторная работа [1,5 M], добавлен 16.06.2013Обзор подходов к разработке музейных приложений с элементами дополненной реальности, формирование требований к ним. Выбор методов разработки приложения, разработка пользовательского интерфейса. Принципы тестирования. Реализация раздела "Распознавание".
дипломная работа [2,8 M], добавлен 03.07.2017Изучение существующих подходов к использованию компьютерных игр в образовательном процессе. Особенности использования мобильного обучения. Методика и этапы закрепления полученных ранее знаний с использованием игрового приложения на мобильной платформе.
дипломная работа [813,0 K], добавлен 27.10.2017Анализ архитектуры ОС Windows 8. Сравнение с предыдущими версиями (интерфейс Modern UI, работа с учетными записями, модель безопасности, диспетчер задач, история файлов, восстановление системы, Storage Spaces). Особенности различных версий Windows 8.
курсовая работа [289,1 K], добавлен 25.01.2016Создание клиент-серверного приложения "Чат" с помощью среды визуальной разработки приложений Borland C++ Builder версии 6. Описание функциональности приложения: наличие клиент-серверной архитектуры, обмен короткими сообщениями, а также передача файлов.
курсовая работа [302,0 K], добавлен 30.01.2012Проблемы создания многоядерных процессоров, новейшие классификации и перспективы развития. Особенности реализации многоядерной архитектуры: параллельные вычисления, программное обеспечение. Инструментарий для разработки многопоточных приложений.
курсовая работа [605,4 K], добавлен 21.03.2013