{"id":347494,"date":"2023-05-15T21:00:43","date_gmt":"2023-05-15T21:00:43","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=347494"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=347494","title":{"rendered":"<span>\u041f\u0438\u0448\u0435\u043c \u043f\u0440\u043e\u0441\u0442\u043e\u0439 docker-compose.yml \u0434\u043b\u044f \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f (React, Spring Boot, PostgreSQL, pgAdmin)<\/span>"},"content":{"rendered":"<div><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<p>\u0412\u0441\u0435\u043c \u043f\u0440\u0438\u0432\u0435\u0442!<\/p>\n<p>\u0412 \u0434\u0430\u043d\u043d\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u043f\u043e\u043f\u0440\u043e\u0431\u0443\u044e \u0440\u0430\u0441\u0441\u043a\u0430\u0437\u0430\u0442\u044c \u043a\u0430\u043a \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c docker-compose.yml \u0434\u043b\u044f \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u043f\u0440\u043e\u0441\u0442\u043e\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u0431\u0443\u0434\u0435\u0442 \u0441\u043e\u0441\u0442\u043e\u044f\u0442\u044c \u0438\u0437 \u0444\u0440\u043e\u043d\u0442\u0430 \u043d\u0430 React, \u0431\u044d\u043a\u0435\u043d\u0434\u0430 \u043d\u0430 Spring Boot, \u0442\u0430\u043a\u0436\u0435 \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0431\u0430\u0437\u0443 \u0434\u0430\u043d\u043d\u044b\u0445 PostgreSQL, \u0430 \u0434\u043b\u044f \u043f\u0440\u043e\u0441\u043c\u043e\u0442\u0440\u0430 \u0434\u0430\u043d\u043d\u044b\u0445 \u0432 \u0431\u0430\u0437\u0435 pgAdmin. <\/p>\n<p>\u0414\u0430\u043d\u043d\u0430\u044f \u0441\u0442\u0430\u0442\u044c\u044f \u0431\u0443\u0434\u0435\u0442 \u043f\u043e\u043b\u0435\u0437\u043d\u0430 \u043d\u0430\u0447\u0438\u043d\u0430\u044e\u0449\u0438\u043c \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u043e\u043c \u0440\u0430\u0437\u043e\u0431\u0440\u0430\u0442\u044c\u0441\u044f \u0434\u043b\u044f \u0447\u0435\u0433\u043e \u043d\u0443\u0436\u0435\u043d docker-compose.yml \u0444\u0430\u0439\u043b \u0438 \u043a\u0430\u043a \u043d\u0430\u0447\u0430\u0442\u044c \u0441 \u043d\u0438\u043c \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c.<\/p>\n<p>\u0414\u043b\u044f \u043f\u043e\u043d\u0438\u043c\u0430\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0438 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b \u043c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u044b\u0435 \u0437\u043d\u0430\u043d\u0438\u044f \u043f\u043e \u0434\u043e\u043a\u0435\u0440\u0443, \u0430 \u0442\u0430\u043a\u0436\u0435 \u0434\u043b\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u043f\u0440\u0438\u043c\u0435\u0440\u043e\u0432 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u043d\u044b\u0439 Docker \u0443 \u0432\u0430\u0441 \u043d\u0430 \u043a\u043e\u043c\u043f\u044c\u044e\u0442\u0435\u0440\u0435.<\/p>\n<p>\u041f\u043b\u0430\u043d, \u0447\u0442\u043e \u043c\u044b \u0445\u043e\u0442\u0438\u043c \u0441\u0434\u0435\u043b\u0430\u0442\u044c:<\/p>\n<ol>\n<li>\n<p>\u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u0431\u044d\u043a\u0435\u043d\u0434 \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c Spring Boot (\u044d\u0442\u043e \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u043e\u0441\u0442\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u043f\u043e \u0440\u0430\u0431\u043e\u0442\u0435 \u0441 \u043a\u043d\u0438\u0433\u0430\u043c\u0438, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c CRUD \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438 \u043d\u0430\u0434 \u043d\u0438\u043c\u0438);<\/p>\n<\/li>\n<li>\n<p>\u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u0431\u0430\u0437\u0443 \u0434\u0430\u043d\u043d\u044b\u0445 PostgreSQL \u0434\u043b\u044f \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u044b\u0445;<\/p>\n<\/li>\n<li>\n<p>\u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c pgAdmin \u0434\u043b\u044f \u043f\u0440\u043e\u0441\u043c\u043e\u0442\u0440\u0430 \u0434\u0430\u043d\u043d\u044b\u0445 \u0432 \u043d\u0430\u0448\u0435\u0439 \u0431\u0430\u0437\u0435 PostgreSQL;<\/p>\n<\/li>\n<li>\n<p>\u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u043f\u0440\u043e\u0441\u0442\u043e\u0439 \u0444\u0440\u043e\u043d\u0442 \u043d\u0430 React \u0434\u043b\u044f \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f, \u0432\u0432\u043e\u0434\u0430, \u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0438      \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f \u043a\u043d\u0438\u0433;<\/p>\n<\/li>\n<li>\n<p>\u043f\u043e\u043c\u0435\u0441\u0442\u0438\u0442\u044c \u0432\u0441\u0435 \u044d\u0442\u043e \u0432 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u044b \u0438 &#171;\u0437\u0430\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c&#187;, \u0434\u043b\u044f \u0447\u0435\u0433\u043e \u0438 \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c docker-compose.yml \u0444\u0430\u0439\u043b.<\/p>\n<\/li>\n<\/ol>\n<p>\u041d\u0430\u0447\u043d\u0435\u043c \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u0441 \u0442\u0435\u043e\u0440\u0438\u0438. Docker-compose \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0434\u043b\u044f \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u043c\u0438 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u0430\u043c\u0438, \u0432\u0445\u043e\u0434\u044f\u0449\u0438\u043c\u0438 \u0432 \u0441\u043e\u0441\u0442\u0430\u0432 \u043e\u0434\u043d\u043e\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f, \u044d\u0442\u043e \u043c\u043e\u0433\u0443\u0442 \u0431\u044b\u0442\u044c \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u044b \u0441\u043e\u0437\u0434\u0430\u043d\u043d\u044b\u0435 \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 \u043e\u0431\u0440\u0430\u0437\u043e\u0432 \u043d\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f (\u0443 \u043d\u0430\u0441 \u044d\u0442\u043e \u0431\u0443\u0434\u0443\u0442 \u0434\u0432\u0430 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u044b: \u043e\u0434\u0438\u043d \u0441 \u0431\u044d\u043a\u0435\u043d\u0434\u043e\u043c, \u0430 \u0432\u0442\u043e\u0440\u043e\u0439 \u0441 \u0444\u0440\u043e\u043d\u0442\u0435\u043d\u0434\u043e\u043c), \u0430 \u0442\u0430\u043a\u0436\u0435 \u0441\u0442\u043e\u0440\u043e\u043d\u043d\u0438\u0435 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u044b \u0441\u043a\u0430\u0447\u0430\u043d\u043d\u044b\u0435, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0441 docker hub (\u0443 \u043d\u0430\u0441 \u044d\u0442\u043e \u0431\u0443\u0434\u0435\u0442 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u044b \u0441 \u0431\u0430\u0437\u043e\u0439 \u0434\u0430\u043d\u043d\u044b\u0445 \u0438 \u0433\u0440\u0430\u0444\u0438\u0447\u0435\u0441\u043a\u0438\u043c \u043a\u043b\u0438\u0435\u043d\u0442\u043e\u043c \u043a \u043d\u0435\u0439).<\/p>\n<p>\u0418 \u043a\u043e\u043d\u0435\u0447\u043d\u043e \u0436\u0435 \u043b\u043e\u0433\u0438\u0447\u043d\u044b\u0439 \u0432\u043e\u043f\u0440\u043e\u0441: \u0434\u043b\u044f \u0447\u0435\u0433\u043e \u0432\u0441\u0435 \u044d\u0442\u043e? \u0437\u0430\u0447\u0435\u043c?<\/p>\n<p>\u041e\u0442\u0432\u0435\u0442 \u043e\u0447\u0435\u043d\u044c \u043f\u0440\u043e\u0441\u0442, Docker compose \u043d\u0443\u0436\u0435\u043d \u0434\u043b\u044f \u0431\u044b\u0441\u0442\u0440\u043e\u0433\u043e \u0440\u0430\u0437\u0432\u0435\u0440\u0442\u044b\u0432\u0430\u043d\u0438\u044f \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043f\u0435\u0440\u0435\u043d\u043e\u0441 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043d\u0430 \u0434\u0440\u0443\u0433\u043e\u0439 \u0441\u0435\u0440\u0432\u0435\u0440 \u0437\u0430\u0439\u043c\u0435\u0442 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043c\u0438\u043d\u0443\u0442, \u0442\u0430\u043a\u0436\u0435 \u0432 \u0441\u043e\u0447\u0435\u0442\u0430\u043d\u0438\u0438 \u0441 kubernetes &#8212; \u0434\u0430\u0435\u0442 \u043f\u0440\u0435\u0432\u043e\u0441\u0445\u043e\u0434\u043d\u044b\u0435 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u044b \u043f\u043e \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0430\u0446\u0438\u0438 \u0440\u0430\u0437\u0432\u0435\u0440\u0442\u044b\u0432\u0430\u043d\u0438\u044f, \u043c\u0430\u0441\u0448\u0442\u0430\u0431\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0438 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0446\u0438\u0438 \u0440\u0430\u0431\u043e\u0442\u044b \u043d\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0432 \u0443\u0441\u043b\u043e\u0432\u0438\u044f\u0445 \u043a\u043b\u0430\u0441\u0442\u0435\u0440\u0430.<\/p>\n<p>\u0418\u0442\u0430\u043a \u043f\u0440\u0438\u0441\u0442\u0443\u043f\u0438\u043c. \u0412\u043d\u0430\u0447\u0430\u043b\u0435 \u043d\u0430\u043f\u0438\u0448\u0435\u043c \u043d\u0430\u0448 \u0431\u044d\u043a\u0435\u043d\u0434, \u0434\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0438\u0434\u0435\u0442 \u043d\u0430 <a href=\"https:\/\/start.spring.io\/\" rel=\"noopener noreferrer nofollow\">https:\/\/start.spring.io\/<\/a> \u0438 \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u043f\u0440\u043e\u0435\u043a\u0442.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/e7c\/8cd\/2b7\/e7c8cd2b777da0041b4dc7cb74e6db98.png\" width=\"1557\" height=\"943\"><\/figure>\n<p>\u041f\u043e\u0434\u0440\u043e\u0431\u043d\u043e \u043d\u0435 \u0431\u0443\u0434\u0443 \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0442\u044c \u043a\u0430\u0436\u0434\u044b\u0439 \u0448\u0430\u0433 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438, \u0431\u0443\u0434\u0443 \u043e\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0442\u044c\u0441\u044f \u0442\u043e\u043b\u044c\u043a\u043e \u043d\u0430 \u0441\u0430\u043c\u044b\u0445 \u0433\u043b\u0430\u0432\u043d\u044b\u0445 \u043c\u043e\u043c\u0435\u043d\u0442\u0430\u0445, \u0432 \u043b\u044e\u0431\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435, \u0432\u0435\u0441\u044c \u043a\u043e\u0434 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u0431\u0443\u0434\u0435\u0442 \u0434\u043e\u0441\u0442\u0443\u043f\u0435\u043d \u043d\u0430 \u0433\u0438\u0442\u0445\u0430\u0431\u0435, \u0441\u0441\u044b\u043b\u043a\u0430 \u0432 \u043a\u043e\u043d\u0446\u0435 \u0441\u0442\u0430\u0442\u044c\u0438.<\/p>\n<p>\u041e\u0442\u043a\u0440\u044b\u0432\u0430\u0435\u043c \u043d\u0430\u0448 \u043f\u0440\u043e\u0435\u043a\u0442 \u0432 \u0441\u0440\u0435\u0434\u0435 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0438 \u0432\u043d\u0430\u0447\u0430\u043b\u0435 \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u043c\u043e\u0434\u0438\u0444\u0438\u0446\u0438\u0440\u0443\u0435\u043c \u043d\u0430\u0448 pom.xml \u0444\u0430\u0439\u043b.<\/p>\n<pre><code class=\"java\">&lt;?xml version=\"1.0\" encoding=\"UTF-8\"?&gt; &lt;project xmlns=\"http:\/\/maven.apache.org\/POM\/4.0.0\" xmlns:xsi=\"http:\/\/www.w3.org\/2001\/XMLSchema-instance\" xsi:schemaLocation=\"http:\/\/maven.apache.org\/POM\/4.0.0 https:\/\/maven.apache.org\/xsd\/maven-4.0.0.xsd\"&gt; &lt;modelVersion&gt;4.0.0&lt;\/modelVersion&gt; &lt;parent&gt; &lt;groupId&gt;org.springframework.boot&lt;\/groupId&gt; &lt;artifactId&gt;spring-boot-starter-parent&lt;\/artifactId&gt; &lt;version&gt;2.7.11&lt;\/version&gt; &lt;relativePath\/&gt; &lt;!-- lookup parent from repository --&gt; &lt;\/parent&gt; &lt;groupId&gt;com.example&lt;\/groupId&gt; &lt;artifactId&gt;my-library-app&lt;\/artifactId&gt; &lt;version&gt;0.0.1-SNAPSHOT&lt;\/version&gt; &lt;name&gt;my-library-app&lt;\/name&gt; &lt;description&gt;Demo project for Spring Boot&lt;\/description&gt; &lt;properties&gt; &lt;java.version&gt;17&lt;\/java.version&gt; &lt;org.mapstruct.version&gt;1.4.2.Final&lt;\/org.mapstruct.version&gt; &lt;lombok-mapstruct-binding.version&gt;0.2.0&lt;\/lombok-mapstruct-binding.version&gt; &lt;maven.compiler.plugin.version&gt;3.5.1&lt;\/maven.compiler.plugin.version&gt; &lt;\/properties&gt; &lt;dependencies&gt; &lt;!--JPA--&gt; &lt;dependency&gt; &lt;groupId&gt;org.springframework.boot&lt;\/groupId&gt; &lt;artifactId&gt;spring-boot-starter-data-jpa&lt;\/artifactId&gt; &lt;\/dependency&gt; &lt;!--WEB--&gt; &lt;dependency&gt; &lt;groupId&gt;org.springframework.boot&lt;\/groupId&gt; &lt;artifactId&gt;spring-boot-starter-web&lt;\/artifactId&gt; &lt;\/dependency&gt; &lt;!--LIQUIBASE--&gt; &lt;dependency&gt; &lt;groupId&gt;org.liquibase&lt;\/groupId&gt; &lt;artifactId&gt;liquibase-core&lt;\/artifactId&gt; &lt;\/dependency&gt; &lt;!--DEV-TOOLS--&gt; &lt;dependency&gt; &lt;groupId&gt;org.springframework.boot&lt;\/groupId&gt; &lt;artifactId&gt;spring-boot-devtools&lt;\/artifactId&gt; &lt;scope&gt;runtime&lt;\/scope&gt; &lt;optional&gt;true&lt;\/optional&gt; &lt;\/dependency&gt; &lt;!--DATABASE--&gt; &lt;dependency&gt; &lt;groupId&gt;org.postgresql&lt;\/groupId&gt; &lt;artifactId&gt;postgresql&lt;\/artifactId&gt; &lt;scope&gt;runtime&lt;\/scope&gt; &lt;\/dependency&gt; &lt;!--LOMBOK--&gt; &lt;dependency&gt; &lt;groupId&gt;org.projectlombok&lt;\/groupId&gt; &lt;artifactId&gt;lombok&lt;\/artifactId&gt; &lt;optional&gt;true&lt;\/optional&gt; &lt;\/dependency&gt; &lt;!--MAPPING--&gt; &lt;dependency&gt; &lt;groupId&gt;org.mapstruct&lt;\/groupId&gt; &lt;artifactId&gt;mapstruct&lt;\/artifactId&gt; &lt;version&gt;${org.mapstruct.version}&lt;\/version&gt; &lt;\/dependency&gt; &lt;dependency&gt; &lt;groupId&gt;org.projectlombok&lt;\/groupId&gt; &lt;artifactId&gt;lombok-mapstruct-binding&lt;\/artifactId&gt; &lt;version&gt;${lombok-mapstruct-binding.version}&lt;\/version&gt; &lt;\/dependency&gt; &lt;!--TESTING--&gt; &lt;dependency&gt; &lt;groupId&gt;org.springframework.boot&lt;\/groupId&gt; &lt;artifactId&gt;spring-boot-starter-test&lt;\/artifactId&gt; &lt;scope&gt;test&lt;\/scope&gt; &lt;\/dependency&gt; &lt;\/dependencies&gt;  &lt;build&gt; &lt;plugins&gt; &lt;plugin&gt; &lt;groupId&gt;org.springframework.boot&lt;\/groupId&gt; &lt;artifactId&gt;spring-boot-maven-plugin&lt;\/artifactId&gt; &lt;configuration&gt; &lt;excludes&gt; &lt;exclude&gt; &lt;groupId&gt;org.projectlombok&lt;\/groupId&gt; &lt;artifactId&gt;lombok&lt;\/artifactId&gt; &lt;\/exclude&gt; &lt;\/excludes&gt; &lt;\/configuration&gt; &lt;\/plugin&gt; &lt;plugin&gt; &lt;groupId&gt;org.apache.maven.plugins&lt;\/groupId&gt; &lt;artifactId&gt;maven-compiler-plugin&lt;\/artifactId&gt; &lt;version&gt;${maven.compiler.plugin.version}&lt;\/version&gt; &lt;configuration&gt; &lt;source&gt;17&lt;\/source&gt; &lt;target&gt;17&lt;\/target&gt; &lt;annotationProcessorPaths&gt; &lt;path&gt; &lt;groupId&gt;org.mapstruct&lt;\/groupId&gt; &lt;artifactId&gt;mapstruct-processor&lt;\/artifactId&gt; &lt;version&gt;${org.mapstruct.version}&lt;\/version&gt; &lt;\/path&gt; &lt;path&gt; &lt;groupId&gt;org.projectlombok&lt;\/groupId&gt; &lt;artifactId&gt;lombok&lt;\/artifactId&gt; &lt;version&gt;${lombok.version}&lt;\/version&gt; &lt;\/path&gt; &lt;path&gt; &lt;groupId&gt;org.projectlombok&lt;\/groupId&gt; &lt;artifactId&gt;lombok-mapstruct-binding&lt;\/artifactId&gt; &lt;version&gt;${lombok-mapstruct-binding.version}&lt;\/version&gt; &lt;\/path&gt; &lt;\/annotationProcessorPaths&gt; &lt;\/configuration&gt; &lt;\/plugin&gt; &lt;\/plugins&gt; &lt;\/build&gt;  &lt;\/project&gt;<\/code><\/pre>\n<p>\u0418\u0437 \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u0445 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439:<\/p>\n<p>postgresql &#8212; \u0434\u043e\u0441\u0442\u0443\u043f \u043a \u0431\u0430\u0437\u0435 \u0434\u0430\u043d\u043d\u044b\u0445;<\/p>\n<p>liquibase &#8212; \u0431\u0443\u0434\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0434\u043b\u044f &#171;\u043d\u0430\u043a\u0430\u0442\u044b\u0432\u0430\u043d\u0438\u044f&#187; \u0442\u0430\u0431\u043b\u0438\u0446 \u0432 \u0431\u0430\u0437\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 \u0438 \u0437\u0430\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u043f\u0435\u0440\u0432\u043e\u043d\u0430\u0447\u0430\u043b\u044c\u043d\u044b\u043c\u0438 \u0434\u0430\u043d\u043d\u044b\u043c\u0438.<\/p>\n<p>mapstruct &#8212; \u0434\u043b\u044f \u043c\u0430\u043f\u043f\u0438\u043d\u0433\u0430 \u043d\u0430\u0448\u0438\u0445 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0435\u0439.<\/p>\n<p>lombok &#8212; \u0434\u043b\u044f \u0441\u043e\u043a\u0440\u0430\u0449\u0435\u043d\u0438\u044f \u0448\u0430\u0431\u043b\u043e\u043d\u043d\u043e\u0433\u043e \u043a\u043e\u0434\u0430 \u0447\u0435\u0440\u0435\u0437 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 \u0430\u043d\u043d\u043e\u0442\u0430\u0446\u0438\u0439.<\/p>\n<p>\u041d\u0430\u0447\u043d\u0435\u043c \u0441 \u043a\u043e\u0434\u0430 \u043d\u0430\u0448\u0435\u0439 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0438, \u043a\u0430\u043a \u044f \u0443\u043f\u043e\u043c\u0438\u043d\u0430\u043b \u0440\u0430\u043d\u044c\u0448\u0435 &#8212; \u044d\u0442\u043e \u0431\u0443\u0434\u0435\u0442 \u043a\u043d\u0438\u0433\u0430 (Book). \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043a\u043b\u0430\u0441\u0441 Book.<\/p>\n<pre><code class=\"java\">@Data @NoArgsConstructor @AllArgsConstructor @Entity @Table(name = \"book\") public class Book {      @Id     @Column(name = \"id\")     @GeneratedValue(strategy = GenerationType.IDENTITY)     private Long id;     @Column(name = \"title\")     private String title;      @Column(name = \"author\")     private String author;      @Column(name = \"year\")     private int year;  } <\/code><\/pre>\n<p>\u0414\u0430\u043b\u0435\u0435 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 BookRepository \u0438 \u0443\u043d\u0430\u0441\u043b\u0435\u0434\u0443\u0435\u043c \u0435\u0433\u043e \u043e\u0442 JpaRepository &#8212; \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u043d\u0430\u0431\u043e\u0440 \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0445 \u043c\u0435\u0442\u043e\u0434\u043e\u0432 JPA \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0411\u0414.<\/p>\n<pre><code class=\"java\">public interface BookRepository extends JpaRepository&lt;Book, Long&gt; { } <\/code><\/pre>\n<p>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043a\u043b\u0430\u0441\u0441 BookDto, \u0442\u0430\u043a \u043a\u0430\u043a \u0443\u0440\u043e\u0432\u0435\u043d\u044c \u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u043d\u0435 \u0434\u043e\u043b\u0436\u0435\u043d \u043d\u0430\u043f\u0440\u044f\u043c\u0443\u044e \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0441 \u043d\u0430\u0448\u0435\u0439 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u044c\u044e Book (\u043c\u043e\u0436\u0435\u0442 \u0432 \u0434\u0430\u043d\u043d\u043e\u043c \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u044d\u0442\u043e \u0438 \u043d\u0435 \u043d\u0443\u0436\u043d\u043e, \u0442\u0430\u043a \u043a\u0430\u043a \u044d\u0442\u043e \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u0443\u0441\u043b\u043e\u0436\u043d\u044f\u0435\u0442 \u043a\u043e\u0434).<\/p>\n<pre><code class=\"java\">@Data @NoArgsConstructor @AllArgsConstructor public class BookDto {      private Long id;     private String title;     private String author;     private int year; } <\/code><\/pre>\n<p>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 BookMapper \u0434\u043b\u044f \u043c\u0430\u043f\u043f\u0438\u043d\u0433\u0430 \u043d\u0430\u0448\u0438\u0445 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0435\u0439 \u0438\u0437 Book \u0438 BookDto \u0438 \u043e\u0431\u0440\u0430\u0442\u043d\u043e, \u0437\u0434\u0435\u0441\u044c \u043c\u044b \u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c mapstruct.<\/p>\n<pre><code class=\"java\">@Mapper(componentModel = \"spring\") public interface BookMapper {     Book dtoToModel(BookDto bookDto);      BookDto modelToDto(Book book);      List&lt;BookDto&gt; toListDto(List&lt;Book&gt; books); } <\/code><\/pre>\n<p>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0435\u0449\u0435 \u043e\u0434\u0438\u043d \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 BookService, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u0438 \u043d\u0430\u043f\u0438\u0448\u0435\u043c \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u0435 \u043c\u0435\u0442\u043e\u0434\u044b \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u043d\u0430\u0448\u0438\u043c\u0438 \u043a\u043d\u0438\u0433\u0430\u043c\u0438.<\/p>\n<pre><code class=\"java\">public interface BookService {     List&lt;BookDto&gt; findAll ();     BookDto findById( Long id);     BookDto save (BookDto book);     void deleteById (Long id); } <\/code><\/pre>\n<p>\u0438 \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u043c \u044d\u0442\u0438 \u043c\u0435\u0442\u043e\u0434\u044b \u0432 \u043a\u043b\u0430\u0441\u0441\u0435 BookServiceImpl.<\/p>\n<pre><code class=\"java\">@Service @RequiredArgsConstructor @Transactional(readOnly = true) public class BookServiceImpl implements BookService {      private final BookRepository bookRepository;     private final BookMapper bookMapper;      @Override     public List&lt;BookDto&gt; findAll() {         return bookMapper.toListDto(bookRepository.findAll());     }      @Override     public BookDto findById(Long id) {         return Optional.of(getById(id)).map(bookMapper::modelToDto).get();     }      @Override     @Transactional     public BookDto save(BookDto book) {         return bookMapper.modelToDto(bookRepository.save(                 bookMapper.dtoToModel(book)));     }      @Override     @Transactional     public void deleteById(Long id) {         var book = getById(id);         bookRepository.delete(book);     }      private Book getById(Long id) {         return bookRepository.findById(id)                 .orElseThrow(() -&gt; new RuntimeException(                         \"Book with id: \" + id + \" not found\"));     } } <\/code><\/pre>\n<p>\u041f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u0439 \u043a\u043b\u0430\u0441\u0441, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043c\u044b \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0437\u0434\u0435\u0441\u044c &#8212; \u044d\u0442\u043e \u0431\u0443\u0434\u0435\u0442 BooksController &#8212; \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0438 \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0434\u0430\u0432\u0430\u0442\u044c \u043d\u0430\u0448\u0438 \u044d\u043d\u0434\u043f\u043e\u0439\u043d\u0442\u044b \u0434\u043b\u044f \u043f\u0440\u043e\u0441\u043c\u043e\u0442\u0440\u0430, \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u044f, \u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0438 \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f \u043d\u0430\u0448\u0438\u0445 \u043a\u043d\u0438\u0433.<\/p>\n<pre><code class=\"java\">@RestController @RequestMapping(\"api\/v1\") @RequiredArgsConstructor public class BooksController {     private final BookService bookService;      @GetMapping(\"\/books\")     public List&lt;BookDto&gt; allBooks() {         return bookService.findAll();     }      @GetMapping(\"\/book\/{id}\")     @ResponseStatus(HttpStatus.OK)     public ResponseEntity&lt;BookDto&gt; getBook(@PathVariable Long id) {         return ResponseEntity.ok().body(bookService.findById(id));     }      @PostMapping(\"\/book\")     public ResponseEntity&lt;BookDto&gt; createBook( @RequestBody BookDto book) throws URISyntaxException {         BookDto result = bookService.save(book);         return ResponseEntity.created(new URI(\"\/api\/v1\/books\/\" + result.getId()))                 .body(result);     }      @PutMapping(\"\/book\/{id}\")     @ResponseStatus(HttpStatus.OK)     public ResponseEntity&lt;BookDto&gt; updateBook( @PathVariable Long id, @RequestBody BookDto book) {         return ResponseEntity.ok().body(bookService.save(book));     }      @DeleteMapping(\"\/book\/{id}\")     public ResponseEntity&lt;?&gt; deleteBook(@PathVariable Long id) {         bookService.deleteById(id);         return ResponseEntity.ok().build();     }  } <\/code><\/pre>\n<p>\u0421\u0435\u0439\u0447\u0430\u0441 \u0437\u0430\u0439\u043c\u0435\u043c\u0441\u044f \u043d\u0430\u0448\u0438\u043c application.properties \u0444\u0430\u0439\u043b\u043e\u043c \u0438 \u043f\u0440\u043e\u043f\u0438\u0448\u0435\u043c \u0442\u0430\u043c \u0434\u043e\u0441\u0442\u0443\u043f \u043a \u043d\u0430\u0448\u0435\u0439 \u0431\u0430\u0437\u0435 \u0434\u0430\u043d\u043d\u044b\u0445, \u0432\u043a\u043b\u044e\u0447\u0438\u043c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 liquibase, \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u043c \u043f\u043e\u0440\u0442 8181 &#8212; \u0447\u0442\u043e\u0431\u044b \u043d\u0430\u0448 \u0431\u044d\u043a\u0435\u043d\u0434 \u043f\u043e\u0434\u043d\u0438\u043c\u0430\u043b\u0441\u044f \u0438\u043c\u0435\u043d\u043d\u043e \u043d\u0430 \u044d\u0442\u043e\u043c \u043f\u043e\u0440\u0442\u0443, \u0430 \u0442\u0430\u043a\u0436\u0435 \u0432\u043a\u043b\u044e\u0447\u0438\u043c \u0434\u0440\u0443\u0433\u0438\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438.<\/p>\n<pre><code class=\"java\">server.port=8181  spring.datasource.url=jdbc:postgresql:\/\/localhost:15432\/books_db spring.datasource.username=username spring.datasource.password=password  spring.jpa.hibernate.ddl-auto=none spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect spring.jpa.properties.hibernate.format_sql=true spring.jpa.show-sql=true  spring.mvc.pathmatch.matching-strategy = ANT_PATH_MATCHER  spring.liquibase.enabled=true spring.liquibase.drop-first=false spring.liquibase.change-log=classpath:db\/changelog\/db.changelog-master.xml spring.liquibase.default-schema=public <\/code><\/pre>\n<p>\u041e\u0441\u0442\u0430\u043b\u043e\u0441\u044c \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c liquibase \u0441\u043a\u0440\u0438\u043f\u0442\u044b \u0434\u043b\u044f \u043d\u0430\u043a\u0430\u0442\u044b\u0432\u0430\u043d\u0438\u044f \u0442\u0430\u0431\u043b\u0438\u0446\u044b \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445 \u0438 \u0432\u0441\u0442\u0430\u0432\u043a\u0438 \u043f\u0435\u0440\u0432\u043e\u043d\u0430\u0447\u0430\u043b\u044c\u043d\u043e\u0439 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438.<\/p>\n<p>\u0412\u043d\u0430\u0447\u0430\u043b\u0435 \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 \u043f\u0430\u043f\u043e\u043a \u043a\u0430\u043a \u043d\u0430 \u0440\u0438\u0441\u0443\u043d\u043a\u0435<\/p>\n<figure class=\"\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/47d\/b90\/c88\/47db90c88ef430e573c79fd5fe7532a7.png\" width=\"378\" height=\"167\"><\/figure>\n<p>\u0422\u043e \u0435\u0441\u0442\u044c \u0432 \u043f\u0430\u043f\u043a\u0435 resources \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u043f\u0430\u043f\u043a\u0443 db \u0438 \u0432 \u043d\u0435\u0439 \u043f\u0430\u043f\u043a\u0443 changelog, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u0444\u0430\u0439\u043b db.changelog-master.xml, \u043f\u0443\u0442\u044c \u043a \u043d\u0435\u043c\u0443 \u043f\u0440\u043e\u043f\u0438\u0441\u0430\u043d \u0432 \u043d\u0430\u0448\u0435\u043c \u0444\u0430\u0439\u043b\u0435 application.properties. \u042d\u0442\u043e \u0431\u0443\u0434\u0435\u0442 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u0444\u0430\u0439\u043b, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0442\u044c \u0441\u043a\u0440\u0438\u043f\u0442\u044b. <\/p>\n<pre><code class=\"java\">&lt;?xml version=\"1.0\" encoding=\"UTF-8\" ?&gt; &lt;databaseChangeLog         xmlns:xsi=\"http:\/\/www.w3.org\/2001\/XMLSchema-instance\"         xmlns=\"http:\/\/www.liquibase.org\/xml\/ns\/dbchangelog\"         xsi:schemaLocation=\"http:\/\/www.liquibase.org\/xml\/ns\/dbchangelog http:\/\/www.liquibase.org\/xml\/ns\/dbchangelog\/dbchangelog-3.1.xsd\"&gt;      &lt;include file=\"v.1.0.0\/cumulative.xml\" relativeToChangelogFile=\"true\" \/&gt;  &lt;\/databaseChangeLog&gt; <\/code><\/pre>\n<p>\u0414\u0430\u043b\u0435\u0435 \u0432 \u043f\u0430\u043f\u043a\u0435 changelog \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u043f\u0430\u043f\u043a\u0443 v.1.0.0, \u0433\u0434\u0435 \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u0442\u0440\u0438 \u0444\u0430\u0439\u043b\u0430.<\/p>\n<p>2023-05-12-1-create-table-book.xml &#8212; \u043d\u0430\u043a\u0430\u0442\u044b\u0432\u0430\u0435\u043c \u0442\u0430\u0431\u043b\u0438\u0446\u0443 book<\/p>\n<pre><code class=\"java\">&lt;?xml version=\"1.0\" encoding=\"UTF-8\" ?&gt; &lt;databaseChangeLog         xmlns:xsi=\"http:\/\/www.w3.org\/2001\/XMLSchema-instance\"         xmlns=\"http:\/\/www.liquibase.org\/xml\/ns\/dbchangelog\"         xsi:schemaLocation=\"http:\/\/www.liquibase.org\/xml\/ns\/dbchangelog http:\/\/www.liquibase.org\/xml\/ns\/dbchangelog\/dbchangelog-3.1.xsd\"&gt;      &lt;changeSet logicalFilePath=\"2023-05-12-1-create-table-book\"                id=\"2023-05-12-1-create-table-book\" author=\"s.m\"&gt;         &lt;createTable tableName=\"book\"&gt;             &lt;column name=\"id\" type=\"serial\"&gt;                 &lt;constraints nullable=\"false\" primaryKey=\"true\"\/&gt;             &lt;\/column&gt;             &lt;column name=\"title\" type=\"varchar(100)\"&gt;                 &lt;constraints nullable=\"false\"\/&gt;             &lt;\/column&gt;             &lt;column name=\"author\" type=\"varchar(100)\"&gt;                 &lt;constraints nullable=\"false\"\/&gt;             &lt;\/column&gt;             &lt;column name=\"year\" type=\"int\"&gt;                 &lt;constraints nullable=\"false\"\/&gt;             &lt;\/column&gt;         &lt;\/createTable&gt;     &lt;\/changeSet&gt;  &lt;\/databaseChangeLog&gt; <\/code><\/pre>\n<p>\u0444\u0430\u0439\u043b 2023-05-12-2-insert-books.xml &#8212; \u0432\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u043c 6 \u043a\u043d\u0438\u0433 \u0432 \u043d\u0430\u0448\u0443 \u0442\u0430\u0431\u043b\u0438\u0446\u0443<\/p>\n<pre><code class=\"java\">&lt;?xml version=\"1.0\" encoding=\"UTF-8\" ?&gt; &lt;databaseChangeLog         xmlns:xsi=\"http:\/\/www.w3.org\/2001\/XMLSchema-instance\"         xmlns=\"http:\/\/www.liquibase.org\/xml\/ns\/dbchangelog\"         xsi:schemaLocation=\"http:\/\/www.liquibase.org\/xml\/ns\/dbchangelog http:\/\/www.liquibase.org\/xml\/ns\/dbchangelog\/dbchangelog-3.1.xsd\"&gt;      &lt;changeSet logicalFilePath=\"2023-05-12-2-insert-books\"                id=\"2023-05-12-2-insert-books\" author=\"s.m\"&gt;         &lt;insert tableName=\"book\"&gt;             &lt;column  name=\"title\"  value=\"Effective Java\"\/&gt;             &lt;column  name=\"author\"  value=\"Joshua Bloch\"\/&gt;             &lt;column  name=\"year\"  value=\"2018\"\/&gt;         &lt;\/insert&gt;         &lt;insert tableName=\"book\"&gt;             &lt;column  name=\"title\"  value=\"Java Projects\"\/&gt;             &lt;column  name=\"author\"  value=\"Peter Verhas\"\/&gt;             &lt;column  name=\"year\"  value=\"2018\"\/&gt;         &lt;\/insert&gt;         &lt;insert tableName=\"book\"&gt;             &lt;column  name=\"title\"  value=\"Spring in Action, 5th Edition\"\/&gt;             &lt;column  name=\"author\"  value=\"Craig Walls\"\/&gt;             &lt;column  name=\"year\"  value=\"2018\"\/&gt;         &lt;\/insert&gt;         &lt;insert tableName=\"book\"&gt;             &lt;column  name=\"title\"  value=\"Java Persistence with Hibernate, Second Edition\"\/&gt;             &lt;column  name=\"author\"  value=\"Christian Bauer, Gavin King, and Gary Gregory\"\/&gt;             &lt;column  name=\"year\"  value=\"2015\"\/&gt;         &lt;\/insert&gt;         &lt;insert tableName=\"book\"&gt;             &lt;column  name=\"title\"  value=\"Java: A Beginner's Guide, Eighth Edition\"\/&gt;             &lt;column  name=\"author\"  value=\"Herbert Schildt\"\/&gt;             &lt;column  name=\"year\"  value=\"2019\"\/&gt;         &lt;\/insert&gt;         &lt;insert tableName=\"book\"&gt;             &lt;column  name=\"title\"  value=\"Spring Boot 2 Recipes\"\/&gt;             &lt;column  name=\"author\"  value=\"Marten Deinum\"\/&gt;             &lt;column  name=\"year\"  value=\"2018\"\/&gt;         &lt;\/insert&gt;      &lt;\/changeSet&gt;  &lt;\/databaseChangeLog&gt; <\/code><\/pre>\n<p>\u0438 \u0444\u0430\u0439\u043b cumulative.xml, \u0433\u0434\u0435 \u043c\u044b \u0430\u043a\u043a\u0443\u043c\u0443\u043b\u0438\u0440\u0443\u0435\u043c \u0432\u0441\u0435 \u043d\u0430\u0448\u0438 \u0444\u0430\u0439\u043b\u044b \u0441\u043e \u0441\u043a\u0440\u0438\u043f\u0442\u0430\u043c\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0445\u043e\u0442\u0438\u043c \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c.<\/p>\n<pre><code class=\"java\">&lt;?xml version=\"1.0\" encoding=\"UTF-8\" ?&gt; &lt;databaseChangeLog         xmlns:xsi=\"http:\/\/www.w3.org\/2001\/XMLSchema-instance\"         xmlns=\"http:\/\/www.liquibase.org\/xml\/ns\/dbchangelog\"         xsi:schemaLocation=\"http:\/\/www.liquibase.org\/xml\/ns\/dbchangelog http:\/\/www.liquibase.org\/xml\/ns\/dbchangelog\/dbchangelog-3.1.xsd\"&gt;      &lt;include file=\"2023-05-12-1-create-table-book.xml\" relativeToChangelogFile=\"true\" \/&gt;     &lt;include file=\"2023-05-12-2-insert-books.xml\" relativeToChangelogFile=\"true\" \/&gt;  &lt;\/databaseChangeLog&gt; <\/code><\/pre>\n<p>\u0415\u0449\u0435 \u043d\u0430\u043f\u0438\u0448\u0435\u043c \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u043e\u0439 \u0441\u043a\u0440\u0438\u043f\u0442, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043d\u0430\u043c \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u0441\u044f \u043f\u043e\u0437\u0436\u0435. \u041a \u043d\u0435\u043c\u0443 \u0431\u0443\u0434\u0435\u0442 \u043e\u0431\u0440\u0430\u0449\u0430\u0442\u044c\u0441\u044f \u0431\u0430\u0437\u0430 \u0434\u0430\u043d\u043d\u044b\u0445 postgreSQL \u0438 \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u0442\u0430\u043c \u0441\u0430\u043c\u0443 \u0431\u0430\u0437\u0443 \u0434\u0430\u043d\u043d\u044b\u0445, \u043a \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u043c\u044b \u0438 \u0431\u0443\u0434\u0435\u043c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0442\u044c\u0441\u044f.<\/p>\n<p>\u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043f\u0430\u043f\u043a\u0443 infrastructure, \u0430 \u0432 \u043d\u0435\u0439 \u043f\u0430\u043f\u043a\u0443 db. \u0414\u043e\u043b\u0436\u043d\u043e \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c\u0441\u044f \u0442\u0430\u043a.<\/p>\n<figure class=\"\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/7dc\/ca7\/732\/7dcca7732b64e481021b0759f543bedf.png\" width=\"233\" height=\"166\"><\/figure>\n<p>\u0418 \u0443\u0436\u0435 \u0432 \u043f\u0430\u043f\u043a\u0435 db \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0444\u0430\u0439\u043b create_db.sql &#8212; \u0437\u0434\u0435\u0441\u044c \u043c\u044b \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u0431\u0430\u0437\u0443 \u0434\u0430\u043d\u043d\u044b\u0445 books_db.<\/p>\n<pre><code class=\"java\">create database books_db;<\/code><\/pre>\n<p>\u0421\u0435\u0439\u0447\u0430\u0441 \u0432 \u043a\u043e\u0440\u043d\u0435 \u043d\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u0441\u043e\u0437\u0434\u0430\u0435\u043c Dockerfile<\/p>\n<figure class=\"\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/e8a\/a59\/827\/e8aa598271cb35db89f66a085b1d486f.png\" width=\"461\" height=\"877\"><\/figure>\n<pre><code class=\"java\">FROM maven:3.8.4-openjdk-17 as builder WORKDIR \/app COPY . \/app\/. RUN mvn -f \/app\/pom.xml clean package -Dmaven.test.skip=true  FROM eclipse-temurin:17-jre-alpine WORKDIR \/app COPY --from=builder \/app\/target\/*.jar \/app\/*.jar EXPOSE 8181 ENTRYPOINT [\"java\", \"-jar\", \"\/app\/*.jar\"]<\/code><\/pre>\n<p>Dockerfile \u043d\u0443\u0436\u0435\u043d \u0434\u043b\u044f \u0442\u043e\u0433\u043e \u0447\u0442\u043e\u0431\u044b \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0430\u043d\u0438\u0438 \u0435\u0433\u043e \u043c\u044b \u0441\u043e\u0437\u0434\u0430\u043b\u0438 \u043e\u0431\u0440\u0430\u0437, \u0430 \u0443\u0436\u0435 \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0430\u043d\u0438\u0438 \u043e\u0431\u0440\u0430\u0437\u0430 \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u043b\u0438 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440 \u0441 \u043d\u0430\u0448\u0438\u043c \u0431\u044d\u043a\u0435\u043d\u0434\u043e\u043c.<\/p>\n<p>\u0417\u0434\u0435\u0441\u044c \u043c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u043c\u043d\u043e\u0433\u043e\u044d\u0442\u0430\u043f\u043d\u0443\u044e \u0441\u0431\u043e\u0440\u043a\u0443, \u043d\u0430 \u043f\u0435\u0440\u0432\u043e\u043c \u044d\u0442\u0430\u043f\u0435 \u043c\u044b \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c jar-\u043d\u0438\u043a \u043d\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0430, \u0430 \u043d\u0430 \u0432\u0442\u043e\u0440\u043e\u043c \u043c\u044b \u0435\u0433\u043e \u0443\u0436\u0435 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c.<\/p>\n<p>\u041f\u0440\u043e\u0439\u0434\u0435\u043c\u0441\u044f \u043f\u043e Dockerfile <\/p>\n<p>FROM maven:3.8.4-openjdk-17 as builder &#8212; \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0430\u043d\u0438\u0438 \u043a\u0430\u043a\u043e\u0433\u043e \u043e\u0431\u0440\u0430\u0437\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0434\u043e\u043a\u0435\u0440 \u0441\u0442\u044f\u043d\u0435\u0442 \u0441 docker hub \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0431\u0438\u043b\u0434\u0438\u0442\u044c \u043d\u0430\u0448 \u043f\u0440\u043e\u0435\u043a\u0442, as builder &#8212; \u044d\u0442\u043e \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u043c\u044b \u043f\u0440\u0438\u0441\u0432\u043e\u0438\u043b\u0438, \u0434\u043b\u044f \u0442\u043e\u0433\u043e \u0447\u0442\u043e\u0431\u044b \u043e\u0431\u0440\u0430\u0442\u0438\u0442\u044c\u0441\u044f \u0441 \u0434\u0440\u0443\u0433\u043e\u0433\u043e \u0441\u043b\u043e\u044f \u043e\u0431\u0440\u0430\u0437\u0430 \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u044b\u0445.<\/p>\n<p>WORKDIR \/app &#8212; \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u044e app \u0432\u043d\u0443\u0442\u0440\u0438 \u0441\u043b\u043e\u044f \u043e\u0431\u0440\u0430\u0437\u0430.<\/p>\n<p>COPY . \/app\/. &#8212; \u043a\u043e\u043f\u0438\u0440\u0443\u0435\u043c \u0432\u0441\u0435 \u043d\u0430\u0448\u0438 \u043f\u0430\u043f\u043a\u0438 \u0441 \u0442\u0435\u043a\u0443\u0449\u0435\u0433\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u0432 \u043f\u0430\u043f\u043a\u0443 app \u0432 \u0441\u043b\u043e\u0435 \u043e\u0431\u0440\u0430\u0437\u0430.<\/p>\n<p>RUN mvn -f \/app\/pom.xml clean package -Dmaven.test.skip=true   &#8212; \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c maven, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0438\u043b\u0434\u0438\u0442 \u043d\u0430\u0448 \u043f\u0440\u043e\u0435\u043a\u0442 \u0438 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c jar-\u043d\u0438\u043a.<\/p>\n<p>FROM eclipse-temurin:17-jre-alpine &#8212; \u0441\u043d\u043e\u0432\u0430 \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0430\u043d\u0438\u0438 \u043a\u0430\u043a\u043e\u0433\u043e \u043e\u0431\u0440\u0430\u0437\u0430, \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0442\u044c \u043d\u0430\u0448 \u043f\u0440\u043e\u0435\u043a\u0442, \u0437\u0434\u0435\u0441\u044c \u0443\u0436\u0435 \u043c\u044b \u043d\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c jdk, \u0430 \u0442\u043e\u043b\u044c\u043a\u043e jre &#8212; \u0442\u0430\u043a \u043a\u0430\u043a \u043d\u0430\u043c \u043d\u0435 \u043d\u0443\u0436\u043d\u044b \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u044b \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0430.<\/p>\n<p>WORKDIR \/app &#8212; \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u044e app \u0432 \u043d\u043e\u0432\u043e\u043c \u0441\u043b\u043e\u0435 \u043e\u0431\u0440\u0430\u0437\u0430.<\/p>\n<p>COPY &#8212;from=builder \/app\/target\/<em>.jar \/app\/<\/em>.jar &#8212; \u043a\u043e\u043f\u0438\u0440\u0443\u0435\u043c \u0441 \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0435\u0433\u043e \u0441\u043b\u043e\u044f \u0441 \u043f\u0430\u043f\u043a\u0438 target \u043d\u0430\u0448 jar-\u043d\u0438\u043a \u0432 \u043f\u0430\u043f\u043a\u0443 app.<\/p>\n<p>EXPOSE 8181 &#8212; \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c \u043d\u0430 \u043a\u0430\u043a\u043e\u043c \u043f\u043e\u0440\u0442\u0443 \u0434\u043e\u043b\u0436\u0435\u043d \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u043d\u0430\u0448 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440.<\/p>\n<p>ENTRYPOINT [&#171;java&#187;, &#171;-jar&#187;, &#171;\/app\/*.jar&#187;] &#8212; \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c \u043d\u0430\u0448\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0432 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u0435.<\/p>\n<p>\u041f\u0440\u0438\u0448\u043b\u043e \u0432\u0440\u0435\u043c\u044f \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u0441\u0430\u043c docker-compose.yml \u0444\u0430\u0439\u043b.<\/p>\n<p>\u0421\u043d\u043e\u0432\u0430 \u0432 \u043a\u043e\u0440\u043d\u0435 \u043d\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u0444\u0430\u0439\u043b docker-compose.yml.<\/p>\n<pre><code class=\"java\">version: '3.8' services:   client-backend:     image: client:0.0.1     build:       context: .       dockerfile: Dockerfile     ports:       - \"8181:8181\"     depends_on:       - service-db     environment:       - SERVER_PORT= 8181       - SPRING_DATASOURCE_URL=jdbc:postgresql:\/\/service-db\/books_db    service-db:     image: postgres:14.7-alpine     environment:       POSTGRES_USER: username       POSTGRES_PASSWORD: password     ports:       - \"15432:5432\"     volumes:       - .\/infrastructure\/db\/create_db.sql:\/docker-entrypoint-initdb.d\/create_db.sql       - db-data:\/var\/lib\/postgresql\/data     restart: unless-stopped    pgadmin     container_name: pgadmin4_container     image: dpage\/pgadmin4:7     restart: always     environment:       PGADMIN_DEFAULT_EMAIL: admin@admin.com       PGADMIN_DEFAULT_PASSWORD: root     ports:       - \"5050:80\"     volumes:       - pgadmin-data:\/var\/lib\/pgadmin  volumes:   db-data:   pgadmin-data:<\/code><\/pre>\n<p>\u041f\u0440\u043e\u0439\u0434\u0435\u043c\u0441\u044f \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u043f\u043e Dockerfile.<\/p>\n<p>version: &#8216;3.8&#8217; &#8212; \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c \u0432\u0435\u0440\u0441\u0438\u044e docker compose<\/p>\n<p>services: &#8212; \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c \u043a\u0430\u043a\u0438\u0435 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u044b \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0431\u0443\u0434\u0435\u0442 \u043f\u043e\u0434\u043d\u044f\u0442\u044c.<\/p>\n<p>client-backend: &#8212; \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u043f\u0435\u0440\u0432\u043e\u0433\u043e \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u0430 \u0441 \u043d\u0430\u0448\u0438\u043c \u0431\u044d\u043a\u0435\u043d\u0434\u043e\u043c.<\/p>\n<p>image: client:0.0.1 &#8212; \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u0430 \u0438 \u0435\u0433\u043e \u0442\u044d\u0433 (\u043c\u043e\u0436\u043d\u043e \u0431\u0435\u0437 \u0442\u044d\u0433\u0430, \u0442\u043e\u0433\u0434\u0430 \u0431\u0443\u0434\u0435\u0442 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043f\u0440\u0438\u0441\u0432\u043e\u0435\u043d latest).<\/p>\n<p>build: &#8212; \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c, \u0447\u0442\u043e \u043c\u044b \u0445\u043e\u0442\u0438\u043c \u0435\u0433\u043e \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u043d\u0435 \u0441\u043a\u0430\u0447\u0438\u0432\u0430\u044f \u0441 \u0434\u043e\u043a\u0435\u0440 \u0445\u0430\u0431\u0430, \u0430 \u0431\u0443\u0434\u0435\u043c &#171;\u0441\u0442\u0440\u043e\u0438\u0442\u044c&#187; \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0430\u043d\u0438\u0438 Dockerfile.<\/p>\n<p>context: . &#8212; \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c \u0440\u0430\u0441\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u0435 Dockerfile, \u043e\u043d \u0443 \u043d\u0430\u0441 \u0440\u0430\u0441\u043f\u043e\u043b\u0430\u0433\u0430\u0435\u0442\u0441\u044f \u0432 \u0442\u043e\u0439 \u0436\u0435 \u043f\u0430\u043f\u043a\u0435, \u0447\u0442\u043e \u0438 docker-compose.yml<\/p>\n<p>dockerfile: Dockerfile &#8212; \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c \u043d\u0430\u0438\u043c\u0435\u043d\u043e\u0432\u0430\u043d\u0438\u0435 Dockerfile<\/p>\n<pre><code>    ports:       - \"8181:8181\"<\/code><\/pre>\n<p>\u0437\u0434\u0435\u0441\u044c \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c \u043f\u0435\u0440\u0432\u044b\u0439 \u043f\u043e\u0440\u0442 &#8212; \u044d\u0442\u043e \u0432\u043d\u0435\u0448\u043d\u0438\u0439 \u043f\u043e\u0440\u0442, \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0434\u043e\u0441\u0442\u0443\u043f \u043a \u043d\u0430\u0448\u0435\u043c\u0443 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u0443, \u0430 \u0432\u0442\u043e\u0440\u043e\u0439 \u043f\u043e\u0440\u0442 &#8212; \u044d\u0442\u043e \u0432\u043d\u0443\u0442\u0440\u0435\u043d\u043d\u0438\u0439 \u043f\u043e\u0440\u0442 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u0430.<\/p>\n<pre><code class=\"java\">    depends_on:       - service-db<\/code><\/pre>\n<p>\u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c, \u0447\u0442\u043e \u043d\u0430\u0448 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440 \u0434\u043e\u043b\u0436\u0435\u043d \u043f\u043e\u0434\u043d\u044f\u0442\u044c\u0441\u044f \u043f\u043e\u0441\u043b\u0435 \u0442\u043e\u0433\u043e \u043a\u0430\u043a \u043f\u043e\u0434\u043d\u0438\u043c\u0435\u0442\u0441\u044f \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440 \u0441 \u0431\u0430\u0437\u043e\u0439 \u0434\u0430\u043d\u043d\u044b\u0445, \u0442\u0430\u043a \u043a\u0430\u043a, \u0435\u0441\u043b\u0438 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440 \u0441 \u0431\u044d\u043a\u0435\u043d\u0434\u043e\u043c \u043f\u043e\u0434\u043d\u0438\u043c\u0435\u0442\u0441\u044f \u043f\u0435\u0440\u0432\u044b\u043c \u0438 \u043d\u0435 \u0431\u0443\u0434\u0435\u0442 \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043a \u0431\u0430\u0437\u0435 \u0434\u0430\u043d\u043d\u044b\u0445, \u0442\u043e \u043e\u043d \u0443\u043f\u0430\u0434\u0435\u0442 \u0441 \u043e\u0448\u0438\u0431\u043a\u043e\u0439.<\/p>\n<pre><code class=\"java\">    environment:       - SERVER_PORT= 8181       - SPRING_DATASOURCE_URL=jdbc:postgresql:\/\/service-db\/books_db<\/code><\/pre>\n<p>\u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438, \u0432 \u0442\u043e\u043c \u0447\u0438\u0441\u043b\u0435 url \u0434\u043b\u044f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043a \u0431\u0430\u0437\u0435 \u0434\u0430\u043d\u043d\u044b\u0445. \u0412 \u044d\u0442\u043e\u043c \u043f\u0443\u0442\u0438 \u043c\u044b \u0443\u0436\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u0432\u043d\u0443\u0442\u0440\u0435\u043d\u043d\u0435\u0435 \u0438\u043c\u044f \u043d\u0430\u0448\u0435\u0433\u043e \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u0430 \u0441 \u0431\u0430\u0437\u043e\u0439 \u0434\u0430\u043d\u043d\u044b\u0445 service-db.<\/p>\n<pre><code class=\"java\"> service-db:     image: postgres:14.7-alpine<\/code><\/pre>\n<p>\u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c \u0438\u043c\u044f \u043d\u0430\u0448\u0435\u0433\u043e \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u0430 \u0441 \u0431\u0430\u0437\u043e\u0439 \u0434\u0430\u043d\u043d\u044b\u0445, \u0430 \u0442\u0430\u043a\u0436\u0435 \u0438\u043c\u044f \u043e\u0431\u0440\u0430\u0437\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u0441\u043a\u0430\u0447\u0435\u043d \u0441 \u0434\u043e\u043a\u0435\u0440 \u0445\u0430\u0431\u0430. \u0416\u0435\u043b\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u043f\u0440\u0438 \u044d\u0442\u043e\u043c \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0442\u044c \u0442\u044d\u0433 \u043e\u0431\u0440\u0430\u0437\u0430, \u0442\u043e \u0435\u0441\u0442\u044c \u0432\u0435\u0440\u0441\u0438\u044e, \u0435\u0441\u043b\u0438 \u0432\u0435\u0440\u0441\u0438\u044f \u043d\u0435 \u0431\u0443\u0434\u0435\u0442 \u0443\u043a\u0430\u0437\u0430\u043d\u0430, \u0442\u043e \u0431\u0443\u0434\u0435\u0442 \u0441\u043a\u0430\u0447\u0435\u043d\u0430 \u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0430 \u0441\u0430\u043c\u0430\u044f \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u044f\u044f \u0432\u0435\u0440\u0441\u0438\u044f \u0434\u0430\u043d\u043d\u043e\u0433\u043e \u043e\u0431\u0440\u0430\u0437\u0430, \u0447\u0442\u043e \u043c\u043e\u0436\u0435\u0442 \u043f\u0440\u0438\u0432\u0435\u0441\u0442\u0438 \u0432 \u0431\u0443\u0434\u0443\u0449\u0435\u043c \u043a \u043d\u0435\u0441\u043e\u0432\u043c\u0435\u0441\u0442\u0438\u043c\u043e\u0441\u0442\u0438 \u043f\u043e \u0432\u0435\u0440\u0441\u0438\u044f\u043c \u0438 \u043e\u0448\u0438\u0431\u043a\u0430\u043c.<\/p>\n<pre><code class=\"haskell\">    environment:       POSTGRES_USER: username       POSTGRES_PASSWORD: password<\/code><\/pre>\n<p>\u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c username \u0438 password, \u043e\u043d\u0438 \u0434\u043e\u043b\u0436\u043d\u044b \u0441\u043e\u0432\u043f\u0430\u0434\u0430\u0442\u044c \u0441 \u0442\u0435\u043c\u0438, \u0447\u0442\u043e \u043c\u044b \u0443\u043a\u0430\u0437\u0430\u043b\u0438 \u0432 application.properties.<\/p>\n<pre><code class=\"java\">    ports:       - \"15432:5432\"<\/code><\/pre>\n<p>\u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c \u043f\u043e\u0440\u0442\u044b.<\/p>\n<pre><code class=\"java\">    volumes:       - .\/infrastructure\/db\/create_db.sql:\/docker-entrypoint-initdb.d\/create_db.sql       - db-data:\/var\/lib\/postgresql\/data<\/code><\/pre>\n<p>\u0437\u0434\u0435\u0441\u044c \u043c\u044b \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c, \u0447\u0442\u043e\u0431\u044b \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u043b\u0441\u044f \u043d\u0430\u0448 \u0441\u043a\u0440\u0438\u043f\u0442, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043c\u044b \u043d\u0430\u043f\u0438\u0441\u0430\u043b\u0438 \u0440\u0430\u043d\u0435\u0435 \u0438 \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0440\u0430\u0441\u043f\u043e\u043b\u043e\u0436\u0435\u043d \u0432 \u043f\u0430\u043f\u043a\u0435 infrastructure\/db \u043f\u043e \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044e \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445 books_db.<\/p>\n<p>\u0430 \u0432\u043e \u0432\u0442\u043e\u0440\u043e\u043c volume \u043c\u044b \u0434\u0435\u043b\u0430\u0435\u043c \u0447\u0442\u043e\u0431\u044b \u043d\u0430\u0448\u0438 \u0434\u0430\u043d\u043d\u044b\u0435 \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u043b\u0438\u0441\u044c \u043d\u0435 \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e \u0432 \u043d\u0430\u0448 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440, \u0430 \u0432 \u0444\u0430\u0439\u043b, \u0440\u0430\u0441\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u043d\u044b\u0439 \u0432\u043d\u0435 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u0430. \u042d\u0442\u043e \u043d\u0443\u0436\u043d\u043e \u0434\u043b\u044f \u0442\u043e\u0433\u043e \u0447\u0442\u043e\u0431\u044b \u043f\u0440\u0438 \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u043a\u0435 \u043e\u0431\u0440\u0430\u0437\u0430, \u0435\u0441\u043b\u0438 \u043c\u044b \u0435\u0433\u043e \u0443\u0434\u0430\u043b\u0438\u043c \u0438 \u043f\u043e\u0434\u043d\u0438\u043c\u0435\u043c \u0441\u043d\u043e\u0432\u0430, \u0447\u0442\u043e\u0431\u044b \u043d\u0430\u0448\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u0432 \u0431\u0430\u0437\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 \u043d\u0435 \u043f\u043e\u0442\u0435\u0440\u044f\u043b\u0438\u0441\u044c, \u0430 \u0441\u043e\u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0441\u044c. \u042d\u0442\u043e \u044f \u043f\u0440\u043e\u0434\u0435\u043c\u043e\u043d\u0441\u0442\u0440\u0438\u0440\u0443\u044e \u0447\u0443\u0442\u044c \u043f\u043e\u0437\u0436\u0435.<\/p>\n<p>\u0421 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u043e\u043c pgadmin \u0432\u0440\u043e\u0434\u0435 \u0432\u0441\u0435 \u043f\u043e\u043d\u044f\u0442\u043d\u043e, \u043e\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u044e\u0441\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u043d\u0430<\/p>\n<pre><code class=\"java\">    environment:       PGADMIN_DEFAULT_EMAIL: admin@admin.com       PGADMIN_DEFAULT_PASSWORD: root<\/code><\/pre>\n<p>\u044d\u0442\u043e \u0434\u0430\u043d\u043d\u044b\u0435 \u0434\u043b\u044f \u0432\u0445\u043e\u0434\u0430 \u0432 pgadmin \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e.<\/p>\n<p>\u041d\u0430\u043f\u0438\u0441\u0430\u043b\u0438 \u043c\u043d\u043e\u0433\u043e \u043a\u043e\u0434\u0430, \u0442\u0435\u043f\u0435\u0440\u044c \u0435\u0433\u043e \u043d\u0430\u0434\u043e \u043f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u0442\u044c, \u0432\u043d\u0430\u0447\u0430\u043b\u0435 \u043f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u043c \u0435\u0433\u043e \u0431\u0435\u0437 \u0444\u0440\u043e\u043d\u0442\u0430, \u043d\u043e \u043f\u043e\u0442\u043e\u043c \u0435\u0449\u0435 \u0434\u043e\u0431\u0430\u0432\u0438\u043b \u0438 \u0435\u0433\u043e.<\/p>\n<p>\u0418\u0442\u0430\u043a, \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0438\u043c \u0432 \u0442\u0435\u0440\u043c\u0438\u043d\u0430\u043b, \u0432 \u043f\u0430\u043f\u043a\u0443 \u0441 \u043f\u0440\u043e\u0435\u043a\u0442\u043e\u043c \u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u043c \u043a\u043e\u043c\u0430\u043d\u0434\u0443 docker-compose up<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/807\/7cf\/3a7\/8077cf3a7d02fe4a2f8d0329e5b667a7.png\" width=\"744\" height=\"126\"><\/figure>\n<p>\u041f\u043e\u0441\u043b\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u043e\u0439 \u043a\u043e\u043c\u0430\u043d\u0434\u044b, \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0431\u0443\u0434\u0435\u0442 \u043f\u043e\u0434\u043e\u0436\u0434\u0430\u0442\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043c\u0438\u043d\u0443\u0442 \u043f\u043e\u043a\u0430 \u0434\u043e\u043a\u0435\u0440 \u0441\u0431\u0438\u043b\u0434\u0438\u0442 \u043d\u0430\u0448 \u043e\u0431\u0440\u0430\u0437, \u0430 \u0442\u0430\u043a\u0436\u0435 \u0441\u0442\u044f\u043d\u0435\u0442 \u0432\u0441\u0435 \u043e\u0431\u0440\u0430\u0437\u044b \u0441 \u0434\u043e\u043a\u0435\u0440 \u0445\u0430\u0431\u0430.<\/p>\n<p>\u0412 \u043a\u043e\u043d\u0446\u0435, \u0432 \u043a\u043e\u043d\u0441\u043e\u043b\u0438 \u0432\u044b \u0434\u043e\u043b\u0436\u043d\u044b \u0431\u0443\u0434\u0435\u0442\u0435 \u0443\u0432\u0438\u0434\u0435\u0442\u044c \u0437\u0430\u043f\u0443\u0441\u043a \u043d\u0430\u0448\u0435\u0433\u043e \u0431\u044d\u043a\u0435\u043d\u0434\u0430.<\/p>\n<p>\u0418 \u0435\u0441\u043b\u0438 \u043c\u044b \u0437\u0430\u0439\u0434\u0435\u043c \u0432 Docker Desktop, \u0442\u043e \u0434\u043e\u043b\u0436\u043d\u044b \u0443\u0432\u0438\u0434\u0435\u0442\u044c \u0432\u0441\u0435 \u0442\u0440\u0438 \u043d\u0430\u0448\u0438 \u0440\u0430\u0431\u043e\u0442\u0430\u044e\u0449\u0438\u0435 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u044b.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/85f\/7f9\/d47\/85f7f9d47728f731c6722dd9c40f6cb4.png\" width=\"982\" height=\"289\"><\/figure>\n<p>\u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u043f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u043c \u043a\u0430\u043a \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u043d\u0430\u0448 \u0431\u044d\u043a\u0435\u043d\u0434. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0447\u0435\u0440\u0435\u0437 Postman \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u043c \u043a\u043e\u043c\u0430\u043d\u0434\u0443 http:\/\/localhost:8181\/api\/v1\/books<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/d92\/937\/31c\/d9293731c3fce137438022930247e436.png\" width=\"1499\" height=\"807\"><\/figure>\n<p>\u0438 \u043c\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u043c 6 \u043d\u0430\u0448\u0438\u0445 \u043a\u043d\u0438\u0433, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u044b \u0437\u0430\u043f\u0438\u0441\u0430\u043b\u0438 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e liquibase.<\/p>\n<p>\u0417\u0430\u0439\u0434\u0435\u043c \u0432 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0435 \u043d\u0430 <a href=\"http:\/\/localhost:5050\/login?next=%2F\" rel=\"noopener noreferrer nofollow\">http:\/\/localhost:5050<\/a> \u0438 \u0432\u0432\u0435\u0434\u0435\u043c username: <a href=\"mailto:admin@admin.com\" rel=\"noopener noreferrer nofollow\">admin@admin.com<\/a> \u0438 \u043f\u0430\u0440\u043e\u043b\u044c: root<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/f39\/63a\/198\/f3963a1984260a56e6c432be887d33f5.png\" width=\"897\" height=\"546\"><\/figure>\n<p>\u041c\u044b \u0434\u043e\u043b\u0436\u043d\u044b \u0437\u0430\u0439\u0442\u0438 \u043d\u0430 pgAdmin<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/5bd\/206\/11b\/5bd20611b3dad40538d26123a94a8b2b.png\" width=\"1859\" height=\"643\"><\/figure>\n<p>\u0417\u0434\u0435\u0441\u044c \u043c\u044b \u0434\u043e\u043b\u0436\u043d\u044b \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043a \u043d\u0430\u0448\u0435\u0439 \u0431\u0430\u0437\u0435 \u0434\u0430\u043d\u043d\u044b\u0445. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043d\u0430\u0436\u0438\u043c\u0430\u0435\u043c Add New Server.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/3c3\/17e\/e87\/3c317ee8751c4726bdf29e8f1e73c02e.png\" width=\"937\" height=\"659\"><\/figure>\n<p>Name \u0437\u0430\u0434\u0430\u0435\u043c \u043b\u044e\u0431\u043e\u0435. <\/p>\n<p>\u0412\u043e \u0432\u043a\u043b\u0430\u0434\u043a\u0435 Connection \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/fc4\/0cb\/cc0\/fc40cbcc08d23485f41ceeab9d540dac.png\" width=\"759\" height=\"634\"><\/figure>\n<p>Host name &#8212; \u043d\u0430\u0438\u043c\u0435\u043d\u043e\u0432\u0430\u043d\u0438\u0435, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u043c\u044b \u0443\u043a\u0430\u0437\u0430\u043b\u0438 \u0434\u043b\u044f \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u0430 \u0441 \u043d\u0430\u0448\u0435\u0439 \u0411\u0414 (service-db).<\/p>\n<p>Port &#8212; \u0432\u043d\u0443\u0442\u0440\u0435\u043d\u043d\u0438\u0439 \u043f\u043e\u0440\u0442 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u0430 \u0411\u0414 (5432).<\/p>\n<p>Maintenance database &#8212; \u043d\u0430\u0438\u043c\u0435\u043d\u043e\u0432\u0430\u043d\u0438\u0435 \u043d\u0430\u0448\u0435\u0439 \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445 (books_db).<\/p>\n<p>Username &#8212; \u0442\u043e\u0442 \u044e\u0437\u0435\u0440\u043d\u0430\u0439\u043c, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043c\u044b \u0443\u043a\u0430\u0437\u0430\u043b\u0438 \u0432 docker-compose.yml \u0434\u043b\u044f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043a \u0411\u0414 (username).<\/p>\n<p>Password- \u0442\u043e\u0442 \u043f\u0430\u0440\u043e\u043b\u044c, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043c\u044b \u0443\u043a\u0430\u0437\u0430\u043b\u0438 \u0432 docker-compose.yml \u0434\u043b\u044f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043a \u0411\u0414 (password).<\/p>\n<p>\u0414\u0430\u043b\u0435\u0435 \u0435\u0441\u043b\u0438 \u043c\u044b \u043d\u0430\u0439\u0434\u0435\u043c \u043d\u0430\u0448\u0443 \u0442\u0430\u0431\u043b\u0438\u0446\u0443 book \u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u043c \u0432 \u043d\u0435\u0439 SELECT * FROM book \u043c\u044b \u0443\u0432\u0438\u0434\u0438\u043c 6 \u043d\u0430\u0448\u0438\u0445 \u043a\u043d\u0438\u0433.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/66b\/8a0\/8e1\/66b8a08e19ecfc246ec3e3fdaf8273c3.png\" width=\"557\" height=\"794\"><\/figure>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/76e\/4a2\/7a8\/76e4a27a88142999fcc0854e225dd75a.png\" width=\"900\" height=\"240\"><\/figure>\n<p>\u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0435\u0449\u0435 \u043e\u0434\u043d\u0443 \u043a\u043d\u0438\u0433\u0443. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0438\u0434\u0435\u043c \u0432 Postman \u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u043c POST \u0437\u0430\u043f\u0440\u043e\u0441 \u043d\u0430 \u0430\u0434\u0440\u0435\u0441 http:\/\/localhost:8181\/api\/v1\/book \u0441 JSON \u043a\u043d\u0438\u0433\u0438, \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u043c\u044b \u0445\u043e\u0442\u0438\u043c \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/fdb\/967\/ffe\/fdb967ffe276830f83b1b9019ab5f53f.png\" width=\"1473\" height=\"251\"><\/figure>\n<p>\u0418\u0434\u0435\u043c \u043e\u0431\u0440\u0430\u0442\u043d\u043e \u0432 pgAdmin \u0438 \u0441\u043c\u043e\u0442\u0440\u0438\u043c \u043a\u0430\u043a\u0438\u0435 \u043a\u043d\u0438\u0433\u0438 \u0443 \u043d\u0430\u0441 \u0435\u0441\u0442\u044c.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/205\/d59\/3a1\/205d593a1d3b88b4c37f39fc7a7b3893.png\" width=\"797\" height=\"315\"><\/figure>\n<p>\u041f\u043e\u0441\u043b\u0435\u0434\u043d\u044f\u044f \u043a\u043d\u0438\u0433\u0430 \u0442\u0430\u043a\u0436\u0435 \u043f\u043e\u044f\u0432\u0438\u043b\u0430\u0441\u044c.<\/p>\n<p>\u0421\u0435\u0439\u0447\u0430\u0441 \u043e\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u043c \u0432\u0441\u0435 \u043d\u0430\u0448\u0438 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u044b \u0438 \u0443\u0434\u0430\u043b\u0438\u043c \u0438\u0445, \u044d\u0442\u043e \u043c\u043e\u0436\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0438\u043b\u0438 \u0447\u0435\u0440\u0435\u0437 Docker Desktop \u0438\u043b\u0438 \u043a\u043e\u043c\u0430\u043d\u0434\u0430\u043c\u0438 \u0432 \u0442\u0435\u0440\u043c\u0438\u043d\u0430\u043b\u0435. \u042f \u044d\u0442\u043e \u0441\u0434\u0435\u043b\u0430\u044e \u0447\u0435\u0440\u0435\u0437 Docker Desktop.<\/p>\n<p>\u0418 \u0441\u043d\u043e\u0432\u0430 \u0438\u0445 \u043f\u043e\u0434\u043d\u0438\u043c\u0435\u043c \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043a\u043e\u043c\u0430\u043d\u0434\u044b docker-compose up.<\/p>\n<p>\u0418 \u0437\u0430\u0439\u0434\u044f \u0441\u043d\u043e\u0432\u0430 \u043d\u0430 pgAdmin \u043c\u044b \u0443\u0432\u0438\u0434\u0438\u043c 7 \u043d\u0430\u0448\u0438\u043c \u043a\u043d\u0438\u0433, \u0432 \u0442\u043e\u043c \u0447\u0438\u0441\u043b\u0435 \u0441 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0435\u0439, \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u043c\u044b \u0434\u043e\u0431\u0430\u0432\u0438\u043b\u0438.<\/p>\n<p>\u0421\u0435\u0439\u0447\u0430\u0441 \u043e\u0441\u0442\u0430\u043b\u0430\u0441\u044c \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u044f\u044f \u0447\u0430\u0441\u0442\u044c \u043d\u0430\u0448\u0435\u0439 \u0440\u0430\u0431\u043e\u0442\u044b &#8212; \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u0444\u0440\u043e\u043d\u0442\u044d\u043d\u0434 \u043d\u0430 React.<\/p>\n<p>\u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0443 \u0432\u0430\u0441 \u043d\u0430 \u043a\u043e\u043c\u043f\u044c\u044e\u0442\u0435\u0440\u0435 \u0434\u043e\u043b\u0436\u0435\u043d \u0431\u044b\u0442\u044c \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d Node.js \u0438 npm.<\/p>\n<p>\u0427\u0442\u043e\u0431\u044b \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c \u043a\u0430\u043a\u0438\u0435 \u0432\u0435\u0440\u0441\u0438\u0438 \u0443 \u0432\u0430\u0441 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u044b \u0438\u043b\u0438 \u0432\u043e\u043e\u0431\u0449\u0435 \u0435\u0441\u0442\u044c \u043b\u0438 \u0443 \u0432\u0430\u0441 Node.js \u0438 npm, \u043c\u043e\u0436\u043d\u043e \u0432\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u043a\u043e\u043c\u0430\u043d\u0434\u0430\u043c\u0438 node &#8212;version \u0438 npm &#8212;version.<\/p>\n<figure class=\"\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/d58\/ec8\/49e\/d58ec849e1150c5aaa3b5c1194f6b34b.png\" width=\"313\" height=\"99\"><\/figure>\n<p>\u041f\u0435\u0440\u0435\u0445\u043e\u0434\u0438\u043c \u0432 \u0442\u0435\u0440\u043c\u0438\u043d\u0430\u043b \u0438 \u0432 \u043a\u043e\u0440\u043d\u0435 \u043d\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u043c \u043a\u043e\u043c\u0430\u043d\u0434\u0443 <code>npx create-react-app@5 frontend<\/code><\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/666\/4ad\/f74\/6664adf74682030f26dae82475ba5d40.png\" width=\"827\" height=\"35\"><\/figure>\n<p>\u0414\u0430\u043d\u043d\u043e\u0439 \u043a\u043e\u043c\u0430\u043d\u0434\u043e\u0439 \u043c\u044b \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c React \u043f\u0440\u043e\u0435\u043a\u0442 \u0432 \u043f\u0430\u043f\u043a\u0435 frontend (\u043f\u0430\u043f\u043a\u0430 \u043f\u043e\u044f\u0432\u0438\u0442\u044c\u0441\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438) \u0432 \u043d\u0430\u0448\u0435\u043c \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u043c \u043f\u0440\u043e\u0435\u043a\u0442\u0435.<\/p>\n<p>\u041a\u043e\u043c\u0430\u043d\u0434\u043e\u0439 cd frontend \u0437\u0430\u0439\u0434\u0435\u043c \u0432 \u043f\u0430\u043f\u043a\u0443 frontend.<\/p>\n<p>\u0418 \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u043c \u043a\u043e\u043c\u0430\u043d\u0434\u0443 <code>npm i bootstrap@5 react-cookie@4 react-router-dom@6 reactstrap@9<\/code>  , \u0434\u0430\u043d\u043d\u043e\u0439 \u043a\u043e\u043c\u0430\u043d\u0434\u043e\u0439 \u043c\u044b \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u043c&nbsp;<a href=\"https:\/\/getbootstrap.com\/\" rel=\"noopener noreferrer nofollow\">Bootstrap<\/a>&nbsp;, \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0443 \u0444\u0430\u0439\u043b\u043e\u0432 cookie \u0434\u043b\u044f React, React Router \u0438&nbsp;<a href=\"https:\/\/reactstrap.github.io\/\" rel=\"noopener noreferrer nofollow\"><u>Reactstrap<\/u><\/a>.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/528\/6fe\/d4a\/5286fed4aba315da187d2b2f89f73a29.png\" width=\"1125\" height=\"52\"><\/figure>\n<p>\u0415\u0441\u043b\u0438 \u043e\u0442\u043a\u0440\u044b\u0442\u044c \u043f\u0430\u043f\u043a\u0443 frontend, \u0442\u043e \u043c\u044b \u0443\u0432\u0438\u0434\u0438\u043c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0443\u044e \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 \u043f\u0430\u043f\u043e\u043a. <\/p>\n<p>\u0414\u0430\u043d\u043d\u044b\u0439 \u043f\u0440\u043e\u0435\u043a\u0442 c \u0444\u0440\u043e\u043d\u0442\u043e\u043c \u043c\u043e\u0436\u043d\u043e \u0434\u0435\u043b\u0430\u0442\u044c \u0438 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e \u0432 \u0434\u0440\u0443\u0433\u043e\u0439 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438 (\u043d\u0435 \u0432 \u043f\u0440\u043e\u0435\u043a\u0442\u0435 \u0441 \u0431\u044d\u043a\u0435\u043d\u0434\u043e\u043c), \u0442\u043e\u0433\u0434\u0430 \u043d\u0430\u0434\u043e \u0431\u0443\u0434\u0435\u0442 \u043f\u043e\u043c\u0435\u043d\u044f\u0442\u044c \u043f\u0443\u0442\u044c \u043a Dockerfile \u043a \u0444\u0440\u043e\u043d\u0442\u0443 \u0432 docker-compose.yml<\/p>\n<figure class=\"\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/fee\/f65\/e0d\/feef65e0d241e6cb27c35b63cad38af3.png\" width=\"445\" height=\"566\"><\/figure>\n<p>\u0412 \u0444\u0430\u0439\u043b index.js \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439 \u0438\u043c\u043f\u043e\u0440\u0442 <code>import 'bootstrap\/dist\/css\/bootstrap.min.css'; <\/code><\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/61c\/00c\/fdd\/61c00cfddad9910e10eb03fdf45b047d.png\" width=\"652\" height=\"420\"><\/figure>\n<p>\u0422\u0430\u043a\u0436\u0435 \u0432 \u0444\u0430\u0439\u043b package.json \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0441\u0442\u0440\u043e\u043a\u0443 &#171;proxy&#187;: &#171;http:\/\/host.docker.internal:8181&#187; &#8212; \u0434\u043b\u044f \u043e\u0431\u0449\u0435\u043d\u0438\u044f \u043d\u0430\u0448\u0435\u0433\u043e \u0444\u0440\u043e\u043d\u0442\u0430 \u0441 \u0431\u044d\u043a\u0435\u043d\u0434\u043e\u043c \u043d\u0430 \u043f\u043e\u0440\u0442\u0443 8181 \u0447\u0435\u0440\u0435\u0437 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440.<\/p>\n<figure class=\"\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/efe\/7cb\/94d\/efe7cb94d4d46b3ab8f0f1fba56c3b86.png\" width=\"391\" height=\"345\"><\/figure>\n<\/p>\n<p>\u0414\u043e\u0431\u0430\u0432\u0438\u043c \u0444\u0430\u0439\u043b BookEdit.js \u0434\u043b\u044f \u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043a\u043d\u0438\u0433.<\/p>\n<pre><code class=\"javascript\">import React, { useEffect, useState } from 'react'; import { Link, useNavigate, useParams } from 'react-router-dom'; import { Button, Container, Form, FormGroup, Input, Label } from 'reactstrap'; import AppNavbar from '.\/AppNavbar';  const BookEdit = () =&gt; {     const initialFormState = {         title: '',         author: '',         year: ''     };     const [book, setBook] = useState(initialFormState);     const navigate = useNavigate();     const { id } = useParams();      useEffect(() =&gt; {         if (id !== 'new') {             fetch(`\/api\/v1\/book\/${id}`)                 .then(response =&gt; response.json())                 .then(data =&gt; setBook(data));         }     }, [id, setBook]);      const handleChange = (event) =&gt; {         const { name, value } = event.target          setBook({ ...book, [name]: value })     }      const handleSubmit = async (event) =&gt; {         event.preventDefault();          await fetch(`\/api\/v1\/book${book.id ? `\/${book.id}` : ''}`, {             method: (book.id) ? 'PUT' : 'POST',             headers: {                 'Accept': 'application\/json',                 'Content-Type': 'application\/json'             },             body: JSON.stringify(book)         });         setBook(initialFormState);         navigate('\/books');     }      const title = &lt;h2&gt;{book.id ? 'Edit Book' : 'Add Book'}&lt;\/h2&gt;;      return (&lt;div&gt;             &lt;AppNavbar\/&gt;             &lt;Container&gt;                 {title}                 &lt;Form onSubmit={handleSubmit}&gt;                     &lt;FormGroup&gt;                         &lt;Label for=\"title\"&gt;Title&lt;\/Label&gt;                         &lt;Input type=\"text\" name=\"title\" id=\"title\" value={book.title || ''}                                onChange={handleChange} autoComplete=\"name\"\/&gt;                     &lt;\/FormGroup&gt;                     &lt;FormGroup&gt;                         &lt;Label for=\"author\"&gt;Author&lt;\/Label&gt;                         &lt;Input type=\"text\" name=\"author\" id=\"author\" value={book.author || ''}                                onChange={handleChange} autoComplete=\"address-level1\"\/&gt;                     &lt;\/FormGroup&gt;                     &lt;FormGroup&gt;                         &lt;Label for=\"author\"&gt;Year&lt;\/Label&gt;                         &lt;Input type=\"text\" name=\"year\" id=\"year\" value={book.year || ''}                                onChange={handleChange} autoComplete=\"address-level1\"\/&gt;                     &lt;\/FormGroup&gt;                      &lt;FormGroup&gt;                         &lt;Button color=\"primary\" type=\"submit\"&gt;Save&lt;\/Button&gt;{' '}                         &lt;Button color=\"secondary\" tag={Link} to=\"\/books\"&gt;Cancel&lt;\/Button&gt;                     &lt;\/FormGroup&gt;                 &lt;\/Form&gt;             &lt;\/Container&gt;         &lt;\/div&gt;     ) };  export default BookEdit;<\/code><\/pre>\n<p>\u0414\u043e\u0431\u0430\u0432\u0438\u043c \u0442\u0430\u043a\u0436\u0435 \u0444\u0430\u0439\u043b BookList.js \u0434\u043b\u044f \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u043a\u043d\u0438\u0433.<\/p>\n<pre><code class=\"javascript\">import React, { useEffect, useState } from 'react'; import { Button, ButtonGroup, Container, Table } from 'reactstrap'; import AppNavbar from '.\/AppNavbar'; import { Link } from 'react-router-dom';  const BookList = () =&gt; {      const [books, setBooks] = useState([]);     const [loading, setLoading] = useState(false);      useEffect(() =&gt; {         setLoading(true);          fetch('\/api\/v1\/books')             .then(response =&gt; response.json())             .then(data =&gt; {                 setBooks(data);                 setLoading(false);             })     }, []);      const remove = async (id) =&gt; {         await fetch(`\/api\/v1\/book\/${id}`, {             method: 'DELETE',             headers: {                 'Accept': 'application\/json',                 'Content-Type': 'application\/json'             }         }).then(() =&gt; {             let updatedBooks = [...books].filter(i =&gt; i.id !== id);             setBooks(updatedBooks);         });     }      if (loading) {         return &lt;p&gt;Loading...&lt;\/p&gt;;     }      const bookList = books.map(book =&gt; {         return &lt;tr key={book.id}&gt;             &lt;td style={{whiteSpace: 'nowrap'}}&gt;{book.title}&lt;\/td&gt;             &lt;td&gt; {book.author || ''} &lt;\/td&gt;             &lt;td&gt; {book.year || ''} &lt;\/td&gt;             &lt;td&gt;                 &lt;ButtonGroup&gt;                     &lt;Button size=\"sm\" color=\"primary\" tag={Link} to={\"\/books\/\" + book.id}&gt;Edit&lt;\/Button&gt;                     &lt;Button size=\"sm\" color=\"danger\" onClick={() =&gt; remove(book.id)}&gt;Delete&lt;\/Button&gt;                 &lt;\/ButtonGroup&gt;             &lt;\/td&gt;         &lt;\/tr&gt;     });      return (         &lt;div&gt;             &lt;AppNavbar\/&gt;             &lt;tr&gt;&lt;\/tr&gt;             &lt;Container fluid&gt;                 &lt;div className=\"float-end\" &gt;                     &lt;Button color=\"success\" tag={Link} to=\"\/books\/new\"&gt;Add Book&lt;\/Button&gt;                 &lt;\/div&gt;                 &lt;h3&gt;My Books&lt;\/h3&gt;                 &lt;Table className=\"mt-4\"&gt;                     &lt;thead&gt;                     &lt;tr&gt;                         &lt;th width=\"20%\"&gt;Title&lt;\/th&gt;                         &lt;th width=\"20%\"&gt;Author&lt;\/th&gt;                         &lt;th width=\"20%\"&gt;Year&lt;\/th&gt;                         &lt;th width=\"10%\"&gt;Actions&lt;\/th&gt;                     &lt;\/tr&gt;                     &lt;\/thead&gt;                     &lt;tbody&gt;                     {bookList}                     &lt;\/tbody&gt;                 &lt;\/Table&gt;             &lt;\/Container&gt;         &lt;\/div&gt;     ); };  export default BookList;<\/code><\/pre>\n<p>\u0424\u0430\u0439\u043b App.js \u0438\u0441\u043f\u0440\u0430\u0432\u0438\u043c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u043d\u0438\u0435\u043c.<\/p>\n<pre><code class=\"javascript\">import React from 'react'; import '.\/App.css'; import Home from '.\/Home'; import { BrowserRouter as Router, Route, Routes } from 'react-router-dom'; import BookList from \".\/BookList\"; import BookEdit from \".\/BookEdit\";   const App = () =&gt; {   return (       &lt;Router&gt;         &lt;Routes&gt;           &lt;Route exact path=\"\/\" element={&lt;Home\/&gt;}\/&gt;           &lt;Route path='\/books' exact={true} element={&lt;BookList\/&gt;}\/&gt;           &lt;Route path='\/books\/:id' element={&lt;BookEdit\/&gt;}\/&gt;         &lt;\/Routes&gt;       &lt;\/Router&gt;   ) }  export default App;<\/code><\/pre>\n<p>\u0414\u043e\u0431\u0430\u0432\u0438\u043c \u0444\u0430\u0439\u043b Home.js &#8212; \u044d\u0442\u043e \u0431\u0443\u0434\u0435\u0442 \u043d\u0430\u0448\u0430 \u0441\u0442\u0430\u0440\u0442\u043e\u0432\u0430\u044f \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0430.<\/p>\n<pre><code class=\"javascript\">import React from 'react'; import '.\/App.css'; import AppNavbar from '.\/AppNavbar'; import { Link } from 'react-router-dom'; import { Button, Container } from 'reactstrap';  const Home = () =&gt; {     return (         &lt;div&gt;             &lt;AppNavbar\/&gt;             &lt;Container fluid&gt;                 &lt;Button color=\"link\"&gt;&lt;Link to=\"\/books\"&gt;Manage My Books&lt;\/Link&gt;&lt;\/Button&gt;             &lt;\/Container&gt;         &lt;\/div&gt;     ); }  export default Home;<\/code><\/pre>\n<p>\u0414\u043e\u0431\u0430\u0432\u0438\u043c \u0444\u0430\u0439\u043b AppNavbar.js<\/p>\n<pre><code class=\"javascript\">import React, { useState } from 'react'; import { Collapse, Nav, Navbar, NavbarBrand, NavbarToggler} from 'reactstrap'; import { Link } from 'react-router-dom';  const AppNavbar = () =&gt; {      const [isOpen, setIsOpen] = useState(false);      return (         &lt;Navbar color=\"info\" dark expand=\"md\"&gt;             &lt;NavbarBrand tag={Link} to=\"\/\"&gt;Home&lt;\/NavbarBrand&gt;             &lt;NavbarToggler onClick={() =&gt; { setIsOpen(!isOpen) }}\/&gt;             &lt;Collapse isOpen={isOpen} navbar&gt;                 &lt;Nav className=\"justify-content-end\" style={{width: \"100%\"}} navbar&gt;                  &lt;\/Nav&gt;             &lt;\/Collapse&gt;         &lt;\/Navbar&gt;     ); };  export default AppNavbar;<\/code><\/pre>\n<p>\u041d\u0430\u0448 \u0444\u0440\u043e\u043d\u0442 \u0433\u043e\u0442\u043e\u0432. \u0422\u0435\u043f\u0435\u0440\u044c \u043d\u0430\u043f\u0438\u0448\u0435\u043c Dockerfile \u0434\u043b\u044f \u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u043d\u0430 \u0435\u0433\u043e \u043e\u0441\u043d\u043e\u0432\u0435 \u043e\u0431\u0440\u0430\u0437.<\/p>\n<p>\u0412 \u043a\u043e\u0440\u043d\u0435 \u043f\u0430\u043f\u043a\u0438 frontend \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u0444\u0430\u0439\u043b Dockerfile \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0433\u043e \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u043d\u0438\u044f.<\/p>\n<pre><code class=\"java\">FROM node:18-alpine  WORKDIR \/app  EXPOSE 3000  COPY [\"package.json\", \"package-lock.json*\", \".\/\"]  RUN npm install  COPY . .  CMD [\"npm\", \"start\"]<\/code><\/pre>\n<p>FROM node:18-alpine &#8212; \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0430\u043d\u0438\u0438 \u043a\u0430\u043a\u043e\u0433\u043e \u043e\u0431\u0440\u0430\u0437\u0430 \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0442\u044c \u043d\u0430\u0448\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0441 \u0444\u0440\u043e\u043d\u0442\u043e\u043c. \u0423 \u043d\u0430\u0441 \u0431\u0443\u0434\u0435\u0442 \u043e\u0431\u0440\u0430\u0437 \u0432\u043d\u0443\u0442\u0440\u0438 \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u0431\u0443\u0434\u0435\u0442 node \u0438 npm.<\/p>\n<p>WORKDIR \/app &#8212; \u043a\u0430\u043a \u0432\u0441\u0435\u0433\u0434\u0430 \u0432\u043d\u0443\u0442\u0440\u0438 \u043e\u0431\u0440\u0430\u0437\u0430 \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u043f\u0430\u043f\u043a\u0443 app, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u043c\u044b \u0438 \u0431\u0443\u0434\u0435\u043c \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0442\u044c \u043d\u0430\u0448\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435.<\/p>\n<p>EXPOSE 3000 &#8212; \u043d\u0430 \u043a\u0430\u043a\u043e\u043c \u043f\u043e\u0440\u0442\u0443 \u0431\u0443\u0434\u0435\u0442 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u043d\u0430\u0448 \u0444\u0440\u043e\u043d\u0442.<\/p>\n<p>COPY [&#171;package.json&#187;, &#171;package-lock.json*&#187;, &#171;.\/&#187;] &#8212; \u043a\u043e\u043f\u0438\u0440\u0443\u0435\u043c \u0444\u0430\u0439\u043b\u044b package.json \u0438 package-lock.json \u0432 \u043d\u0430\u0448 \u043e\u0431\u0440\u0430\u0437, \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0449\u0438\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438.<\/p>\n<p>RUN npm install &#8212; &nbsp;\u043a\u043e\u043c\u0430\u043d\u0434\u0430, \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u044e\u0449\u0430\u044f \u043f\u0430\u043a\u0435\u0442\u044b, \u0442\u043e \u0435\u0441\u0442\u044c \u043e\u043d\u0430 \u0441\u043a\u0430\u0447\u0430\u0435\u0442 \u043f\u0430\u043a\u0435\u0442 \u0432 \u043f\u0430\u043f\u043a\u0443 \u043f\u0440\u043e\u0435\u043a\u0442\u0430&nbsp;<code>node_modules<\/code>&nbsp;\u0432 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0438\u0438 \u0441 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0435\u0439 \u0432 \u0444\u0430\u0439\u043b\u0435&nbsp;<code>package.json<\/code>, \u043e\u0431\u043d\u043e\u0432\u0438\u0432 \u0432\u0435\u0440\u0441\u0438\u044e \u043f\u0430\u043a\u0435\u0442\u0430 \u0432\u0435\u0437\u0434\u0435, \u0433\u0434\u0435 \u044d\u0442\u043e \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e (\u0438, \u0432 \u0441\u0432\u043e\u044e \u043e\u0447\u0435\u0440\u0435\u0434\u044c, \u043e\u0431\u043d\u043e\u0432\u0438\u0432&nbsp;<code>package-lock.json<\/code>) <\/p>\n<p>COPY . . &#8212; \u043a\u043e\u043f\u0438\u0440\u0443\u0435\u043c \u0438\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043e\u0434 \u0432 \u043e\u0431\u0440\u0430\u0437.<\/p>\n<p>CMD [&#171;npm&#187;, &#171;start&#187;] &#8212; \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c \u043f\u0440\u043e\u0435\u043a\u0442.<\/p>\n<p>\u041e\u0441\u0442\u0430\u043b\u043e\u0441\u044c \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0432 \u043d\u0430\u0448 docker-copmose.yml \u0444\u0430\u0439\u043b, \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u0447\u0442\u043e\u0431\u044b \u043e\u043d \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u043b \u043e\u0431\u0440\u0430\u0437 \u0441 \u043d\u0430\u0448\u0438\u043c \u0444\u0440\u043e\u043d\u0442\u043e\u043c.<\/p>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c docker-compose \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u0442\u044c \u0442\u0430\u043a.<\/p>\n<pre><code class=\"java\">version: '3.8' services:   client-frontend:     image: frontend:0.0.1     build: .\/frontend     restart: always     ports:       - '3000:3000'     volumes:       - \/app\/node_modules       - .\/frontend:\/app          client-backend:     image: client:0.0.1     build:       context: .       dockerfile: Dockerfile     ports:       - \"8181:8181\"     depends_on:       - service-db     environment:       - SERVER_PORT= 8181       - SPRING_DATASOURCE_URL=jdbc:postgresql:\/\/service-db\/books_db    service-db:     image: postgres:14.7-alpine     environment:       POSTGRES_USER: username       POSTGRES_PASSWORD: password     ports:       - \"15432:5432\"     volumes:       - .\/infrastructure\/db\/create_db.sql:\/docker-entrypoint-initdb.d\/create_db.sql       - db-data:\/var\/lib\/postgresql\/data     restart: unless-stopped    pgadmin:     container_name: pgadmin4_container     image: dpage\/pgadmin4:7     restart: always     environment:       PGADMIN_DEFAULT_EMAIL: admin@admin.com       PGADMIN_DEFAULT_PASSWORD: root     ports:       - \"5050:80\"     volumes:       - pgadmin-data:\/var\/lib\/pgadmin  volumes:   db-data:   pgadmin-data:<\/code><\/pre>\n<p>\u041c\u044b \u0434\u043e\u0431\u0430\u0432\u0438\u043b\u0438 \u043d\u043e\u0432\u044b\u0439 \u0441\u0435\u0440\u0432\u0438\u0441 &#8212; \u0442\u043e \u0435\u0441\u0442\u044c \u043d\u043e\u0432\u044b\u0439 \u043e\u0431\u0440\u0430\u0437 \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0430\u043d\u0438\u0438 \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u0441\u044f \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440.<\/p>\n<pre><code class=\"java\">  client-frontend:     image: frontend:0.0.1<\/code><\/pre>\n<pre><code class=\"java\"> build: .\/frontend<\/code><\/pre>\n<p>\u0437\u0434\u0435\u0441\u044c \u043c\u044b \u0443\u043a\u0430\u0437\u0430\u043b\u0438, \u0447\u0442\u043e \u0445\u043e\u0442\u0438\u043c \u0447\u0442\u043e\u0431\u044b docker \u0441\u0431\u0438\u043b\u0434\u0438\u043b \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440 \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0430\u043d\u0438\u0438 \u043e\u0431\u0440\u0430\u0437\u0430, \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u043e\u0433\u043e \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0430\u043d\u0438\u0438 Dockerfile \u0440\u0430\u0441\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u043d\u043e\u0433\u043e \u0432 \u043f\u0430\u043f\u043a\u0435 frontend.<\/p>\n<pre><code class=\"java\">    ports:       - '3000:3000'<\/code><\/pre>\n<p>\u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c \u043d\u0430\u0448 \u0444\u0440\u043e\u043d\u0442 \u043d\u0430 \u043f\u043e\u0440\u0442\u0443 3000.<\/p>\n<p>\u0424\u0430\u0439\u043b docker-compose \u043f\u043e\u043b\u043d\u043e\u0441\u0442\u044c\u044e \u0433\u043e\u0442\u043e\u0432. \u0417\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c.<\/p>\n<p>\u0421\u043d\u043e\u0432\u0430 \u0432\u0445\u043e\u0434\u0438\u043c \u0432 \u0442\u0435\u0440\u043c\u0438\u043d\u0430\u043b \u0432 \u043f\u0430\u043f\u043a\u0443 \u0441 \u043d\u0430\u0448\u0438\u043c \u043f\u0440\u043e\u0435\u043a\u0442\u043e\u043c \u0438 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c \u043a\u043e\u043c\u0430\u043d\u0434\u0443 docker-compose up<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/f6b\/f4f\/b69\/f6bf4fb6980e504638d0471723601d0b.png\" width=\"911\" height=\"111\"><\/figure>\n<p>\u0412\u0441\u0435 \u043d\u0430\u0448\u0438 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u044b \u0434\u043e\u043b\u0436\u043d\u044b \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/efd\/27a\/fb4\/efd27afb49003f03c80f734e61f24b0f.png\" width=\"1600\" height=\"361\"><\/figure>\n<p>\u041f\u0435\u0440\u0435\u0445\u043e\u0434\u0438\u043c \u0432 \u0431\u0440\u0430\u0443\u0437\u0435\u0440 \u0438 \u0437\u0430\u0445\u043e\u0434\u0438\u043c \u043d\u0430 \u0430\u0434\u0440\u0435\u0441 <a href=\"http:\/\/localhost:3000\/\" rel=\"noopener noreferrer nofollow\">http:\/\/localhost:3000<\/a>\/<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/b10\/71e\/28c\/b1071e28c1589ad8eb75b3fd6e3433eb.png\" width=\"933\" height=\"287\"><\/figure>\n<p>\u041d\u0430\u0436\u0438\u043c\u0430\u0435\u043c \u043d\u0430 Manage My Books \u0438 \u043c\u044b \u0434\u043e\u043b\u0436\u043d\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0432\u0441\u0435 \u043d\u0430\u0448\u0438 \u043a\u043d\u0438\u0433\u0438.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/159\/df1\/778\/159df1778c7693eb2fc10e59ef0922c0.png\" width=\"1899\" height=\"581\"><\/figure>\n<p>\u0422\u0430\u043a\u0436\u0435 \u043f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u0440\u0443\u0435\u043c \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043a\u043d\u0438\u0433\u0438.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/a4b\/659\/e3b\/a4b659e3bb5d8f69c7fb522a83bd5bb3.png\" width=\"1573\" height=\"467\"><\/figure>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/391\/2fd\/826\/3912fd82612cd23bdd985e13fc20eec1.png\" width=\"1906\" height=\"608\"><\/figure>\n<p>\u0423\u0434\u0430\u043b\u0435\u043d\u0438\u0435 \u043a\u043d\u0438\u0433\u0438.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/060\/045\/b85\/060045b85645e3248b571db8dedc693c.png\" width=\"1900\" height=\"553\"><\/figure>\n<p>\u0418 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0435 \u043a\u043d\u0438\u0433\u0438<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/98d\/7ee\/36d\/98d7ee36d2362d4a85911c86aed457ad.png\" width=\"1651\" height=\"545\"><\/figure>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/1be\/934\/a26\/1be934a26dbcb046a8606a7bbdb4b45f.png\" width=\"1886\" height=\"591\"><\/figure>\n<p>\u0412\u0441\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442. \u0422\u0430\u043a\u0436\u0435 \u043c\u043e\u0436\u0435\u0442\u0435 \u0443\u0431\u0435\u0434\u0438\u0442\u044c\u0441\u044f, \u0447\u0442\u043e \u0432\u0441\u0435 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u043f\u0435\u0440\u0435\u043d\u043e\u0441\u044f\u0442\u0441\u044f \u0432 \u043d\u0430\u0448\u0443 \u0431\u0430\u0437\u0443 \u0434\u0430\u043d\u043d\u044b\u0445.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/f4a\/0bb\/c69\/f4a0bbc694f60ed686ca359ebc723d1c.png\" width=\"809\" height=\"272\"><\/figure>\n<p>\u0412\u043e\u0442 \u0438 \u043c\u044b \u043f\u043e\u0434\u043e\u0448\u043b\u0438 \u043a \u043a\u043e\u043d\u0446\u0443 \u0441 \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u0435\u043c \u043d\u0430\u0448\u0435\u0433\u043e \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u043e\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043f\u043e \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044e \u043a\u043d\u0438\u0433 \u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b\u0438 \u043f\u0440\u0438 \u044d\u0442\u043e\u043c docker-compose \u0444\u0430\u0439\u043b\u0430.<\/p>\n<p>\u0414\u0430\u043d\u043d\u0430\u044f \u0441\u0442\u0430\u0442\u044c\u044f \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u0430\u0441\u044c \u043e\u0447\u0435\u043d\u044c \u0431\u043e\u043b\u044c\u0448\u043e\u0439. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e \u0432\u0441\u0435\u043c \u0421\u041f\u0410\u0421\u0418\u0411\u041e, \u043a\u0442\u043e \u0434\u043e\u0447\u0438\u0442\u0430\u043b \u0434\u043e \u043a\u043e\u043d\u0446\u0430.<\/p>\n<p>\u0412\u0441\u0435\u043c \u043f\u043e\u043a\u0430!!!<\/p>\n<p>P.S.<\/p>\n<p>\u0412\u043e\u0442 \u0441\u0441\u044b\u043b\u043a\u0438 \u043d\u0430 \u043f\u0440\u043e\u0435\u043a\u0442 \u043d\u0430 \u0433\u0438\u0442\u0445\u0430\u0431\u0435: <a href=\"https:\/\/github.com\/mista1984gmail\/docker-my-library-app\" rel=\"noopener noreferrer nofollow\">\u0431\u044d\u043a\u0435\u043d\u0434<\/a> \u0438 <a href=\"https:\/\/github.com\/mista1984gmail\/docker-my-library-app-frontend\" rel=\"noopener noreferrer nofollow\">\u0444\u0440\u043e\u043d\u0442\u044d\u043d\u0434<\/a>.<\/p>\n<\/div>\n<\/div>\n<p> <!----> <!----><\/div>\n<p> <!----> <!----><br \/> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/735274\/\"> https:\/\/habr.com\/ru\/articles\/735274\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<p>\u0412\u0441\u0435\u043c \u043f\u0440\u0438\u0432\u0435\u0442!<\/p>\n<p>\u0412 \u0434\u0430\u043d\u043d\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u043f\u043e\u043f\u0440\u043e\u0431\u0443\u044e \u0440\u0430\u0441\u0441\u043a\u0430\u0437\u0430\u0442\u044c \u043a\u0430\u043a \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c docker-compose.yml \u0434\u043b\u044f \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u043f\u0440\u043e\u0441\u0442\u043e\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u0431\u0443\u0434\u0435\u0442 \u0441\u043e\u0441\u0442\u043e\u044f\u0442\u044c \u0438\u0437 \u0444\u0440\u043e\u043d\u0442\u0430 \u043d\u0430 React, \u0431\u044d\u043a\u0435\u043d\u0434\u0430 \u043d\u0430 Spring Boot, \u0442\u0430\u043a\u0436\u0435 \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0431\u0430\u0437\u0443 \u0434\u0430\u043d\u043d\u044b\u0445 PostgreSQL, \u0430 \u0434\u043b\u044f \u043f\u0440\u043e\u0441\u043c\u043e\u0442\u0440\u0430 \u0434\u0430\u043d\u043d\u044b\u0445 \u0432 \u0431\u0430\u0437\u0435 pgAdmin. <\/p>\n<p>\u0414\u0430\u043d\u043d\u0430\u044f \u0441\u0442\u0430\u0442\u044c\u044f \u0431\u0443\u0434\u0435\u0442 \u043f\u043e\u043b\u0435\u0437\u043d\u0430 \u043d\u0430\u0447\u0438\u043d\u0430\u044e\u0449\u0438\u043c \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u043e\u043c \u0440\u0430\u0437\u043e\u0431\u0440\u0430\u0442\u044c\u0441\u044f \u0434\u043b\u044f \u0447\u0435\u0433\u043e \u043d\u0443\u0436\u0435\u043d docker-compose.yml \u0444\u0430\u0439\u043b \u0438 \u043a\u0430\u043a \u043d\u0430\u0447\u0430\u0442\u044c \u0441 \u043d\u0438\u043c \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c.<\/p>\n<p>\u0414\u043b\u044f \u043f\u043e\u043d\u0438\u043c\u0430\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0438 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b \u043c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u044b\u0435 \u0437\u043d\u0430\u043d\u0438\u044f \u043f\u043e \u0434\u043e\u043a\u0435\u0440\u0443, \u0430 \u0442\u0430\u043a\u0436\u0435 \u0434\u043b\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u043f\u0440\u0438\u043c\u0435\u0440\u043e\u0432 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u043d\u044b\u0439 Docker \u0443 \u0432\u0430\u0441 \u043d\u0430 \u043a\u043e\u043c\u043f\u044c\u044e\u0442\u0435\u0440\u0435.<\/p>\n<p>\u041f\u043b\u0430\u043d, \u0447\u0442\u043e \u043c\u044b \u0445\u043e\u0442\u0438\u043c \u0441\u0434\u0435\u043b\u0430\u0442\u044c:<\/p>\n<ol>\n<li>\n<p>\u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u0431\u044d\u043a\u0435\u043d\u0434 \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c Spring Boot (\u044d\u0442\u043e \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u043e\u0441\u0442\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u043f\u043e \u0440\u0430\u0431\u043e\u0442\u0435 \u0441 \u043a\u043d\u0438\u0433\u0430\u043c\u0438, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c CRUD \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438 \u043d\u0430\u0434 \u043d\u0438\u043c\u0438);<\/p>\n<\/li>\n<li>\n<p>\u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u0431\u0430\u0437\u0443 \u0434\u0430\u043d\u043d\u044b\u0445 PostgreSQL \u0434\u043b\u044f \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u044b\u0445;<\/p>\n<\/li>\n<li>\n<p>\u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c pgAdmin \u0434\u043b\u044f \u043f\u0440\u043e\u0441\u043c\u043e\u0442\u0440\u0430 \u0434\u0430\u043d\u043d\u044b\u0445 \u0432 \u043d\u0430\u0448\u0435\u0439 \u0431\u0430\u0437\u0435 PostgreSQL;<\/p>\n<\/li>\n<li>\n<p>\u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u043f\u0440\u043e\u0441\u0442\u043e\u0439 \u0444\u0440\u043e\u043d\u0442 \u043d\u0430 React \u0434\u043b\u044f \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f, \u0432\u0432\u043e\u0434\u0430, \u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0438      \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f \u043a\u043d\u0438\u0433;<\/p>\n<\/li>\n<li>\n<p>\u043f\u043e\u043c\u0435\u0441\u0442\u0438\u0442\u044c \u0432\u0441\u0435 \u044d\u0442\u043e \u0432 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u044b \u0438 &#171;\u0437\u0430\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c&#187;, \u0434\u043b\u044f \u0447\u0435\u0433\u043e \u0438 \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c docker-compose.yml \u0444\u0430\u0439\u043b.<\/p>\n<\/li>\n<\/ol>\n<p>\u041d\u0430\u0447\u043d\u0435\u043c \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u0441 \u0442\u0435\u043e\u0440\u0438\u0438. Docker-compose \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0434\u043b\u044f \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u043c\u0438 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u0430\u043c\u0438, \u0432\u0445\u043e\u0434\u044f\u0449\u0438\u043c\u0438 \u0432 \u0441\u043e\u0441\u0442\u0430\u0432 \u043e\u0434\u043d\u043e\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f, \u044d\u0442\u043e \u043c\u043e\u0433\u0443\u0442 \u0431\u044b\u0442\u044c \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u044b \u0441\u043e\u0437\u0434\u0430\u043d\u043d\u044b\u0435 \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 \u043e\u0431\u0440\u0430\u0437\u043e\u0432 \u043d\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f (\u0443 \u043d\u0430\u0441 \u044d\u0442\u043e \u0431\u0443\u0434\u0443\u0442 \u0434\u0432\u0430 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u044b: \u043e\u0434\u0438\u043d \u0441 \u0431\u044d\u043a\u0435\u043d\u0434\u043e\u043c, \u0430 \u0432\u0442\u043e\u0440\u043e\u0439 \u0441 \u0444\u0440\u043e\u043d\u0442\u0435\u043d\u0434\u043e\u043c), \u0430 \u0442\u0430\u043a\u0436\u0435 \u0441\u0442\u043e\u0440\u043e\u043d\u043d\u0438\u0435 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u044b \u0441\u043a\u0430\u0447\u0430\u043d\u043d\u044b\u0435, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0441 docker hub (\u0443 \u043d\u0430\u0441 \u044d\u0442\u043e \u0431\u0443\u0434\u0435\u0442 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u044b \u0441 \u0431\u0430\u0437\u043e\u0439 \u0434\u0430\u043d\u043d\u044b\u0445 \u0438 \u0433\u0440\u0430\u0444\u0438\u0447\u0435\u0441\u043a\u0438\u043c \u043a\u043b\u0438\u0435\u043d\u0442\u043e\u043c \u043a \u043d\u0435\u0439).<\/p>\n<p>\u0418 \u043a\u043e\u043d\u0435\u0447\u043d\u043e \u0436\u0435 \u043b\u043e\u0433\u0438\u0447\u043d\u044b\u0439 \u0432\u043e\u043f\u0440\u043e\u0441: \u0434\u043b\u044f \u0447\u0435\u0433\u043e \u0432\u0441\u0435 \u044d\u0442\u043e? \u0437\u0430\u0447\u0435\u043c?<\/p>\n<p>\u041e\u0442\u0432\u0435\u0442 \u043e\u0447\u0435\u043d\u044c \u043f\u0440\u043e\u0441\u0442, Docker compose \u043d\u0443\u0436\u0435\u043d \u0434\u043b\u044f \u0431\u044b\u0441\u0442\u0440\u043e\u0433\u043e \u0440\u0430\u0437\u0432\u0435\u0440\u0442\u044b\u0432\u0430\u043d\u0438\u044f \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043f\u0435\u0440\u0435\u043d\u043e\u0441 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043d\u0430 \u0434\u0440\u0443\u0433\u043e\u0439 \u0441\u0435\u0440\u0432\u0435\u0440 \u0437\u0430\u0439\u043c\u0435\u0442 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043c\u0438\u043d\u0443\u0442, \u0442\u0430\u043a\u0436\u0435 \u0432 \u0441\u043e\u0447\u0435\u0442\u0430\u043d\u0438\u0438 \u0441 kubernetes &#8212; \u0434\u0430\u0435\u0442 \u043f\u0440\u0435\u0432\u043e\u0441\u0445\u043e\u0434\u043d\u044b\u0435 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u044b \u043f\u043e \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0430\u0446\u0438\u0438 \u0440\u0430\u0437\u0432\u0435\u0440\u0442\u044b\u0432\u0430\u043d\u0438\u044f, \u043c\u0430\u0441\u0448\u0442\u0430\u0431\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0438 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0446\u0438\u0438 \u0440\u0430\u0431\u043e\u0442\u044b \u043d\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0432 \u0443\u0441\u043b\u043e\u0432\u0438\u044f\u0445 \u043a\u043b\u0430\u0441\u0442\u0435\u0440\u0430.<\/p>\n<p>\u0418\u0442\u0430\u043a \u043f\u0440\u0438\u0441\u0442\u0443\u043f\u0438\u043c. \u0412\u043d\u0430\u0447\u0430\u043b\u0435 \u043d\u0430\u043f\u0438\u0448\u0435\u043c \u043d\u0430\u0448 \u0431\u044d\u043a\u0435\u043d\u0434, \u0434\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0438\u0434\u0435\u0442 \u043d\u0430 <a href=\"https:\/\/start.spring.io\/\" rel=\"noopener noreferrer nofollow\">https:\/\/start.spring.io\/<\/a> \u0438 \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u043f\u0440\u043e\u0435\u043a\u0442.<\/p>\n<figure class=\"full-width\"><\/figure>\n<p>\u041f\u043e\u0434\u0440\u043e\u0431\u043d\u043e \u043d\u0435 \u0431\u0443\u0434\u0443 \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0442\u044c \u043a\u0430\u0436\u0434\u044b\u0439 \u0448\u0430\u0433 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438, \u0431\u0443\u0434\u0443 \u043e\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0442\u044c\u0441\u044f \u0442\u043e\u043b\u044c\u043a\u043e \u043d\u0430 \u0441\u0430\u043c\u044b\u0445 \u0433\u043b\u0430\u0432\u043d\u044b\u0445 \u043c\u043e\u043c\u0435\u043d\u0442\u0430\u0445, \u0432 \u043b\u044e\u0431\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435, \u0432\u0435\u0441\u044c \u043a\u043e\u0434 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u0431\u0443\u0434\u0435\u0442 \u0434\u043e\u0441\u0442\u0443\u043f\u0435\u043d \u043d\u0430 \u0433\u0438\u0442\u0445\u0430\u0431\u0435, \u0441\u0441\u044b\u043b\u043a\u0430 \u0432 \u043a\u043e\u043d\u0446\u0435 \u0441\u0442\u0430\u0442\u044c\u0438.<\/p>\n<p>\u041e\u0442\u043a\u0440\u044b\u0432\u0430\u0435\u043c \u043d\u0430\u0448 \u043f\u0440\u043e\u0435\u043a\u0442 \u0432 \u0441\u0440\u0435\u0434\u0435 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0438 \u0432\u043d\u0430\u0447\u0430\u043b\u0435 \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u043c\u043e\u0434\u0438\u0444\u0438\u0446\u0438\u0440\u0443\u0435\u043c \u043d\u0430\u0448 pom.xml \u0444\u0430\u0439\u043b.<\/p>\n<pre><code class=\"java\">&lt;?xml version=\"1.0\" encoding=\"UTF-8\"?&gt; &lt;project xmlns=\"http:\/\/maven.apache.org\/POM\/4.0.0\" xmlns:xsi=\"http:\/\/www.w3.org\/2001\/XMLSchema-instance\" xsi:schemaLocation=\"http:\/\/maven.apache.org\/POM\/4.0.0 https:\/\/maven.apache.org\/xsd\/maven-4.0.0.xsd\"&gt; &lt;modelVersion&gt;4.0.0&lt;\/modelVersion&gt; &lt;parent&gt; &lt;groupId&gt;org.springframework.boot&lt;\/groupId&gt; &lt;artifactId&gt;spring-boot-starter-parent&lt;\/artifactId&gt; &lt;version&gt;2.7.11&lt;\/version&gt; &lt;relativePath\/&gt; &lt;!-- lookup parent from repository --&gt; &lt;\/parent&gt; &lt;groupId&gt;com.example&lt;\/groupId&gt; &lt;artifactId&gt;my-library-app&lt;\/artifactId&gt; &lt;version&gt;0.0.1-SNAPSHOT&lt;\/version&gt; &lt;name&gt;my-library-app&lt;\/name&gt; &lt;description&gt;Demo project for Spring Boot&lt;\/description&gt; &lt;properties&gt; &lt;java.version&gt;17&lt;\/java.version&gt; &lt;org.mapstruct.version&gt;1.4.2.Final&lt;\/org.mapstruct.version&gt; &lt;lombok-mapstruct-binding.version&gt;0.2.0&lt;\/lombok-mapstruct-binding.version&gt; &lt;maven.compiler.plugin.version&gt;3.5.1&lt;\/maven.compiler.plugin.version&gt; &lt;\/properties&gt; &lt;dependencies&gt; &lt;!--JPA--&gt; &lt;dependency&gt; &lt;groupId&gt;org.springframework.boot&lt;\/groupId&gt; &lt;artifactId&gt;spring-boot-starter-data-jpa&lt;\/artifactId&gt; &lt;\/dependency&gt; &lt;!--WEB--&gt; &lt;dependency&gt; &lt;groupId&gt;org.springframework.boot&lt;\/groupId&gt; &lt;artifactId&gt;spring-boot-starter-web&lt;\/artifactId&gt; &lt;\/dependency&gt; &lt;!--LIQUIBASE--&gt; &lt;dependency&gt; &lt;groupId&gt;org.liquibase&lt;\/groupId&gt; &lt;artifactId&gt;liquibase-core&lt;\/artifactId&gt; &lt;\/dependency&gt; &lt;!--DEV-TOOLS--&gt; &lt;dependency&gt; &lt;groupId&gt;org.springframework.boot&lt;\/groupId&gt; &lt;artifactId&gt;spring-boot-devtools&lt;\/artifactId&gt; &lt;scope&gt;runtime&lt;\/scope&gt; &lt;optional&gt;true&lt;\/optional&gt; &lt;\/dependency&gt; &lt;!--DATABASE--&gt; &lt;dependency&gt; &lt;groupId&gt;org.postgresql&lt;\/groupId&gt; &lt;artifactId&gt;postgresql&lt;\/artifactId&gt; &lt;scope&gt;runtime&lt;\/scope&gt; &lt;\/dependency&gt; &lt;!--LOMBOK--&gt; &lt;dependency&gt; &lt;groupId&gt;org.projectlombok&lt;\/groupId&gt; &lt;artifactId&gt;lombok&lt;\/artifactId&gt; &lt;optional&gt;true&lt;\/optional&gt; &lt;\/dependency&gt; &lt;!--MAPPING--&gt; &lt;dependency&gt; &lt;groupId&gt;org.mapstruct&lt;\/groupId&gt; &lt;artifactId&gt;mapstruct&lt;\/artifactId&gt; &lt;version&gt;${org.mapstruct.version}&lt;\/version&gt; &lt;\/dependency&gt; &lt;dependency&gt; &lt;groupId&gt;org.projectlombok&lt;\/groupId&gt; &lt;artifactId&gt;lombok-mapstruct-binding&lt;\/artifactId&gt; &lt;version&gt;${lombok-mapstruct-binding.version}&lt;\/version&gt; &lt;\/dependency&gt; &lt;!--TESTING--&gt; &lt;dependency&gt; &lt;groupId&gt;org.springframework.boot&lt;\/groupId&gt; &lt;artifactId&gt;spring-boot-starter-test&lt;\/artifactId&gt; &lt;scope&gt;test&lt;\/scope&gt; &lt;\/dependency&gt; &lt;\/dependencies&gt;  &lt;build&gt; &lt;plugins&gt; &lt;plugin&gt; &lt;groupId&gt;org.springframework.boot&lt;\/groupId&gt; &lt;artifactId&gt;spring-boot-maven-plugin&lt;\/artifactId&gt; &lt;configuration&gt; &lt;excludes&gt; &lt;exclude&gt; &lt;groupId&gt;org.projectlombok&lt;\/groupId&gt; &lt;artifactId&gt;lombok&lt;\/artifactId&gt; &lt;\/exclude&gt; &lt;\/excludes&gt; &lt;\/configuration&gt; &lt;\/plugin&gt; &lt;plugin&gt; &lt;groupId&gt;org.apache.maven.plugins&lt;\/groupId&gt; &lt;artifactId&gt;maven-compiler-plugin&lt;\/artifactId&gt; &lt;version&gt;${maven.compiler.plugin.version}&lt;\/version&gt; &lt;configuration&gt; &lt;source&gt;17&lt;\/source&gt; &lt;target&gt;17&lt;\/target&gt; &lt;annotationProcessorPaths&gt; &lt;path&gt; &lt;groupId&gt;org.mapstruct&lt;\/groupId&gt; &lt;artifactId&gt;mapstruct-processor&lt;\/artifactId&gt; &lt;version&gt;${org.mapstruct.version}&lt;\/version&gt; &lt;\/path&gt; &lt;path&gt; &lt;groupId&gt;org.projectlombok&lt;\/groupId&gt; &lt;artifactId&gt;lombok&lt;\/artifactId&gt; &lt;version&gt;${lombok.version}&lt;\/version&gt; &lt;\/path&gt; &lt;path&gt; &lt;groupId&gt;org.projectlombok&lt;\/groupId&gt; &lt;artifactId&gt;lombok-mapstruct-binding&lt;\/artifactId&gt; &lt;version&gt;${lombok-mapstruct-binding.version}&lt;\/version&gt; &lt;\/path&gt; &lt;\/annotationProcessorPaths&gt; &lt;\/configuration&gt; &lt;\/plugin&gt; &lt;\/plugins&gt; &lt;\/build&gt;  &lt;\/project&gt;<\/code><\/pre>\n<p>\u0418\u0437 \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u0445 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439:<\/p>\n<p>postgresql &#8212; \u0434\u043e\u0441\u0442\u0443\u043f \u043a \u0431\u0430\u0437\u0435 \u0434\u0430\u043d\u043d\u044b\u0445;<\/p>\n<p>liquibase &#8212; \u0431\u0443\u0434\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0434\u043b\u044f &#171;\u043d\u0430\u043a\u0430\u0442\u044b\u0432\u0430\u043d\u0438\u044f&#187; \u0442\u0430\u0431\u043b\u0438\u0446 \u0432 \u0431\u0430\u0437\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 \u0438 \u0437\u0430\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u043f\u0435\u0440\u0432\u043e\u043d\u0430\u0447\u0430\u043b\u044c\u043d\u044b\u043c\u0438 \u0434\u0430\u043d\u043d\u044b\u043c\u0438.<\/p>\n<p>mapstruct &#8212; \u0434\u043b\u044f \u043c\u0430\u043f\u043f\u0438\u043d\u0433\u0430 \u043d\u0430\u0448\u0438\u0445 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0435\u0439.<\/p>\n<p>lombok &#8212; \u0434\u043b\u044f \u0441\u043e\u043a\u0440\u0430\u0449\u0435\u043d\u0438\u044f \u0448\u0430\u0431\u043b\u043e\u043d\u043d\u043e\u0433\u043e \u043a\u043e\u0434\u0430 \u0447\u0435\u0440\u0435\u0437 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 \u0430\u043d\u043d\u043e\u0442\u0430\u0446\u0438\u0439.<\/p>\n<p>\u041d\u0430\u0447\u043d\u0435\u043c \u0441 \u043a\u043e\u0434\u0430 \u043d\u0430\u0448\u0435\u0439 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0438, \u043a\u0430\u043a \u044f \u0443\u043f\u043e\u043c\u0438\u043d\u0430\u043b \u0440\u0430\u043d\u044c\u0448\u0435 &#8212; \u044d\u0442\u043e \u0431\u0443\u0434\u0435\u0442 \u043a\u043d\u0438\u0433\u0430 (Book). \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043a\u043b\u0430\u0441\u0441 Book.<\/p>\n<pre><code class=\"java\">@Data @NoArgsConstructor @AllArgsConstructor @Entity @Table(name = \"book\") public class Book {      @Id     @Column(name = \"id\")     @GeneratedValue(strategy = GenerationType.IDENTITY)     private Long id;     @Column(name = \"title\")     private String title;      @Column(name = \"author\")     private String author;      @Column(name = \"year\")     private int year;  } <\/code><\/pre>\n<p>\u0414\u0430\u043b\u0435\u0435 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 BookRepository \u0438 \u0443\u043d\u0430\u0441\u043b\u0435\u0434\u0443\u0435\u043c \u0435\u0433\u043e \u043e\u0442 JpaRepository &#8212; \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u043d\u0430\u0431\u043e\u0440 \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0445 \u043c\u0435\u0442\u043e\u0434\u043e\u0432 JPA \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0411\u0414.<\/p>\n<pre><code class=\"java\">public interface BookRepository extends JpaRepository&lt;Book, Long&gt; { } <\/code><\/pre>\n<p>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043a\u043b\u0430\u0441\u0441 BookDto, \u0442\u0430\u043a \u043a\u0430\u043a \u0443\u0440\u043e\u0432\u0435\u043d\u044c \u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u043d\u0435 \u0434\u043e\u043b\u0436\u0435\u043d \u043d\u0430\u043f\u0440\u044f\u043c\u0443\u044e \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0441 \u043d\u0430\u0448\u0435\u0439 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u044c\u044e Book (\u043c\u043e\u0436\u0435\u0442 \u0432 \u0434\u0430\u043d\u043d\u043e\u043c \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u044d\u0442\u043e \u0438 \u043d\u0435 \u043d\u0443\u0436\u043d\u043e, \u0442\u0430\u043a \u043a\u0430\u043a \u044d\u0442\u043e \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u0443\u0441\u043b\u043e\u0436\u043d\u044f\u0435\u0442 \u043a\u043e\u0434).<\/p>\n<pre><code class=\"java\">@Data @NoArgsConstructor @AllArgsConstructor public class BookDto {      private Long id;     private String title;     private String author;     private int year; } <\/code><\/pre>\n<p>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 BookMapper \u0434\u043b\u044f \u043c\u0430\u043f\u043f\u0438\u043d\u0433\u0430 \u043d\u0430\u0448\u0438\u0445 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0435\u0439 \u0438\u0437 Book \u0438 BookDto \u0438 \u043e\u0431\u0440\u0430\u0442\u043d\u043e, \u0437\u0434\u0435\u0441\u044c \u043c\u044b \u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c mapstruct.<\/p>\n<pre><code class=\"java\">@Mapper(componentModel = \"spring\") public interface BookMapper {     Book dtoToModel(BookDto bookDto);      BookDto modelToDto(Book book);      List&lt;BookDto&gt; toListDto(List&lt;Book&gt; books); } <\/code><\/pre>\n<p>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0435\u0449\u0435 \u043e\u0434\u0438\u043d \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 BookService, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u0438 \u043d\u0430\u043f\u0438\u0448\u0435\u043c \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u0435 \u043c\u0435\u0442\u043e\u0434\u044b \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u043d\u0430\u0448\u0438\u043c\u0438 \u043a\u043d\u0438\u0433\u0430\u043c\u0438.<\/p>\n<pre><code class=\"java\">public interface BookService {     List&lt;BookDto&gt; findAll ();     BookDto findById( Long id);     BookDto save (BookDto book);     void deleteById (Long id); } <\/code><\/pre>\n<p>\u0438 \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u043c \u044d\u0442\u0438 \u043c\u0435\u0442\u043e\u0434\u044b \u0432 \u043a\u043b\u0430\u0441\u0441\u0435 BookServiceImpl.<\/p>\n<pre><code class=\"java\">@Service @RequiredArgsConstructor @Transactional(readOnly = true) public class BookServiceImpl implements BookService {      private final BookRepository bookRepository;     private final BookMapper bookMapper;      @Override     public List&lt;BookDto&gt; findAll() {         return bookMapper.toListDto(bookRepository.findAll());     }      @Override     public BookDto findById(Long id) {         return Optional.of(getById(id)).map(bookMapper::modelToDto).get();     }      @Override     @Transactional     public BookDto save(BookDto book) {         return bookMapper.modelToDto(bookRepository.save(                 bookMapper.dtoToModel(book)));     }      @Override     @Transactional     public void deleteById(Long id) {         var book = getById(id);         bookRepository.delete(book);     }      private Book getById(Long id) {         return bookRepository.findById(id)                 .orElseThrow(() -&gt; new RuntimeException(                         \"Book with id: \" + id + \" not found\"));     } } <\/code><\/pre>\n<p>\u041f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u0439 \u043a\u043b\u0430\u0441\u0441, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043c\u044b \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0437\u0434\u0435\u0441\u044c &#8212; \u044d\u0442\u043e \u0431\u0443\u0434\u0435\u0442 BooksController &#8212; \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0438 \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0434\u0430\u0432\u0430\u0442\u044c \u043d\u0430\u0448\u0438 \u044d\u043d\u0434\u043f\u043e\u0439\u043d\u0442\u044b \u0434\u043b\u044f \u043f\u0440\u043e\u0441\u043c\u043e\u0442\u0440\u0430, \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u044f, \u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0438 \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f \u043d\u0430\u0448\u0438\u0445 \u043a\u043d\u0438\u0433.<\/p>\n<pre><code class=\"java\">@RestController @RequestMapping(\"api\/v1\") @RequiredArgsConstructor public class BooksController {     private final BookService bookService;      @GetMapping(\"\/books\")     public List&lt;BookDto&gt; allBooks() {         return bookService.findAll();     }      @GetMapping(\"\/book\/{id}\")     @ResponseStatus(HttpStatus.OK)     public ResponseEntity&lt;BookDto&gt; getBook(@PathVariable Long id) {         return ResponseEntity.ok().body(bookService.findById(id));     }      @PostMapping(\"\/book\")     public ResponseEntity&lt;BookDto&gt; createBook( @RequestBody BookDto book) throws URISyntaxException {         BookDto result = bookService.save(book);         return ResponseEntity.created(new URI(\"\/api\/v1\/books\/\" + result.getId()))                 .body(result);     }      @PutMapping(\"\/book\/{id}\")     @ResponseStatus(HttpStatus.OK)     public ResponseEntity&lt;BookDto&gt; updateBook( @PathVariable Long id, @RequestBody BookDto book) {         return ResponseEntity.ok().body(bookService.save(book));     }      @DeleteMapping(\"\/book\/{id}\")     public ResponseEntity&lt;?&gt; deleteBook(@PathVariable Long id) {         bookService.deleteById(id);         return ResponseEntity.ok().build();     }  } <\/code><\/pre>\n<p>\u0421\u0435\u0439\u0447\u0430\u0441 \u0437\u0430\u0439\u043c\u0435\u043c\u0441\u044f \u043d\u0430\u0448\u0438\u043c application.properties \u0444\u0430\u0439\u043b\u043e\u043c \u0438 \u043f\u0440\u043e\u043f\u0438\u0448\u0435\u043c \u0442\u0430\u043c \u0434\u043e\u0441\u0442\u0443\u043f \u043a \u043d\u0430\u0448\u0435\u0439 \u0431\u0430\u0437\u0435 \u0434\u0430\u043d\u043d\u044b\u0445, \u0432\u043a\u043b\u044e\u0447\u0438\u043c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 liquibase,<\/p>\n<\/div>\n<\/div>\n<\/div>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-347494","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/347494","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=347494"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/347494\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=347494"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=347494"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=347494"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}