diff --git a/filmorate-app/pom.xml b/filmorate-app/pom.xml
new file mode 100644
index 0000000..e2c5b38
--- /dev/null
+++ b/filmorate-app/pom.xml
@@ -0,0 +1,93 @@
+
+
+ 4.0.0
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.7.5
+
+
+
+ dev.struchkov.filmorate
+ filmorate-app
+ 0.0.1-SNAPSHOT
+ jar
+
+
+
+
+ dev.struchkov.filmorate
+ filmorate-bom
+ ${filmorate.version}
+ pom
+ import
+
+
+
+
+
+ 17
+ ${java.version}
+ ${java.version}
+ UTF-8
+ UTF-8
+
+ 0.0.1-SNAPSHOT
+
+
+
+
+ dev.struchkov.filmorate
+ filmorate-core
+
+
+ dev.struchkov.filmorate
+ filmorate-data-jpa
+
+
+
+
+
+
+ dev.struchkov.filmorate
+ filmorate-web
+
+
+
+ org.springframework.boot
+ spring-boot-devtools
+ runtime
+ true
+
+
+ org.springframework.boot
+ spring-boot-configuration-processor
+ true
+
+
+ org.springframework.boot
+ spring-boot-actuator
+
+
+
+
+ filmorate
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+ org.projectlombok
+ lombok
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/java/dev/struchkov/filmorate/FilmorateApplication.java b/filmorate-app/src/main/java/dev/struchkov/filmorate/FilmorateApplication.java
similarity index 100%
rename from src/main/java/dev/struchkov/filmorate/FilmorateApplication.java
rename to filmorate-app/src/main/java/dev/struchkov/filmorate/FilmorateApplication.java
diff --git a/filmorate-app/src/main/resources/application-local.yml b/filmorate-app/src/main/resources/application-local.yml
new file mode 100644
index 0000000..cc08e49
--- /dev/null
+++ b/filmorate-app/src/main/resources/application-local.yml
@@ -0,0 +1,5 @@
+spring:
+ application:
+ name: Filmorate Local
+ liquibase:
+ contexts: local
\ No newline at end of file
diff --git a/filmorate-app/src/main/resources/application.yml b/filmorate-app/src/main/resources/application.yml
new file mode 100644
index 0000000..ae58d81
--- /dev/null
+++ b/filmorate-app/src/main/resources/application.yml
@@ -0,0 +1,10 @@
+spring:
+ application:
+ name: Filmorate
+ liquibase:
+ change-log: db/changelog/changelog-master.xml
+ datasource:
+ driver-class-name: org.postgresql.Driver
+ url: ${DB_URL}
+ username: ${DB_USERNAME}
+ password: ${DB_PASSWORD}
diff --git a/filmorate-app/src/main/resources/db/changelog/changelog-master.xml b/filmorate-app/src/main/resources/db/changelog/changelog-master.xml
new file mode 100644
index 0000000..5f7bae2
--- /dev/null
+++ b/filmorate-app/src/main/resources/db/changelog/changelog-master.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/filmorate-app/src/main/resources/db/changelog/v.1.0.0/2022-10-22-create-main-tables.xml b/filmorate-app/src/main/resources/db/changelog/v.1.0.0/2022-10-22-create-main-tables.xml
new file mode 100644
index 0000000..efb7b2d
--- /dev/null
+++ b/filmorate-app/src/main/resources/db/changelog/v.1.0.0/2022-10-22-create-main-tables.xml
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/filmorate-app/src/main/resources/db/changelog/v.1.0.0/2022-10-22-insert-test-data.xml b/filmorate-app/src/main/resources/db/changelog/v.1.0.0/2022-10-22-insert-test-data.xml
new file mode 100644
index 0000000..e361622
--- /dev/null
+++ b/filmorate-app/src/main/resources/db/changelog/v.1.0.0/2022-10-22-insert-test-data.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/filmorate-app/src/main/resources/db/changelog/v.1.0.0/changelog.xml b/filmorate-app/src/main/resources/db/changelog/v.1.0.0/changelog.xml
new file mode 100644
index 0000000..c76647e
--- /dev/null
+++ b/filmorate-app/src/main/resources/db/changelog/v.1.0.0/changelog.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/filmorate-bom/pom.xml b/filmorate-bom/pom.xml
new file mode 100644
index 0000000..d33e951
--- /dev/null
+++ b/filmorate-bom/pom.xml
@@ -0,0 +1,84 @@
+
+
+ 4.0.0
+
+ dev.struchkov.filmorate
+ filmorate-bom
+ 0.0.1-SNAPSHOT
+ pom
+
+
+ 17
+ 17
+ UTF-8
+
+ 0.0.1-SNAPSHOT
+ ${filmorate.version}
+ ${filmorate.version}
+ ${filmorate.version}
+ ${filmorate.version}
+ ${filmorate.version}
+ ${filmorate.version}
+
+ 2.7.5
+
+ 1.5.2.Final
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ ${spring.starter.parent.version}
+ pom
+ import
+
+
+
+ dev.struchkov.filmorate
+ filmorate-domain
+ ${filmorate.domain.version}
+
+
+ dev.struchkov.filmorate
+ filmorate-context
+ ${filmorate.context.version}
+
+
+ dev.struchkov.filmorate
+ filmorate-core
+ ${filmorate.core.version}
+
+
+ dev.struchkov.filmorate
+ filmorate-data-local
+ ${filmorate.data.local.version}
+
+
+ dev.struchkov.filmorate
+ filmorate-data-jpa
+ ${filmorate.data.jpa.version}
+
+
+ dev.struchkov.filmorate
+ filmorate-web
+ ${filmorate.web.version}
+
+
+
+ org.mapstruct
+ mapstruct
+ ${mapstruct.version}
+
+
+ org.mapstruct
+ mapstruct-processor
+ ${mapstruct.version}
+
+
+
+
+
\ No newline at end of file
diff --git a/filmorate-context/pom.xml b/filmorate-context/pom.xml
new file mode 100644
index 0000000..5f37749
--- /dev/null
+++ b/filmorate-context/pom.xml
@@ -0,0 +1,44 @@
+
+
+
+ dev.struchkov.filmorate
+ filmorate
+ 0.0.1-SNAPSHOT
+
+ 4.0.0
+
+ filmorate-context
+
+
+ 17
+ 17
+ UTF-8
+
+
+
+
+ dev.struchkov.filmorate
+ filmorate-domain
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+ org.apache.maven.plugins
+ maven-source-plugin
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+
+
+
+
+
\ No newline at end of file
diff --git a/filmorate-context/src/main/java/dev/struchkov/filmorate/context/Message.java b/filmorate-context/src/main/java/dev/struchkov/filmorate/context/Message.java
new file mode 100644
index 0000000..ca1d841
--- /dev/null
+++ b/filmorate-context/src/main/java/dev/struchkov/filmorate/context/Message.java
@@ -0,0 +1,19 @@
+package dev.struchkov.filmorate.context;
+
+import lombok.experimental.UtilityClass;
+
+@UtilityClass
+public final class Message {
+
+ @UtilityClass
+ public static class NotFound {
+ public static final String FILM = "Фильм не найден. Идентификатор: {0}";
+ public static final String PERSON = "Пользователь не найден. Идентификатор: {0}";
+ }
+
+ @UtilityClass
+ public static class Error {
+
+ }
+
+}
diff --git a/filmorate-context/src/main/java/dev/struchkov/filmorate/context/exception/FilmorateException.java b/filmorate-context/src/main/java/dev/struchkov/filmorate/context/exception/FilmorateException.java
new file mode 100644
index 0000000..678a015
--- /dev/null
+++ b/filmorate-context/src/main/java/dev/struchkov/filmorate/context/exception/FilmorateException.java
@@ -0,0 +1,15 @@
+package dev.struchkov.filmorate.context.exception;
+
+import java.text.MessageFormat;
+
+public abstract class FilmorateException extends RuntimeException {
+
+ protected FilmorateException(String message) {
+ super(message);
+ }
+
+ protected FilmorateException(String message, Object... args) {
+ super(MessageFormat.format(message, args));
+ }
+
+}
diff --git a/filmorate-context/src/main/java/dev/struchkov/filmorate/context/exception/NotFoundException.java b/filmorate-context/src/main/java/dev/struchkov/filmorate/context/exception/NotFoundException.java
new file mode 100644
index 0000000..acf9be3
--- /dev/null
+++ b/filmorate-context/src/main/java/dev/struchkov/filmorate/context/exception/NotFoundException.java
@@ -0,0 +1,15 @@
+package dev.struchkov.filmorate.context.exception;
+
+import java.util.function.Supplier;
+
+public class NotFoundException extends FilmorateException{
+
+ public NotFoundException(String message, Object... args) {
+ super(message, args);
+ }
+
+ public static Supplier notFoundException(String message, Object... args) {
+ return () -> new NotFoundException(message, args);
+ }
+
+}
diff --git a/filmorate-context/src/main/java/dev/struchkov/filmorate/context/repository/FilmRepository.java b/filmorate-context/src/main/java/dev/struchkov/filmorate/context/repository/FilmRepository.java
new file mode 100644
index 0000000..c1800c6
--- /dev/null
+++ b/filmorate-context/src/main/java/dev/struchkov/filmorate/context/repository/FilmRepository.java
@@ -0,0 +1,20 @@
+package dev.struchkov.filmorate.context.repository;
+
+import dev.strichkov.filmorate.domain.Film;
+import dev.strichkov.filmorate.domain.page.Pagination;
+import dev.strichkov.filmorate.domain.page.Sheet;
+import lombok.NonNull;
+
+import java.util.Optional;
+
+public interface FilmRepository {
+
+ Film save(@NonNull Film film);
+
+ Optional findById(@NonNull Long filmId);
+
+ Sheet findAll(@NonNull Pagination pagination);
+
+ void deleteById(@NonNull Long filmId);
+
+}
diff --git a/filmorate-context/src/main/java/dev/struchkov/filmorate/context/repository/PersonRepository.java b/filmorate-context/src/main/java/dev/struchkov/filmorate/context/repository/PersonRepository.java
new file mode 100644
index 0000000..3a54ba0
--- /dev/null
+++ b/filmorate-context/src/main/java/dev/struchkov/filmorate/context/repository/PersonRepository.java
@@ -0,0 +1,20 @@
+package dev.struchkov.filmorate.context.repository;
+
+import dev.strichkov.filmorate.domain.Person;
+import dev.strichkov.filmorate.domain.page.Pagination;
+import dev.strichkov.filmorate.domain.page.Sheet;
+import lombok.NonNull;
+
+import java.util.Optional;
+
+public interface PersonRepository {
+
+ Person save(@NonNull Person person);
+
+ Optional findById(@NonNull Long personId);
+
+ void deleteById(@NonNull Long personId);
+
+ Sheet findByAll(@NonNull Pagination pagination);
+
+}
diff --git a/filmorate-context/src/main/java/dev/struchkov/filmorate/context/service/FilmService.java b/filmorate-context/src/main/java/dev/struchkov/filmorate/context/service/FilmService.java
new file mode 100644
index 0000000..33d2555
--- /dev/null
+++ b/filmorate-context/src/main/java/dev/struchkov/filmorate/context/service/FilmService.java
@@ -0,0 +1,24 @@
+package dev.struchkov.filmorate.context.service;
+
+import dev.strichkov.filmorate.domain.Film;
+import dev.strichkov.filmorate.domain.page.Pagination;
+import dev.strichkov.filmorate.domain.page.Sheet;
+import lombok.NonNull;
+
+import java.util.Optional;
+
+public interface FilmService {
+
+ Film create(@NonNull Film film);
+
+ Film update(@NonNull Film film);
+
+ void deleteById(@NonNull Long filmId);
+
+ Optional getById(@NonNull Long filmId);
+
+ Film getByIdOrThrow(@NonNull Long filmId);
+
+ Sheet getAll(@NonNull Pagination pagination);
+
+}
diff --git a/filmorate-context/src/main/java/dev/struchkov/filmorate/context/service/PersonService.java b/filmorate-context/src/main/java/dev/struchkov/filmorate/context/service/PersonService.java
new file mode 100644
index 0000000..2090e47
--- /dev/null
+++ b/filmorate-context/src/main/java/dev/struchkov/filmorate/context/service/PersonService.java
@@ -0,0 +1,24 @@
+package dev.struchkov.filmorate.context.service;
+
+import dev.strichkov.filmorate.domain.Person;
+import dev.strichkov.filmorate.domain.page.Pagination;
+import dev.strichkov.filmorate.domain.page.Sheet;
+import lombok.NonNull;
+
+import java.util.Optional;
+
+public interface PersonService {
+
+ Person create(@NonNull Person person);
+
+ Person update(@NonNull Person person);
+
+ void deleteById(@NonNull Long personId);
+
+ Optional getById(@NonNull Long personId);
+
+ Person getByIdOrThrow(@NonNull Long personId);
+
+ Sheet getAll(@NonNull Pagination pagination);
+
+}
diff --git a/filmorate-core/pom.xml b/filmorate-core/pom.xml
new file mode 100644
index 0000000..3a0ecaf
--- /dev/null
+++ b/filmorate-core/pom.xml
@@ -0,0 +1,53 @@
+
+
+
+ dev.struchkov.filmorate
+ filmorate
+ 0.0.1-SNAPSHOT
+
+ 4.0.0
+
+ filmorate-core
+
+
+ 17
+ 17
+ UTF-8
+
+
+
+
+ dev.struchkov.filmorate
+ filmorate-context
+
+
+
+ org.springframework
+ spring-context
+
+
+ org.springframework
+ spring-tx
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+ org.apache.maven.plugins
+ maven-source-plugin
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+
+
+
+
+
\ No newline at end of file
diff --git a/filmorate-core/src/main/java/dev/struchkov/filmorate/core/service/FilmServiceImpl.java b/filmorate-core/src/main/java/dev/struchkov/filmorate/core/service/FilmServiceImpl.java
new file mode 100644
index 0000000..0dbe14c
--- /dev/null
+++ b/filmorate-core/src/main/java/dev/struchkov/filmorate/core/service/FilmServiceImpl.java
@@ -0,0 +1,69 @@
+package dev.struchkov.filmorate.core.service;
+
+import dev.strichkov.filmorate.domain.Film;
+import dev.strichkov.filmorate.domain.page.Pagination;
+import dev.strichkov.filmorate.domain.page.Sheet;
+import dev.struchkov.filmorate.context.Message;
+import dev.struchkov.filmorate.context.repository.FilmRepository;
+import dev.struchkov.filmorate.context.service.FilmService;
+import lombok.NonNull;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.time.LocalDateTime;
+import java.util.Optional;
+
+import static dev.struchkov.filmorate.context.exception.NotFoundException.notFoundException;
+
+@Service
+@RequiredArgsConstructor
+public class FilmServiceImpl implements FilmService {
+
+ private final FilmRepository repository;
+
+ @Override
+ @Transactional
+ public Film create(@NonNull Film film) {
+ film.setDateCreated(film.getDateCreated());
+ return repository.save(film);
+ }
+
+ @Override
+ @Transactional
+ public Film update(@NonNull Film newFilm) {
+ final Film oldFilm = repository.findById(newFilm.getId())
+ .orElseThrow(notFoundException(Message.NotFound.FILM, newFilm.getId()));
+ oldFilm.setName(newFilm.getName());
+ oldFilm.setDurationInMinutes(newFilm.getDurationInMinutes());
+ oldFilm.setDateRelease(newFilm.getDateRelease());
+ oldFilm.setDescription(newFilm.getDescription());
+ oldFilm.setDateUpdated(LocalDateTime.now());
+ return oldFilm;
+ }
+
+ @Override
+ public void deleteById(@NonNull Long filmId) {
+ repository.deleteById(filmId);
+ }
+
+ @Override
+ @Transactional(readOnly = true)
+ public Optional getById(@NonNull Long filmId) {
+ return repository.findById(filmId);
+ }
+
+ @Override
+ @Transactional(readOnly = true)
+ public Film getByIdOrThrow(@NonNull Long filmId) {
+ return repository.findById(filmId)
+ .orElseThrow(notFoundException(Message.NotFound.FILM, filmId));
+ }
+
+ @Override
+ @Transactional(readOnly = true)
+ public Sheet getAll(@NonNull Pagination pagination) {
+ return repository.findAll(pagination);
+ }
+
+}
diff --git a/filmorate-core/src/main/java/dev/struchkov/filmorate/core/service/PersonServiceImpl.java b/filmorate-core/src/main/java/dev/struchkov/filmorate/core/service/PersonServiceImpl.java
new file mode 100644
index 0000000..3e9e7fa
--- /dev/null
+++ b/filmorate-core/src/main/java/dev/struchkov/filmorate/core/service/PersonServiceImpl.java
@@ -0,0 +1,65 @@
+package dev.struchkov.filmorate.core.service;
+
+import dev.strichkov.filmorate.domain.Person;
+import dev.strichkov.filmorate.domain.page.Pagination;
+import dev.strichkov.filmorate.domain.page.Sheet;
+import dev.struchkov.filmorate.context.Message;
+import dev.struchkov.filmorate.context.repository.PersonRepository;
+import dev.struchkov.filmorate.context.service.PersonService;
+import lombok.NonNull;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.Optional;
+
+import static dev.struchkov.filmorate.context.exception.NotFoundException.notFoundException;
+
+@Service
+@RequiredArgsConstructor
+public class PersonServiceImpl implements PersonService {
+
+ private final PersonRepository repository;
+
+ @Override
+ @Transactional
+ public Person create(@NonNull Person person) {
+ return repository.save(person);
+ }
+
+ @Override
+ @Transactional
+ public Person update(@NonNull Person newPerson) {
+ final Person oldPerson = repository.findById(newPerson.getId())
+ .orElseThrow(notFoundException(Message.NotFound.PERSON, newPerson.getId()));
+ oldPerson.setName(newPerson.getName());
+ oldPerson.setEmail(newPerson.getEmail());
+ oldPerson.setBirthday(newPerson.getBirthday());
+ return oldPerson;
+ }
+
+ @Override
+ @Transactional
+ public void deleteById(@NonNull Long personId) {
+ repository.deleteById(personId);
+ }
+
+ @Override
+ @Transactional(readOnly = true)
+ public Optional getById(@NonNull Long personId) {
+ return repository.findById(personId);
+ }
+
+ @Override
+ @Transactional(readOnly = true)
+ public Person getByIdOrThrow(@NonNull Long personId) {
+ return repository.findById(personId)
+ .orElseThrow(notFoundException(Message.NotFound.PERSON, personId));
+ }
+
+ @Override
+ @Transactional(readOnly = true)
+ public Sheet getAll(@NonNull Pagination pagination) {
+ return repository.findByAll(pagination);
+ }
+}
diff --git a/filmorate-data-jpa/pom.xml b/filmorate-data-jpa/pom.xml
new file mode 100644
index 0000000..4a0c318
--- /dev/null
+++ b/filmorate-data-jpa/pom.xml
@@ -0,0 +1,57 @@
+
+
+
+ dev.struchkov.filmorate
+ filmorate
+ 0.0.1-SNAPSHOT
+
+ 4.0.0
+
+ filmorate-data-jpa
+
+
+
+ dev.struchkov.filmorate
+ filmorate-context
+
+
+
+ org.springframework.boot
+ spring-boot-starter-data-jpa
+
+
+ org.liquibase
+ liquibase-core
+
+
+ org.postgresql
+ postgresql
+ runtime
+
+
+
+ org.mapstruct
+ mapstruct
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+ org.apache.maven.plugins
+ maven-source-plugin
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+
+
+
+
+
\ No newline at end of file
diff --git a/filmorate-data-jpa/src/main/java/dev/struchkov/filmorate/data/entity/BaseEntity.java b/filmorate-data-jpa/src/main/java/dev/struchkov/filmorate/data/entity/BaseEntity.java
new file mode 100644
index 0000000..48f2343
--- /dev/null
+++ b/filmorate-data-jpa/src/main/java/dev/struchkov/filmorate/data/entity/BaseEntity.java
@@ -0,0 +1,22 @@
+package dev.struchkov.filmorate.data.entity;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import javax.persistence.Column;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.MappedSuperclass;
+
+@Getter
+@Setter
+@MappedSuperclass
+public abstract class BaseEntity {
+
+ @Id
+ @Column(name = "id")
+ @GeneratedValue(strategy = GenerationType.SEQUENCE)
+ private Long id;
+
+}
diff --git a/filmorate-data-jpa/src/main/java/dev/struchkov/filmorate/data/entity/FilmEntity.java b/filmorate-data-jpa/src/main/java/dev/struchkov/filmorate/data/entity/FilmEntity.java
new file mode 100644
index 0000000..1eeb916
--- /dev/null
+++ b/filmorate-data-jpa/src/main/java/dev/struchkov/filmorate/data/entity/FilmEntity.java
@@ -0,0 +1,38 @@
+package dev.struchkov.filmorate.data.entity;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Table;
+import java.time.LocalDateTime;
+
+@Entity
+@Getter
+@Setter
+@Table(name = "film")
+public class FilmEntity extends BaseEntity {
+
+ @Column(name = "name")
+ private String name;
+
+ @Column(name = "description")
+ private String description;
+
+ @Column(name = "date_release")
+ private LocalDateTime dateRelease;
+
+ @Column(name = "duration_in_minutes")
+ private Integer durationInMinutes;
+
+ @Column(name = "date_created")
+ private LocalDateTime dateCreated;
+
+ @Column(name = "date_updated")
+ private LocalDateTime dateUpdated;
+
+ @Column(name = "person_owner_id")
+ private Long ownerId;
+
+}
diff --git a/filmorate-data-jpa/src/main/java/dev/struchkov/filmorate/data/entity/PersonEntity.java b/filmorate-data-jpa/src/main/java/dev/struchkov/filmorate/data/entity/PersonEntity.java
new file mode 100644
index 0000000..73d448c
--- /dev/null
+++ b/filmorate-data-jpa/src/main/java/dev/struchkov/filmorate/data/entity/PersonEntity.java
@@ -0,0 +1,29 @@
+package dev.struchkov.filmorate.data.entity;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Table;
+import java.time.LocalDateTime;
+
+@Entity
+@Getter
+@Setter
+@Table(name = "person")
+public class PersonEntity extends BaseEntity {
+
+ @Column(name = "name")
+ private String name;
+
+ @Column(name = "email")
+ private String email;
+
+ @Column(name = "login")
+ private String login;
+
+ @Column(name = "birthday")
+ private LocalDateTime birthday;
+
+}
diff --git a/filmorate-data-jpa/src/main/java/dev/struchkov/filmorate/data/jpa/FilmJpaRepository.java b/filmorate-data-jpa/src/main/java/dev/struchkov/filmorate/data/jpa/FilmJpaRepository.java
new file mode 100644
index 0000000..dfdbdd6
--- /dev/null
+++ b/filmorate-data-jpa/src/main/java/dev/struchkov/filmorate/data/jpa/FilmJpaRepository.java
@@ -0,0 +1,7 @@
+package dev.struchkov.filmorate.data.jpa;
+
+import dev.struchkov.filmorate.data.entity.FilmEntity;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+public interface FilmJpaRepository extends JpaRepository {
+}
diff --git a/filmorate-data-jpa/src/main/java/dev/struchkov/filmorate/data/jpa/PersonJpaRepository.java b/filmorate-data-jpa/src/main/java/dev/struchkov/filmorate/data/jpa/PersonJpaRepository.java
new file mode 100644
index 0000000..26a399a
--- /dev/null
+++ b/filmorate-data-jpa/src/main/java/dev/struchkov/filmorate/data/jpa/PersonJpaRepository.java
@@ -0,0 +1,7 @@
+package dev.struchkov.filmorate.data.jpa;
+
+import dev.struchkov.filmorate.data.entity.PersonEntity;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+public interface PersonJpaRepository extends JpaRepository {
+}
diff --git a/filmorate-data-jpa/src/main/java/dev/struchkov/filmorate/data/mapper/FilmEntityMapper.java b/filmorate-data-jpa/src/main/java/dev/struchkov/filmorate/data/mapper/FilmEntityMapper.java
new file mode 100644
index 0000000..e5c1a8c
--- /dev/null
+++ b/filmorate-data-jpa/src/main/java/dev/struchkov/filmorate/data/mapper/FilmEntityMapper.java
@@ -0,0 +1,14 @@
+package dev.struchkov.filmorate.data.mapper;
+
+import dev.strichkov.filmorate.domain.Film;
+import dev.struchkov.filmorate.data.entity.FilmEntity;
+import org.mapstruct.Mapper;
+
+@Mapper(componentModel = "spring")
+public interface FilmEntityMapper {
+
+ FilmEntity toEntity(Film film);
+
+ Film toDomain(FilmEntity film);
+
+}
diff --git a/filmorate-data-jpa/src/main/java/dev/struchkov/filmorate/data/mapper/PersonEntityMapper.java b/filmorate-data-jpa/src/main/java/dev/struchkov/filmorate/data/mapper/PersonEntityMapper.java
new file mode 100644
index 0000000..30663d1
--- /dev/null
+++ b/filmorate-data-jpa/src/main/java/dev/struchkov/filmorate/data/mapper/PersonEntityMapper.java
@@ -0,0 +1,14 @@
+package dev.struchkov.filmorate.data.mapper;
+
+import dev.strichkov.filmorate.domain.Person;
+import dev.struchkov.filmorate.data.entity.PersonEntity;
+import org.mapstruct.Mapper;
+
+@Mapper(componentModel = "spring")
+public interface PersonEntityMapper {
+
+ PersonEntity toEntity(Person person);
+
+ Person toDomain(PersonEntity entity);
+
+}
diff --git a/filmorate-data-jpa/src/main/java/dev/struchkov/filmorate/data/repository/FilmRepositoryImpl.java b/filmorate-data-jpa/src/main/java/dev/struchkov/filmorate/data/repository/FilmRepositoryImpl.java
new file mode 100644
index 0000000..835e65b
--- /dev/null
+++ b/filmorate-data-jpa/src/main/java/dev/struchkov/filmorate/data/repository/FilmRepositoryImpl.java
@@ -0,0 +1,56 @@
+package dev.struchkov.filmorate.data.repository;
+
+import dev.strichkov.filmorate.domain.Film;
+import dev.strichkov.filmorate.domain.page.Pagination;
+import dev.strichkov.filmorate.domain.page.Sheet;
+import dev.struchkov.filmorate.context.repository.FilmRepository;
+import dev.struchkov.filmorate.data.jpa.FilmJpaRepository;
+import dev.struchkov.filmorate.data.mapper.FilmEntityMapper;
+import lombok.NonNull;
+import lombok.RequiredArgsConstructor;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.stereotype.Repository;
+
+import java.util.Optional;
+
+@Repository
+@RequiredArgsConstructor
+public class FilmRepositoryImpl implements FilmRepository {
+
+ private final FilmJpaRepository jpaRepository;
+ private final FilmEntityMapper filmMapper;
+
+ @Override
+ public Film save(@NonNull Film film) {
+ return filmMapper.toDomain(
+ jpaRepository.save(filmMapper.toEntity(film))
+ );
+ }
+
+ @Override
+ public Optional findById(@NonNull Long filmId) {
+ return jpaRepository.findById(filmId)
+ .map(filmMapper::toDomain);
+ }
+
+ @Override
+ public Sheet findAll(@NonNull Pagination pagination) {
+ final PageRequest pageable = PageRequest.of(pagination.getPageNumber(), pagination.getPageSize());
+ final Page page = jpaRepository.findAll(pageable)
+ .map(filmMapper::toDomain);
+ return Sheet.builder()
+ .totalElements(page.getTotalElements())
+ .totalPage(page.getTotalPages())
+ .pageNumber(pagination.getPageNumber())
+ .pageSize(pagination.getPageSize())
+ .content(page.getContent())
+ .build();
+ }
+
+ @Override
+ public void deleteById(@NonNull Long filmId) {
+ jpaRepository.deleteById(filmId);
+ }
+
+}
diff --git a/filmorate-data-jpa/src/main/java/dev/struchkov/filmorate/data/repository/PersonRepositoryImpl.java b/filmorate-data-jpa/src/main/java/dev/struchkov/filmorate/data/repository/PersonRepositoryImpl.java
new file mode 100644
index 0000000..ce67dc3
--- /dev/null
+++ b/filmorate-data-jpa/src/main/java/dev/struchkov/filmorate/data/repository/PersonRepositoryImpl.java
@@ -0,0 +1,58 @@
+package dev.struchkov.filmorate.data.repository;
+
+import dev.strichkov.filmorate.domain.Person;
+import dev.strichkov.filmorate.domain.page.Pagination;
+import dev.strichkov.filmorate.domain.page.Sheet;
+import dev.struchkov.filmorate.context.repository.PersonRepository;
+import dev.struchkov.filmorate.data.jpa.PersonJpaRepository;
+import dev.struchkov.filmorate.data.mapper.PersonEntityMapper;
+import lombok.NonNull;
+import lombok.RequiredArgsConstructor;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.stereotype.Repository;
+
+import java.util.Optional;
+
+@Repository
+@RequiredArgsConstructor
+public class PersonRepositoryImpl implements PersonRepository {
+
+ private final PersonJpaRepository jpaRepository;
+ private final PersonEntityMapper personMapper;
+
+ @Override
+ public Person save(@NonNull Person person) {
+ return personMapper.toDomain(
+ jpaRepository.save(
+ personMapper.toEntity(person)
+ )
+ );
+ }
+
+ @Override
+ public Optional findById(@NonNull Long personId) {
+ return jpaRepository.findById(personId)
+ .map(personMapper::toDomain);
+ }
+
+ @Override
+ public void deleteById(@NonNull Long personId) {
+ jpaRepository.deleteById(personId);
+ }
+
+ @Override
+ public Sheet findByAll(@NonNull Pagination pagination) {
+ final PageRequest pageable = PageRequest.of(pagination.getPageNumber(), pagination.getPageSize());
+ final Page page = jpaRepository.findAll(pageable)
+ .map(personMapper::toDomain);
+ return Sheet.builder()
+ .totalPage(page.getTotalPages())
+ .totalElements(page.getTotalElements())
+ .pageSize(page.getSize())
+ .pageNumber(page.getNumber())
+ .content(page.getContent())
+ .build();
+ }
+
+}
diff --git a/filmorate-data-local/pom.xml b/filmorate-data-local/pom.xml
new file mode 100644
index 0000000..fdd00d0
--- /dev/null
+++ b/filmorate-data-local/pom.xml
@@ -0,0 +1,43 @@
+
+
+
+ dev.struchkov.filmorate
+ filmorate
+ 0.0.1-SNAPSHOT
+
+ 4.0.0
+
+ filmorate-data-local
+
+
+
+ org.springframework
+ spring-context
+
+
+
+ dev.struchkov.filmorate
+ filmorate-context
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+ org.apache.maven.plugins
+ maven-source-plugin
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+
+
+
+
+
\ No newline at end of file
diff --git a/filmorate-data-local/src/main/java/dev/struchkov/filmorate/data/repository/FilmLocalRepository.java b/filmorate-data-local/src/main/java/dev/struchkov/filmorate/data/repository/FilmLocalRepository.java
new file mode 100644
index 0000000..cd602ce
--- /dev/null
+++ b/filmorate-data-local/src/main/java/dev/struchkov/filmorate/data/repository/FilmLocalRepository.java
@@ -0,0 +1,42 @@
+package dev.struchkov.filmorate.data.repository;
+
+import dev.strichkov.filmorate.domain.Film;
+import dev.strichkov.filmorate.domain.page.Pagination;
+import dev.strichkov.filmorate.domain.page.Sheet;
+import dev.struchkov.filmorate.context.repository.FilmRepository;
+import lombok.NonNull;
+import org.springframework.stereotype.Repository;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+
+@Repository
+public class FilmLocalRepository implements FilmRepository {
+
+ private final Map storage = new HashMap<>();
+ private long gen = 0;
+
+ @Override
+ public Film save(@NonNull Film film) {
+ film.setId(gen++);
+ storage.put(film.getId(), film);
+ return film;
+ }
+
+ @Override
+ public Optional findById(@NonNull Long filmId) {
+ return Optional.ofNullable(storage.get(filmId));
+ }
+
+ @Override
+ public Sheet findAll(@NonNull Pagination pagination) {
+ throw new UnsupportedOperationException("Не работает :(");
+ }
+
+ @Override
+ public void deleteById(@NonNull Long filmId) {
+ storage.remove(filmId);
+ }
+
+}
diff --git a/filmorate-data-local/src/main/java/dev/struchkov/filmorate/data/repository/PersonLocalRepository.java b/filmorate-data-local/src/main/java/dev/struchkov/filmorate/data/repository/PersonLocalRepository.java
new file mode 100644
index 0000000..fda0592
--- /dev/null
+++ b/filmorate-data-local/src/main/java/dev/struchkov/filmorate/data/repository/PersonLocalRepository.java
@@ -0,0 +1,42 @@
+package dev.struchkov.filmorate.data.repository;
+
+import dev.strichkov.filmorate.domain.Person;
+import dev.strichkov.filmorate.domain.page.Pagination;
+import dev.strichkov.filmorate.domain.page.Sheet;
+import dev.struchkov.filmorate.context.repository.PersonRepository;
+import lombok.NonNull;
+import org.springframework.stereotype.Repository;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+
+@Repository
+public class PersonLocalRepository implements PersonRepository {
+
+ private final Map storage = new HashMap<>();
+ private long gen = 0;
+
+ @Override
+ public Person save(@NonNull Person person) {
+ person.setId(gen++);
+ storage.put(person.getId(), person);
+ return person;
+ }
+
+ @Override
+ public Optional findById(@NonNull Long personId) {
+ return Optional.ofNullable(storage.get(personId));
+ }
+
+ @Override
+ public void deleteById(@NonNull Long personId) {
+ storage.remove(personId);
+ }
+
+ @Override
+ public Sheet findByAll(@NonNull Pagination pagination) {
+ throw new UnsupportedOperationException("Не работает");
+ }
+
+}
diff --git a/filmorate-domain/pom.xml b/filmorate-domain/pom.xml
new file mode 100644
index 0000000..d10c0e9
--- /dev/null
+++ b/filmorate-domain/pom.xml
@@ -0,0 +1,37 @@
+
+
+
+ dev.struchkov.filmorate
+ filmorate
+ 0.0.1-SNAPSHOT
+
+ 4.0.0
+
+ filmorate-domain
+
+
+ 17
+ 17
+ UTF-8
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+ org.apache.maven.plugins
+ maven-source-plugin
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+
+
+
+
+
\ No newline at end of file
diff --git a/filmorate-domain/src/main/java/dev/strichkov/filmorate/domain/Film.java b/filmorate-domain/src/main/java/dev/strichkov/filmorate/domain/Film.java
new file mode 100644
index 0000000..c3de062
--- /dev/null
+++ b/filmorate-domain/src/main/java/dev/strichkov/filmorate/domain/Film.java
@@ -0,0 +1,29 @@
+package dev.strichkov.filmorate.domain;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+
+@Getter
+@Setter
+public class Film {
+
+ private Long id;
+
+ private String name;
+
+ private String description;
+
+ private LocalDate dateRelease;
+
+ private Integer durationInMinutes;
+
+ private LocalDateTime dateCreated;
+
+ private LocalDateTime dateUpdated;
+
+ private Long ownerId;
+
+}
diff --git a/filmorate-domain/src/main/java/dev/strichkov/filmorate/domain/Person.java b/filmorate-domain/src/main/java/dev/strichkov/filmorate/domain/Person.java
new file mode 100644
index 0000000..f7fec23
--- /dev/null
+++ b/filmorate-domain/src/main/java/dev/strichkov/filmorate/domain/Person.java
@@ -0,0 +1,22 @@
+package dev.strichkov.filmorate.domain;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import java.time.LocalDateTime;
+
+@Getter
+@Setter
+public class Person {
+
+ private Long id;
+
+ private String name;
+
+ private String email;
+
+ private String login;
+
+ private LocalDateTime birthday;
+
+}
diff --git a/filmorate-domain/src/main/java/dev/strichkov/filmorate/domain/page/Pagination.java b/filmorate-domain/src/main/java/dev/strichkov/filmorate/domain/page/Pagination.java
new file mode 100644
index 0000000..10a4371
--- /dev/null
+++ b/filmorate-domain/src/main/java/dev/strichkov/filmorate/domain/page/Pagination.java
@@ -0,0 +1,13 @@
+package dev.strichkov.filmorate.domain.page;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+@AllArgsConstructor
+@Getter
+public class Pagination {
+
+ private Integer pageNumber;
+ private Integer pageSize;
+
+}
diff --git a/filmorate-domain/src/main/java/dev/strichkov/filmorate/domain/page/Sheet.java b/filmorate-domain/src/main/java/dev/strichkov/filmorate/domain/page/Sheet.java
new file mode 100644
index 0000000..20a21c7
--- /dev/null
+++ b/filmorate-domain/src/main/java/dev/strichkov/filmorate/domain/page/Sheet.java
@@ -0,0 +1,29 @@
+package dev.strichkov.filmorate.domain.page;
+
+import lombok.Builder;
+import lombok.Getter;
+
+import java.util.List;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+@Getter
+@Builder
+public class Sheet {
+
+ private List content;
+ private Long totalElements;
+ private Integer totalPage;
+ private Integer pageSize;
+ private Integer pageNumber;
+
+ public Sheet map(Function mapping) {
+ return Sheet.builder()
+ .totalElements(totalElements)
+ .totalPage(totalPage)
+ .pageNumber(pageNumber)
+ .pageSize(pageSize)
+ .content(content.stream().map(mapping).collect(Collectors.toList()) )
+ .build();
+ }
+}
diff --git a/filmorate-web/pom.xml b/filmorate-web/pom.xml
new file mode 100644
index 0000000..aa51828
--- /dev/null
+++ b/filmorate-web/pom.xml
@@ -0,0 +1,63 @@
+
+
+
+ dev.struchkov.filmorate
+ filmorate
+ 0.0.1-SNAPSHOT
+
+ 4.0.0
+
+ filmorate-web
+
+
+ 17
+ 17
+ UTF-8
+
+
+
+
+ dev.struchkov.filmorate
+ filmorate-context
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.mapstruct
+ mapstruct
+
+
+ io.swagger.core.v3
+ swagger-annotations
+ 2.2.4
+
+
+ org.springdoc
+ springdoc-openapi-ui
+ 1.6.12
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+ org.apache.maven.plugins
+ maven-source-plugin
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+
+
+
+
+
\ No newline at end of file
diff --git a/filmorate-web/src/main/java/dev/struchkov/filmorate/web/controller/FilmController.java b/filmorate-web/src/main/java/dev/struchkov/filmorate/web/controller/FilmController.java
new file mode 100644
index 0000000..9631294
--- /dev/null
+++ b/filmorate-web/src/main/java/dev/struchkov/filmorate/web/controller/FilmController.java
@@ -0,0 +1,82 @@
+package dev.struchkov.filmorate.web.controller;
+
+import dev.strichkov.filmorate.domain.Film;
+import dev.strichkov.filmorate.domain.page.Pagination;
+import dev.strichkov.filmorate.domain.page.Sheet;
+import dev.struchkov.filmorate.context.service.FilmService;
+import dev.struchkov.filmorate.web.dto.FilmDto;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.RequiredArgsConstructor;
+import org.springframework.core.convert.ConversionService;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.validation.constraints.Max;
+import javax.validation.constraints.Min;
+
+import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
+
+@RestController
+@RequiredArgsConstructor
+@RequestMapping("api/film")
+@Tag(name = "Фильмы", description = "Эндпойнты для работы с фильмами")
+public class FilmController {
+
+ private final FilmService filmService;
+ private final ConversionService conversionService;
+
+ @Operation(summary = "Добавление нового фильма")
+ @PostMapping(produces = APPLICATION_JSON_VALUE, consumes = APPLICATION_JSON_VALUE)
+ public ResponseEntity create(@RequestBody FilmDto filmDto) {
+ return ResponseEntity.ok(
+ conversionService.convert(
+ filmService.create(conversionService.convert(filmDto, Film.class)),
+ FilmDto.class
+ )
+ );
+ }
+
+ @Operation(summary = "Обновление фильма")
+ @PutMapping(produces = APPLICATION_JSON_VALUE, consumes = APPLICATION_JSON_VALUE)
+ public ResponseEntity update(@RequestBody FilmDto filmDto) {
+ return ResponseEntity.ok(
+ conversionService.convert(
+ filmService.update(conversionService.convert(filmDto, Film.class)),
+ FilmDto.class
+ )
+ );
+ }
+
+ @Operation(summary = "Получение фильма по идентификатору")
+ @PutMapping(value = "{filmId}", produces = APPLICATION_JSON_VALUE)
+ public ResponseEntity getById(@PathVariable Long filmId) {
+ return ResponseEntity.ok(
+ conversionService.convert(
+ filmService.getByIdOrThrow(filmId),
+ FilmDto.class
+ )
+ );
+ }
+
+ @Operation(summary = "Получение всех фильмов с пагинацией")
+ @GetMapping(produces = APPLICATION_JSON_VALUE)
+ public ResponseEntity> getAllByFilter(
+ @Parameter(description = "Номер страницы", required = true) @Min(0) @RequestParam Integer pageNum,
+ @Parameter(description = "Количество элементов", required = true) @Min(1) @Max(100) @RequestParam Integer pageSize
+ ) {
+ return ResponseEntity.ok(
+ filmService.getAll(new Pagination(pageSize, pageSize))
+ .map(film -> conversionService.convert(film, FilmDto.class))
+ );
+ }
+
+}
diff --git a/filmorate-web/src/main/java/dev/struchkov/filmorate/web/controller/PersonController.java b/filmorate-web/src/main/java/dev/struchkov/filmorate/web/controller/PersonController.java
new file mode 100644
index 0000000..5388cd8
--- /dev/null
+++ b/filmorate-web/src/main/java/dev/struchkov/filmorate/web/controller/PersonController.java
@@ -0,0 +1,82 @@
+package dev.struchkov.filmorate.web.controller;
+
+import dev.strichkov.filmorate.domain.page.Pagination;
+import dev.strichkov.filmorate.domain.page.Sheet;
+import dev.struchkov.filmorate.context.service.PersonService;
+import dev.struchkov.filmorate.web.dto.PersonDto;
+import dev.struchkov.filmorate.web.mapper.PersonMapper;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.RequiredArgsConstructor;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.validation.constraints.Max;
+import javax.validation.constraints.Min;
+
+import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
+
+@RestController
+@RequiredArgsConstructor
+@RequestMapping("api/person")
+@Tag(name = "Пользователи системы", description = "Эндпойнты для работы с пользователями системы")
+public class PersonController {
+
+ private final PersonService personService;
+ private final PersonMapper personMapper;
+
+ @Operation(summary = "Регистрация нового пользователя")
+ @PostMapping(produces = APPLICATION_JSON_VALUE, consumes = APPLICATION_JSON_VALUE)
+ public ResponseEntity create(@RequestBody PersonDto personDto) {
+ return ResponseEntity.ok(
+ personMapper.toDto(
+ personService.create(
+ personMapper.toDomain(personDto)
+ )
+ )
+ );
+ }
+
+ @Operation(summary = "Обновление пользователя")
+ @PutMapping(produces = APPLICATION_JSON_VALUE, consumes = APPLICATION_JSON_VALUE)
+ public ResponseEntity update(@RequestBody PersonDto personDto) {
+ return ResponseEntity.ok(
+ personMapper.toDto(
+ personService.update(
+ personMapper.toDomain(personDto)
+ )
+ )
+ );
+ }
+
+ @Operation(summary = "Получение пользователя по идентификатору")
+ @PutMapping(value = "{personId}", produces = APPLICATION_JSON_VALUE)
+ public ResponseEntity getById(@PathVariable Long personId) {
+ return ResponseEntity.ok(
+ personMapper.toDto(
+ personService.getByIdOrThrow(personId)
+ )
+ );
+ }
+
+ @Operation(summary = "Получение всех пользователей с пагинацией")
+ @GetMapping(produces = APPLICATION_JSON_VALUE)
+ public ResponseEntity> getAllByFilter(
+ @Parameter(description = "Номер страницы", required = true) @Min(0) @RequestParam Integer pageNum,
+ @Parameter(description = "Количество элементов", required = true) @Min(1) @Max(100) @RequestParam Integer pageSize
+ ) {
+ return ResponseEntity.ok(
+ personService.getAll(new Pagination(pageNum, pageSize))
+ .map(personMapper::toDto)
+ );
+ }
+
+}
diff --git a/filmorate-web/src/main/java/dev/struchkov/filmorate/web/converter/FilmToDtoConvert.java b/filmorate-web/src/main/java/dev/struchkov/filmorate/web/converter/FilmToDtoConvert.java
new file mode 100644
index 0000000..63912ae
--- /dev/null
+++ b/filmorate-web/src/main/java/dev/struchkov/filmorate/web/converter/FilmToDtoConvert.java
@@ -0,0 +1,21 @@
+package dev.struchkov.filmorate.web.converter;
+
+import dev.strichkov.filmorate.domain.Film;
+import dev.struchkov.filmorate.web.dto.FilmDto;
+import org.springframework.core.convert.converter.Converter;
+import org.springframework.stereotype.Component;
+
+@Component
+public class FilmToDtoConvert implements Converter {
+
+ @Override
+ public FilmDto convert(Film source) {
+ final FilmDto filmDto = new FilmDto();
+ filmDto.setId(source.getId());
+ filmDto.setName(source.getName());
+ filmDto.setDurationInMinutes(source.getDurationInMinutes());
+ filmDto.setDateRelease(source.getDateRelease());
+ filmDto.setDescription(source.getDescription());
+ return filmDto;
+ }
+}
diff --git a/filmorate-web/src/main/java/dev/struchkov/filmorate/web/converter/FilmToEntityConvert.java b/filmorate-web/src/main/java/dev/struchkov/filmorate/web/converter/FilmToEntityConvert.java
new file mode 100644
index 0000000..869c61b
--- /dev/null
+++ b/filmorate-web/src/main/java/dev/struchkov/filmorate/web/converter/FilmToEntityConvert.java
@@ -0,0 +1,22 @@
+package dev.struchkov.filmorate.web.converter;
+
+import dev.strichkov.filmorate.domain.Film;
+import dev.struchkov.filmorate.web.dto.FilmDto;
+import org.springframework.core.convert.converter.Converter;
+import org.springframework.stereotype.Component;
+
+@Component
+public class FilmToEntityConvert implements Converter {
+
+ @Override
+ public Film convert(FilmDto source) {
+ final Film film = new Film();
+ film.setId(source.getId());
+ film.setName(source.getName());
+ film.setDescription(source.getDescription());
+ film.setDateRelease(source.getDateRelease());
+ film.setDurationInMinutes(source.getDurationInMinutes());
+ return film;
+ }
+
+}
diff --git a/filmorate-web/src/main/java/dev/struchkov/filmorate/web/dto/FilmDto.java b/filmorate-web/src/main/java/dev/struchkov/filmorate/web/dto/FilmDto.java
new file mode 100644
index 0000000..023286d
--- /dev/null
+++ b/filmorate-web/src/main/java/dev/struchkov/filmorate/web/dto/FilmDto.java
@@ -0,0 +1,39 @@
+package dev.struchkov.filmorate.web.dto;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+
+@Getter
+@Setter
+@Schema(description = "Фильм")
+public class FilmDto {
+
+ @Schema(name = "Идентификатор")
+ private Long id;
+
+ @Schema(name = "Название")
+ private String name;
+
+ @Schema(name = "Описание")
+ private String description;
+
+ @Schema(name = "Дата релиза")
+ private LocalDate dateRelease;
+
+ @Schema(name = "Продолжительность")
+ private Integer durationInMinutes;
+
+ @Schema(name = "Дата Создания")
+ private LocalDateTime dateCreated;
+
+ @Schema(name = "Дата обновления")
+ private LocalDateTime dateUpdated;
+
+ @Schema(name = "Владелец фильма")
+ private Long ownerId;
+
+}
diff --git a/filmorate-web/src/main/java/dev/struchkov/filmorate/web/dto/PersonDto.java b/filmorate-web/src/main/java/dev/struchkov/filmorate/web/dto/PersonDto.java
new file mode 100644
index 0000000..f9d09de
--- /dev/null
+++ b/filmorate-web/src/main/java/dev/struchkov/filmorate/web/dto/PersonDto.java
@@ -0,0 +1,29 @@
+package dev.struchkov.filmorate.web.dto;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.time.LocalDateTime;
+
+@Getter
+@Setter
+@Schema(description = "Пользователь системы")
+public class PersonDto {
+
+ @Schema(description = "Идентификатор")
+ private Long id;
+
+ @Schema(description = "Имя")
+ private String name;
+
+ @Schema(description = "Почта")
+ private String email;
+
+ @Schema(description = "Логин")
+ private String login;
+
+ @Schema(description = "День рождения")
+ private LocalDateTime birthday;
+
+}
diff --git a/filmorate-web/src/main/java/dev/struchkov/filmorate/web/mapper/PersonMapper.java b/filmorate-web/src/main/java/dev/struchkov/filmorate/web/mapper/PersonMapper.java
new file mode 100644
index 0000000..c4d9425
--- /dev/null
+++ b/filmorate-web/src/main/java/dev/struchkov/filmorate/web/mapper/PersonMapper.java
@@ -0,0 +1,14 @@
+package dev.struchkov.filmorate.web.mapper;
+
+import dev.strichkov.filmorate.domain.Person;
+import dev.struchkov.filmorate.web.dto.PersonDto;
+import org.mapstruct.Mapper;
+
+@Mapper(componentModel = "spring")
+public interface PersonMapper {
+
+ PersonDto toDto(Person person);
+
+ Person toDomain(PersonDto personDto);
+
+}
diff --git a/pom.xml b/pom.xml
index 6944b36..599c60e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -2,77 +2,126 @@
4.0.0
-
- org.springframework.boot
- spring-boot-starter-parent
- 2.7.5
-
-
- dev.struchkov.example
+
+ dev.struchkov.filmorate
filmorate
0.0.1-SNAPSHOT
- filmorate
- filmorate
+ pom
+
+
+ filmorate-domain
+ filmorate-context
+ filmorate-core
+ filmorate-data-jpa
+ filmorate-data-local
+ filmorate-web
+
+
17
-
-
-
- org.springframework.boot
- spring-boot-starter-data-jpa
-
-
- org.springframework.boot
- spring-boot-starter-web
-
-
- org.liquibase
- liquibase-core
-
+ ${java.version}
+ ${java.version}
+ UTF-8
+ UTF-8
-
- org.springframework.boot
- spring-boot-devtools
- runtime
- true
-
-
- org.postgresql
- postgresql
- runtime
-
-
- org.springframework.boot
- spring-boot-configuration-processor
- true
-
+ 3.10.1
+ 3.2.1
+ 3.4.1
+
+
+
+
+
+ dev.struchkov.filmorate
+ filmorate-bom
+ 0.0.1-SNAPSHOT
+ pom
+ import
+
+
+
+
+
org.projectlombok
lombok
true
- org.springframework.boot
- spring-boot-starter-test
- test
+ org.mapstruct
+ mapstruct
+
+
+ org.mapstruct
+ mapstruct-processor
-
-
- org.springframework.boot
- spring-boot-maven-plugin
-
-
-
- org.projectlombok
- lombok
-
-
-
-
-
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ ${plugin.maven.compiler.version}
+
+
+ ${maven.compiler.target}
+
+
+ org.projectlombok
+ lombok
+ 1.18.24
+
+
+ org.mapstruct
+ mapstruct-processor
+ 1.5.2.Final
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-source-plugin
+ ${plugin.maven.source.version}
+
+
+ attach-sources
+
+ jar
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+ ${plugin.maven.javadoc.version}
+
+
+ attach-javadocs
+
+ jar
+
+
+
+
+
+
+
+
+
+ uPagge
+ Struchkov Mark
+ mark@struchkov.dev
+ https://mark.struchkov.dev
+
+
+
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
deleted file mode 100644
index 8b13789..0000000
--- a/src/main/resources/application.properties
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/src/test/java/dev/struchkov/filmorate/FilmorateApplicationTests.java b/src/test/java/dev/struchkov/filmorate/FilmorateApplicationTests.java
deleted file mode 100644
index 6f8b2b2..0000000
--- a/src/test/java/dev/struchkov/filmorate/FilmorateApplicationTests.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package dev.struchkov.filmorate;
-
-import org.junit.jupiter.api.Test;
-import org.springframework.boot.test.context.SpringBootTest;
-
-@SpringBootTest
-class FilmorateApplicationTests {
-
- @Test
- void contextLoads() {
- }
-
-}