{"id":329725,"date":"2022-02-17T09:00:22","date_gmt":"2022-02-17T09:00:22","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=329725"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=329725","title":{"rendered":"<span>\u041a\u0430\u043a \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0433\u043e\u043b\u043e\u0441\u043e\u0432\u043e\u0439 \u043d\u0430\u0432\u044b\u043a \u0434\u043b\u044f \u042f\u043d\u0434\u0435\u043a\u0441.\u0410\u043b\u0438\u0441\u044b, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f Spring Boot \u0438 \u042f\u043d\u0434\u0435\u043a\u0441.\u041e\u0431\u043b\u0430\u043a\u043e<\/span>"},"content":{"rendered":"<div><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>\u0412 \u0441\u0442\u0430\u0442\u044c\u0435 \u0440\u0430\u0441\u0441\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f, \u043a\u0430\u043a \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u043d\u0430\u0432\u044b\u043a \u0434\u043b\u044f \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u044b \u042f\u043d\u0434\u0435\u043a\u0441.\u0414\u0438\u0430\u043b\u043e\u0433\u0438, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f Java \u0438 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a Spring Boot, \u0430 \u0437\u0430\u0442\u0435\u043c \u0440\u0430\u0437\u0432\u0435\u0440\u043d\u0443\u0442\u044c \u0435\u0433\u043e \u0432 \u042f\u043d\u0434\u0435\u043a\u0441.\u041e\u0431\u043b\u0430\u043a\u0435. <\/p>\n<p>\u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043d\u0443\u0436\u043d\u043e:<\/p>\n<ol>\n<li>\n<p>\u041d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u043a\u043e\u0434 \u0441\u0435\u0440\u0432\u0438\u0441\u0430, \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0443\u044e\u0449\u0435\u0433\u043e \u0441 \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u043e\u0439.<\/p>\n<\/li>\n<li>\n<p>\u0420\u0430\u0437\u0432\u0435\u0440\u043d\u0443\u0442\u044c \u0435\u0433\u043e \u0432 \u043e\u0431\u043b\u0430\u0447\u043d\u043e\u0439 \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0435, \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0432 \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u043c\u0443\u044e \u043e\u0431\u043b\u0430\u043a\u043e\u043c (Managed Service) \u0431\u0430\u0437\u0443 \u0434\u0430\u043d\u043d\u044b\u0445.<\/p>\n<\/li>\n<li>\n<p>\u041a\u0443\u043f\u0438\u0442\u044c \u0434\u043e\u043c\u0435\u043d \u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u0442\u0435\u0440\u043c\u0438\u043d\u0430\u0446\u0438\u044e SSL.<\/p>\n<\/li>\n<li>\n<p>\u041e\u043f\u0443\u0431\u043b\u0438\u043a\u043e\u0432\u0430\u0442\u044c \u0430\u0434\u0440\u0435\u0441 \u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u043d\u0430 \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u0435.<\/p>\n<\/li>\n<\/ol>\n<p>\u0414\u043b\u044f \u043f\u0440\u0438\u043c\u0435\u0440\u0430 \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u043f\u043e\u043c\u043e\u0433\u0430\u0435\u0442 \u0437\u0430\u043f\u043e\u043c\u0438\u043d\u0430\u0442\u044c \u0430\u043d\u0433\u043b\u0438\u0439\u0441\u043a\u0438\u0435 \u0441\u043b\u043e\u0432\u0430. \u0410\u043b\u0438\u0441\u0430 \u043f\u0440\u043e\u0441\u0438\u0442 \u043f\u0435\u0440\u0435\u0432\u0435\u0441\u0442\u0438 \u0440\u0443\u0441\u0441\u043a\u043e\u0435 \u0438\u043b\u0438 \u0430\u043d\u0433\u043b\u0438\u0439\u0441\u043a\u043e\u0435 \u0441\u043b\u043e\u0432\u043e, \u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0438\u0442 \u044d\u0442\u043e \u0441\u043b\u043e\u0432\u043e. \u0410\u043b\u0438\u0441\u0430 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442, \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e \u043b\u0438 \u0441\u043b\u043e\u0432\u043e \u043f\u0435\u0440\u0435\u0432\u0435\u0434\u0435\u043d\u043e. \u0415\u0441\u043b\u0438 \u043f\u0435\u0440\u0435\u0432\u043e\u0434 \u043d\u0435 \u0432\u0435\u0440\u043d\u044b\u0439, \u0442\u043e \u0410\u043b\u0438\u0441\u0430 \u0433\u043e\u0432\u043e\u0440\u0438\u0442, \u043a\u0430\u043a \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e \u043f\u0435\u0440\u0435\u0432\u0435\u0441\u0442\u0438 \u0441\u043b\u043e\u0432\u043e. \u0421\u043b\u043e\u0432\u0430\u0440\u044c \u0445\u0440\u0430\u043d\u0438\u0442\u0441\u044f \u0432 \u0421\u0423\u0411\u0414.<\/p>\n<p>\u0415\u0441\u043b\u0438 \u0432\u044b \u0437\u043d\u0430\u043a\u043e\u043c\u044b \u0441 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435\u043c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439 \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c Spring Boot, \u0442\u043e \u0440\u0430\u0437\u0434\u0435\u043b\u044b \u043f\u0440\u043e \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0443 \u043c\u043e\u0436\u043d\u043e \u043f\u0440\u043e\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u0438 \u043f\u0435\u0440\u0435\u0439\u0442\u0438 \u043a \u0440\u0430\u0437\u0432\u0451\u0440\u0442\u044b\u0432\u0430\u043d\u0438\u044e \u0441\u0435\u0440\u0432\u0438\u0441\u0430.<\/p>\n<h2>\u041a\u0430\u043a \u0443\u0441\u0442\u0440\u043e\u0435\u043d\u044b \u042f\u043d\u0434\u0435\u043a\u0441.\u0414\u0438\u0430\u043b\u043e\u0433\u0438<\/h2>\n<p>API \u043e\u043f\u0438\u0441\u0430\u043d\u043e \u0432 \u0440\u0430\u0437\u0434\u0435\u043b\u0435 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438 <a href=\"https:\/\/yandex.ru\/dev\/dialogs\/alice\/doc\/request.html\" rel=\"noopener noreferrer nofollow\">\u042f\u043d\u0434\u0435\u043a\u0441.\u0414\u0438\u0430\u043b\u043e\u0433\u043e\u0432<\/a>. \u041f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u0430 \u043f\u043e\u0441\u044b\u043b\u0430\u0435\u0442 POST-\u0437\u0430\u043f\u0440\u043e\u0441\u044b \u043d\u0430 \u0430\u0434\u0440\u0435\u0441 \u0431\u044d\u043a\u0435\u043d\u0434\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0432\u044b \u043d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u0442\u0435 \u0432 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\u0445 \u043d\u0430\u0432\u044b\u043a\u0430. \u0412 \u0437\u0430\u043f\u0440\u043e\u0441\u0435 \u043f\u0435\u0440\u0435\u0434\u0430\u0451\u0442\u0441\u044f \u0444\u0440\u0430\u0437\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0438 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0441\u043b\u0443\u0436\u0435\u0431\u043d\u0430\u044f \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u043f\u043e\u043c\u043e\u0433\u0430\u0435\u0442 \u0441\u0442\u0440\u043e\u0438\u0442\u044c \u043f\u043e\u043b\u043d\u043e\u0446\u0435\u043d\u043d\u044b\u0439 \u0434\u0438\u0430\u043b\u043e\u0433 \u0438 \u0437\u0430\u043f\u043e\u043c\u0438\u043d\u0430\u0442\u044c \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u043c\u0435\u0436\u0434\u0443 \u0441\u0435\u0430\u043d\u0441\u0430\u043c\u0438 \u043e\u0431\u0449\u0435\u043d\u0438\u044f. <\/p>\n<p>\u0412 \u0441\u043e\u0441\u0442\u0430\u0432 \u0441\u043b\u0443\u0436\u0435\u0431\u043d\u043e\u0439 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438 \u0432\u0445\u043e\u0434\u044f\u0442: <\/p>\n<ul>\n<li>\n<p>\u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440 \u0441\u0435\u0441\u0441\u0438\u0438;<\/p>\n<\/li>\n<li>\n<p>\u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f \u043e \u043a\u043e\u043d\u0435\u0447\u043d\u043e\u043c \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0435;<\/p>\n<\/li>\n<li>\n<p>\u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0432 \u042f\u043d\u0434\u0435\u043a\u0441.\u041f\u0430\u0441\u043f\u043e\u0440\u0442\u0435, \u0435\u0441\u043b\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u0446\u0438\u0440\u043e\u0432\u0430\u043d.<\/p>\n<\/li>\n<\/ul>\n<p>\u0424\u0440\u0430\u0437\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u043f\u0435\u0440\u0435\u0434\u0430\u0451\u0442\u0441\u044f \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u043a\u0430\u043a \u0442\u0435\u043a\u0441\u0442, \u043d\u043e \u0438 \u0440\u0430\u0437\u0431\u0438\u0440\u0430\u0435\u0442\u0441\u044f \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u043e\u0439 \u043d\u0430 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0435 <a href=\"https:\/\/yandex.ru\/dev\/dialogs\/alice\/doc\/request-simpleutterance.html\" rel=\"noopener noreferrer nofollow\">\u0442\u0438\u043f\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u0442\u043e\u043a\u0435\u043d\u044b<\/a>, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0434\u0430\u0442\u0430 \u0438 \u0432\u0440\u0435\u043c\u044f, \u0447\u0438\u0441\u043b\u043e, \u0430\u0434\u0440\u0435\u0441 \u0438\u043b\u0438 \u0438\u043c\u044f \u0438 \u0444\u0430\u043c\u0438\u043b\u0438\u044e. \u042d\u0442\u043e \u0441\u043e\u043a\u0440\u0430\u0449\u0430\u0435\u0442 \u0432\u0440\u0435\u043c\u044f \u043f\u0440\u0438 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0435, \u0442\u0430\u043a \u043d\u0435 \u043d\u0443\u0436\u043d\u043e \u0440\u0430\u0437\u0431\u0438\u0440\u0430\u0442\u044c\u0441\u044f \u0432 \u0442\u043e\u043d\u043a\u043e\u0441\u0442\u044f\u0445 \u0440\u0430\u0437\u0431\u043e\u0440\u0430 \u0435\u0441\u0442\u0435\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0445 \u044f\u0437\u044b\u043a\u043e\u0432.<\/p>\n<p>\u0412 \u043e\u0442\u0432\u0435\u0442 \u0441\u0435\u0440\u0432\u0438\u0441 \u043d\u0430\u0432\u044b\u043a\u0430 \u0434\u043e\u043b\u0436\u0435\u043d \u0432\u0435\u0440\u043d\u0443\u0442\u044c \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u0435 \u0444\u0440\u0430\u0437\u0443, \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u043f\u0440\u043e\u0438\u0437\u043d\u0435\u0441\u0451\u0442 \u0410\u043b\u0438\u0441\u0430. \u041c\u043e\u0436\u043d\u043e \u0442\u0430\u043a\u0436\u0435 \u0443\u043a\u0430\u0437\u0430\u0442\u044c \u0443\u0434\u0430\u0440\u0435\u043d\u0438\u044f, \u043f\u043e\u0434\u0441\u043a\u0430\u0437\u044b\u0432\u0430\u044f \u0410\u043b\u0438\u0441\u0435 \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u0443\u044e \u0438\u043d\u0442\u043e\u043d\u0430\u0446\u0438\u044e.<\/p>\n<p>\u0417\u0432\u0443\u0447\u0438\u0442 \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u043d\u0435 \u0441\u043b\u043e\u0436\u043d\u043e, \u0442\u0430\u043a \u0447\u0442\u043e \u043c\u043e\u0436\u043d\u043e \u043d\u0430\u0447\u0430\u0442\u044c \u043f\u0438\u0441\u0430\u0442\u044c \u0441\u0435\u0440\u0432\u0438\u0441.<\/p>\n<h2>\u041f\u0438\u0448\u0435\u043c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u043d\u0430 Sping Boot, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f TDD<\/h2>\n<h3>TDD &#8212; Test Driven Development<\/h3>\n<p>TDD &#8212; \u043e\u0434\u043d\u0430 \u0438\u0437 \u043c\u0435\u0442\u043e\u0434\u0438\u043a \u044d\u043a\u0441\u0442\u0440\u0435\u043c\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u043f\u043e\u043c\u043e\u0433\u0430\u0435\u0442 \u043f\u0438\u0441\u0430\u0442\u044c \u043d\u0430\u0434\u0451\u0436\u043d\u044b\u0439 \u0438 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u043c\u044b\u0439 \u043a\u043e\u0434. \u0427\u0438\u0442\u0430\u0442\u0435\u043b\u0438, \u0437\u043d\u0430\u043a\u043e\u043c\u044b\u0435 \u0441 \u043d\u0435\u0439, \u043c\u043e\u0433\u0443\u0442 \u043f\u0440\u043e\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u044d\u0442\u043e\u0442 \u0440\u0430\u0437\u0434\u0435\u043b. \u041d\u0438\u0436\u0435 \u044f \u043a\u043e\u0440\u043e\u0442\u043a\u043e \u043e\u043f\u0438\u0448\u0443 \u0441\u0443\u0442\u044c \u043c\u0435\u0442\u043e\u0434\u0430, \u0432\u0432\u0435\u0434\u0451\u043d\u043d\u043e\u0433\u043e \u041a\u0435\u043d\u0442\u043e\u043c \u0411\u0435\u043a\u043e\u043c \u0432 2003 \u0433\u043e\u0434\u0443. <\/p>\n<p>\u041a\u0430\u043a\u043e\u0439 \u043f\u043e\u0434\u0445\u043e\u0434 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0432 \u043b\u043e\u0431, \u0447\u0442\u043e\u0431\u044b \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0443? <\/p>\n<ol start=\"1\">\n<li>\n<p>\u041e\u0442\u043a\u0440\u044b\u0432\u0430\u0435\u043c \u0441\u0432\u043e\u0439 \u043b\u044e\u0431\u0438\u043c\u044b\u0439 IDE. <\/p>\n<\/li>\n<li>\n<p>\u041f\u0438\u0448\u0435\u043c \u043a\u043e\u0434 \u0441 \u043f\u0440\u043e\u0446\u0435\u0434\u0443\u0440\u043e\u0439 main.<\/p>\n<\/li>\n<li>\n<p>\u041a\u043e\u043c\u043f\u0438\u043b\u0438\u0440\u0443\u0435\u043c, \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c, \u0432\u0432\u043e\u0434\u0438\u043c \u0442\u0435\u0441\u0442\u043e\u0432\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435.<\/p>\n<\/li>\n<li>\n<p>\u0423\u0431\u0435\u0436\u0434\u0430\u0435\u043c\u0441\u044f, \u0447\u0442\u043e \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0430 \u0434\u0430\u0451\u0442 \u043e\u0436\u0438\u0434\u0430\u0435\u043c\u044b\u0439 \u043e\u0442\u0432\u0435\u0442.<\/p>\n<\/li>\n<li>\n<p>\u0415\u0441\u043b\u0438 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0430 \u0441\u0431\u043e\u0438\u0442, \u0442\u043e \u043f\u0440\u0430\u0432\u0438\u043c \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0443, \u0438 \u043f\u043e\u0432\u0442\u043e\u0440\u044f\u0435\u043c \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u0441 \u0442\u0440\u0435\u0442\u044c\u0435\u0433\u043e \u043f\u043e \u043f\u044f\u0442\u043e\u0435.<\/p>\n<\/li>\n<\/ol>\n<p>\u0422\u0430\u043a\u0438\u043c\u0438 \u043a\u043e\u0440\u043e\u0442\u043a\u0438\u043c\u0438 \u043f\u043e\u0432\u0442\u043e\u0440\u0435\u043d\u0438\u044f\u043c\u0438 \u043e\u0431\u044b\u0447\u043d\u043e \u0432\u0435\u0434\u0451\u0442\u0441\u044f \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0430. \u0420\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0430\u043c \u043f\u043e\u0441\u0442\u043e\u044f\u043d\u043d\u043e \u043f\u0440\u0438\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u043f\u043e\u0432\u0442\u043e\u0440\u044f\u0442\u044c \u043e\u0434\u0438\u043d\u0430\u043a\u043e\u0432\u044b\u0435 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u043f\u0440\u0438 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0438. \u0427\u0435\u043c \u0441\u043b\u043e\u0436\u043d\u0435\u0435 \u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0441\u044f \u043b\u043e\u0433\u0438\u043a\u0430 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u044b, \u0442\u0435\u043c \u0431\u043e\u043b\u044c\u0448\u0435 \u0442\u0435\u0441\u0442\u043e\u0432 \u043f\u0440\u0438\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u043f\u0440\u043e\u0432\u043e\u0434\u0438\u0442\u044c \u0432 \u0440\u0443\u0447\u043d\u0443\u044e. \u0410 \u0432\u0435\u0434\u044c \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0442\u0435\u0441\u0442\u044b \u0442\u0440\u0435\u0431\u0443\u044e\u0442 \u043f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u043a\u0438 \u0442\u0435\u0441\u0442\u043e\u0432\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445 \u0438 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0445 \u043f\u0440\u0435\u0434\u0443\u0441\u043b\u043e\u0432\u0438\u0439!<\/p>\n<p>\u041d\u043e \u043c\u044b \u0436\u0435 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0441\u0442\u044b \u0438 \u043c\u043e\u0436\u0435\u043c \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0434\u0430\u0436\u0435 \u044d\u0442\u0443 \u0440\u0443\u0442\u0438\u043d\u043d\u0443\u044e \u0440\u0430\u0431\u043e\u0442\u0443, \u0438 \u043d\u0430\u043c \u043d\u0430 \u043f\u043e\u043c\u043e\u0449\u044c \u043f\u0440\u0438\u0445\u043e\u0434\u0438\u0442 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u0435 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435. \u0426\u0438\u043a\u043b\u044b \u043f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u043a\u0438 \u0442\u0435\u0441\u0442\u043e\u0432\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445, \u0432\u044b\u0437\u043e\u0432\u044b \u043d\u0430\u0448\u0435\u0439 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u044b \u0438 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0443 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u0432 \u043c\u043e\u0436\u043d\u043e \u0437\u0430\u043d\u0435\u0441\u0442\u0438 \u0432 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u0443\u044e \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0443.<\/p>\n<p>\u041c\u0435\u0442\u043e\u0434\u0438\u043a\u0430 \u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u0435\u0442 \u043d\u0430\u0447\u0430\u0442\u044c \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u044b \u0441 \u0442\u0435\u0441\u0442\u0430, \u0430 \u0437\u0430\u0442\u0435\u043c \u0446\u0438\u043a\u043b\u044b \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0431\u0443\u0434\u0443\u0442 \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u0442\u044c \u043a\u0430\u043a Red, Green, Refactor.<\/p>\n<ul>\n<li>\n<p>Red &#8212; \u0434\u0443\u043c\u0430\u0435\u043c \u043e \u0442\u043e\u043c, \u0447\u0442\u043e \u043d\u0443\u0436\u043d\u043e \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c. \u041f\u0438\u0448\u0435\u043c \u0442\u0435\u0441\u0442, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442 \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b. \u041f\u0440\u0438 \u043f\u0435\u0440\u0432\u043e\u0439 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0435 \u043e\u043d \u043f\u0430\u0434\u0430\u0435\u0442, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0448\u0430\u0433 \u043d\u0430\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f Red.<\/p>\n<\/li>\n<li>\n<p>Green &#8212; \u043f\u0438\u0448\u0435\u043c \u043a\u043e\u0434 \u0441\u0430\u043c\u044b\u043c \u043f\u0440\u043e\u0441\u0442\u044b\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c, \u0447\u0442\u043e\u0431\u044b \u0442\u0435\u0441\u0442 \u0440\u0430\u0431\u043e\u0442\u0430\u043b. <\/p>\n<\/li>\n<li>\n<p>Refactor &#8212; \u0443\u043b\u0443\u0447\u0448\u0430\u0435\u043c \u043d\u0430\u0448 \u043a\u043e\u0434, \u0447\u0442\u043e\u0431\u044b \u043e\u043d \u0431\u044b\u043b \u043b\u0443\u0447\u0448\u0435 \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u043e\u0432\u0430\u043d, \u043d\u043e \u043f\u0440\u0438 \u044d\u0442\u043e\u043c \u0442\u0435\u0441\u0442 \u0440\u0430\u0431\u043e\u0442\u0430\u043b.<\/p>\n<\/li>\n<\/ul>\n<p>\u041f\u043e\u0441\u043b\u0435 \u043a\u0430\u0436\u0434\u043e\u0439 \u0442\u0430\u043a\u043e\u0439 \u0438\u0442\u0435\u0440\u0430\u0446\u0438\u0438 \u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u0435\u0442\u0441\u044f \u0434\u0435\u043b\u0430\u0442\u044c \u043a\u043e\u043c\u043c\u0438\u0442. \u0412 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0435, \u043d\u0430 \u043a\u0430\u0436\u0434\u043e\u043c \u044d\u0442\u0430\u043f\u0435 \u043c\u044b \u0438\u043c\u0435\u0435\u043c \u0440\u0430\u0431\u043e\u0442\u0430\u044e\u0449\u0438\u0439 \u043a\u043e\u0434 (\u043e\u043d \u043f\u0440\u043e\u0432\u0435\u0440\u0435\u043d \u0430\u0432\u0442\u043e\u0442\u0435\u0441\u0442\u0430\u043c\u0438) \u0438 \u043d\u0435 \u043f\u0438\u0448\u0435\u043c \u043b\u0438\u0448\u043d\u0438\u0439 \u043a\u043e\u0434, \u044d\u043a\u043e\u043d\u043e\u043c\u044f \u0441\u0432\u043e\u0451 \u0432\u0440\u0435\u043c\u044f.<\/p>\n<p>\u041f\u0440\u043e\u0432\u043e\u0434\u044f \u0440\u0435\u0444\u0430\u043a\u0442\u043e\u0440\u0438\u043d\u0433 \u043a\u043e\u0434\u0430, \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u043c\u043e\u0436\u0435\u0442 \u0432\u044b\u0434\u0435\u043b\u044f\u0442\u044c \u0430\u0431\u0441\u0442\u0440\u0430\u043a\u0446\u0438\u0438 \u0438 \u0441\u043b\u043e\u0438 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u044b \u0438 \u0443\u0434\u0430\u043b\u044f\u0442\u044c \u0434\u0443\u0431\u043b\u0438\u0440\u0443\u044e\u0449\u0438\u0439\u0441\u044f \u043a\u043e\u0434. <\/p>\n<h2>\u0428\u0430\u0431\u043b\u043e\u043d \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/h2>\n<p>\u0424\u0440\u0435\u0439\u043c\u0444\u043e\u0440\u043a Spring Boot \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0433\u043e\u0442\u043e\u0432\u044b\u0439 \u043f\u0440\u043e\u0435\u043a\u0442 \u0441 \u043d\u0443\u043b\u044f, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f <a href=\"https:\/\/start.spring.io\/\" rel=\"noopener noreferrer nofollow\">Sping Initializr<\/a>. <\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/03f\/79a\/20b\/03f79a20b71eedfb6952bbf38761cf4d.PNG\" width=\"1495\" height=\"716\"\/><figcaption><\/figcaption><\/figure>\n<p>\u0417\u0430 \u0441\u0431\u043e\u0440\u043a\u0443 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u0431\u0443\u0434\u0435\u0442 \u043e\u0442\u0432\u0435\u0447\u0430\u0442\u044c Maven. \u0418\u0437 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439 \u043f\u043e\u0442\u0440\u0435\u0431\u0443\u044e\u0442\u0441\u044f:<\/p>\n<ul>\n<li>\n<p>Lombok &#8212; \u0434\u043b\u044f \u0443\u043c\u0435\u043d\u044c\u0448\u0435\u043d\u0438\u044f \u0432\u0441\u043f\u043e\u043c\u043e\u0433\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0433\u043e (boilerplate) \u043a\u043e\u0434\u0430.<\/p>\n<\/li>\n<li>\n<p>Spring Web &#8212; \u0434\u043b\u044f \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 HTTP-\u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432.<\/p>\n<\/li>\n<li>\n<p>Spring Data JPA &#8212; \u0434\u043b\u044f \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043a \u0421\u0423\u0411\u0414.<\/p>\n<\/li>\n<li>\n<p>PostgreSQL &#8212; \u0434\u0440\u0430\u0439\u0432\u0435\u0440 \u043a \u043f\u0440\u043e\u0434\u0443\u043a\u0442\u0438\u0432\u043d\u043e\u0439 \u0421\u0423\u0411\u0414.<\/p>\n<\/li>\n<li>\n<p>H2 Database &#8212; \u0434\u0440\u0430\u0439\u0432\u0435\u0440 \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u0430 \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0421\u0423\u0411\u0414.<\/p>\n<\/li>\n<li>\n<p>Liquibase Migration &#8212; \u0441\u0440\u0435\u0434\u0441\u0442\u0432\u043e \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0441\u0445\u0435\u043c\u043e\u0439 \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445.<\/p>\n<\/li>\n<\/ul>\n<p>\u041f\u043e\u0441\u043b\u0435 \u0432\u044b\u0431\u043e\u0440\u0430 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439 \u043d\u0430\u0436\u0438\u043c\u0430\u0435\u043c \u043a\u043d\u043e\u043f\u043a\u0443 &#171;Generate&#187; \u0438 \u0441\u043a\u0430\u0447\u0438\u0432\u0430\u0435\u043c \u0430\u0440\u0445\u0438\u0432 \u0441 \u0448\u0430\u0431\u043b\u043e\u043d\u043e\u043c \u043f\u0440\u043e\u0435\u043a\u0442\u0430. \u0410\u0440\u0445\u0438\u0432 \u0440\u0430\u0441\u043f\u0430\u043a\u043e\u0432\u044b\u0432\u0430\u0435\u043c \u0432 \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u0443\u044e \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u044e. \u041e\u0442\u043a\u0440\u044b\u0432\u0430\u0435\u043c \u043a\u043e\u043d\u0441\u043e\u043b\u044c \u0438 \u043f\u0440\u043e\u0431\u0443\u0435\u043c \u0441\u043e\u0431\u0440\u0430\u0442\u044c.<\/p>\n<pre><code>cd C:\\dev\\dictionary-service mvnw package<\/code><\/pre>\n<p>\u041f\u0440\u043e\u0435\u043a\u0442 \u043f\u0430\u0434\u0430\u0435\u0442 \u0441 \u043e\u0448\u0438\u0431\u043a\u043e\u0439.<\/p>\n<pre><code>Caused by: liquibase.exception.ChangeLogParseException: classpath:\/db\/changelog\/db.changelog-master.yaml does not exist  [INFO] [INFO] Results: [INFO] [ERROR] Errors: [ERROR]   DictionaryServiceApplicationTests.contextLoads ? IllegalState Failed to load A... [INFO] [ERROR] Tests run: 1, Failures: 0, Errors: 1, Skipped: 0 [INFO] [INFO] ------------------------------------------------------------------------ [INFO] BUILD FAILURE [INFO] ------------------------------------------------------------------------<\/code><\/pre>\n<p>\u041f\u0440\u0438\u0447\u0438\u043d\u0430 \u0432 \u0442\u043e\u043c, \u0447\u0442\u043e Liquibase \u0432 \u0440\u0435\u0441\u0443\u0440\u0441\u0430\u0445 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043d\u0435 \u043d\u0430\u0448\u0451\u043b \u0436\u0443\u0440\u043d\u0430\u043b \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0439 <code>\/db\/changelog\/db.changelog-master.yaml<\/code>, \u0442\u0440\u0435\u0431\u0443\u0435\u043c\u044b\u0439 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e.<\/p>\n<p>\u0417\u0430\u043c\u0435\u043d\u0438\u043c \u0444\u0430\u0439\u043b <code>src\/main\/resources\/application.properties<\/code> \u043d\u0430 <code>src\/main\/resources\/application.yml<\/code> \u0438 \u043f\u0440\u043e\u043f\u0438\u0448\u0435\u043c \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e Liquibase.<\/p>\n<pre><code class=\"yaml\">spring:   profiles:      default: local #\u043f\u0440\u043e\u0444\u0438\u043b\u044c Spring \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e   datasource: #\u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f \u0441 \u0421\u0423\u0411\u0414 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e     driverClassName: org.postgresql.Driver      url: ${SPRING_DATASOURCE_URL} #\u0431\u0443\u0434\u0443\u0442 \u043e\u043f\u0435\u0440\u0435\u0434\u0435\u043b\u044f\u0442\u044c\u0441\u044f \u0447\u0435\u0440\u0435\u0437 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0443\u044e \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f     username: ${SPRING_DATASOURCE_USERNAME}     password: ${SPRING_DATASOURCE_PASSWORD}   jpa:     show-sql: false #\u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u043e\u0442\u043a\u043b\u044e\u0447\u0438\u043c \u043f\u043e\u043a\u0430\u0437 SQL     hibernate.ddl-auto: validate #Hibernate \u0431\u0443\u0434\u0435\u0442 \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0442\u044c \u0441\u0445\u0435\u043c\u0443      properties.hibernate.dialect: org.hibernate.dialect.PostgreSQLDialect     open-in-view: false   liquibase:     enabled: true #\u0432\u043a\u043b\u044e\u0447\u0451\u043d \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e     contexts: schema, data #\u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u044b \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e     change-log: classpath:db.changelog.xml #\u043f\u0443\u0442\u044c \u043a \u0436\u0443\u0440\u043d\u0430\u043b\u0443 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0439 Liquibase     liquibaseSchema: liquibase #\u0441\u043b\u0443\u0436\u0435\u0431\u043d\u0430\u044f \u0441\u0445\u0435\u043c\u0430 \u0421\u0423\u0411\u0414 \u0434\u043b\u044f liquibase     default-schema: dict #\u0441\u0445\u0435\u043c\u0430 \u0421\u0423\u0411\u0414 \u0434\u043b\u044f \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f     user: ${SPRING_DATASOURCE_USERNAME}     password: ${SPRING_DATASOURCE_PASSWORD} --- spring:   config.activate.on-profile: test #\u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u0435 \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u0445 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u043e\u0432\u043e\u0433\u043e \u043f\u0440\u043e\u0444\u0438\u043b\u044f    jpa:     properties.hibernate.dialect: org.hibernate.dialect.H2Dialect #\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c H2 \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u043e\u0432   datasource:     driverClassName: org.h2.Driver     url: jdbc:h2:mem:domain1-db;MODE=PostgreSQL;CASE_INSENSITIVE_IDENTIFIERS=TRUE;DB_CLOSE_DELAY=-1;INIT=CREATE SCHEMA IF NOT EXISTS LIQUIBASE\\;CREATE SCHEMA IF NOT EXISTS DICT\\;SET SCHEMA DICT;     username: sa     password:   liquibase:     user: sa     password:     contexts: schema<\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0432 \u0440\u0435\u0441\u0443\u0440\u0441\u0430\u0445 (src\/main\/resources) \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043f\u0443\u0441\u0442\u043e\u0439 \u0436\u0443\u0440\u043d\u0430\u043b \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0439 <code>db.changelog.xml<\/code>.<\/p>\n<pre><code class=\"xml\">&lt;databaseChangeLog xmlns=\"http:\/\/www.liquibase.org\/xml\/ns\/dbchangelog\"     xmlns:xsi=\"http:\/\/www.w3.org\/2001\/XMLSchema-instance\"     xsi:schemaLocation=\"http:\/\/www.liquibase.org\/xml\/ns\/dbchangelog http:\/\/www.liquibase.org\/xml\/ns\/dbchangelog\/dbchangelog-3.3.xsd\">  &lt;\/databaseChangeLog><\/code><\/pre>\n<p>\u0427\u0442\u043e\u0431\u044b \u0432 \u0442\u0435\u0441\u0442\u0430\u0445 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b\u0441\u044f \u0442\u0435\u0441\u0442\u043e\u0432\u044b\u0439 \u043f\u0440\u043e\u0444\u0438\u043b\u044c, \u0443\u043a\u0430\u0436\u0435\u043c \u0435\u0433\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u0432 \u0442\u0435\u0441\u0442\u0435 src\/test\/java\/com\/example\/dictionary-service\/DictionaryServiceApplicationTests.java<\/p>\n<pre><code class=\"java\">package com.example.dictionaryservice;  import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.ActiveProfiles;  @SpringBootTest @ActiveProfiles(\"test\") \/\/\u0430\u043a\u0442\u0438\u0432\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0442\u0435\u0441\u0442\u043e\u0432\u044b\u0439 \u043f\u0440\u043e\u0444\u0438\u043b\u044c Spring \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u043e\u0432 class DictionaryServiceApplicationTests { @Test void contextLoads() { } }<\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043f\u0440\u043e\u0435\u043a\u0442 \u0441\u043e\u0431\u0438\u0440\u0430\u0435\u0442\u0441\u044f. \u041c\u043e\u0436\u043d\u043e \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0439 git \u043a\u043e\u043c\u0430\u043d\u0434\u043e\u0439 <code>git init<\/code>. \u041f\u043e\u0441\u043b\u0435 \u044d\u0442\u043e\u0433\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0432 \u0438\u043d\u0434\u0435\u043a\u0441 git \u0444\u0430\u0439\u043b \u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u043c \u043a\u043e\u043c\u043c\u0438\u0442.<\/p>\n<pre><code>git add *.cmd *.xml *.java *.yml *.jar *.properties .gitignore git commit -m \"Init repository\"<\/code><\/pre>\n<p>\u0427\u0442\u043e\u0431\u044b \u043f\u0440\u043e\u0435\u043a\u0442 \u0441\u043e\u0431\u0438\u0440\u0430\u043b\u0441\u044f \u043d\u0430 *nix \u0441\u0438\u0441\u0442\u0435\u043c\u0430\u0445, \u043d\u0443\u0436\u043d\u043e \u0435\u0449\u0451 \u0432\u044b\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u043f\u0440\u0430\u0432\u0430 \u0434\u043b\u044f \u0437\u0430\u043f\u0443\u0441\u043a\u0430 Shell-\u0441\u043a\u0440\u0438\u043f\u0442\u0430 <code>mnvw<\/code>.<\/p>\n<pre><code>git update-index --chmod=+x mvnw <\/code><\/pre>\n<h3>\u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f API<\/h3>\n<p>\u041e\u0434\u0438\u043d \u0438\u0437 \u043f\u043e\u0434\u0445\u043e\u0434\u043e\u0432 \u043a \u043f\u0440\u043e\u0435\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f API First. \u0415\u0433\u043e \u043f\u0440\u0435\u0438\u043c\u0443\u0449\u0435\u0441\u0442\u0432\u043e \u0432 \u0442\u043e\u043c, \u0447\u0442\u043e \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u0444\u043e\u043a\u0443\u0441\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u043d\u0430 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u0441 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435\u043c, \u0430 \u043d\u0435 \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0435. \u0422\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c, \u043c\u044b \u0438\u0437\u0431\u0435\u0433\u0430\u0435\u043c \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043b\u043e\u0433\u0438\u043a\u0438 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043e\u0442 \u0434\u0435\u0442\u0430\u043b\u0435\u0439 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u043e\u0433\u0443\u0442 \u043c\u0435\u043d\u044f\u0442\u044c\u0441\u044f.<\/p>\n<p>\u0412 \u0441\u043b\u0443\u0447\u0430\u0435 \u0441 \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u043e\u0439 \u042f\u043d\u0434\u0435\u043a\u0441.\u0414\u0438\u0430\u043b\u043e\u0433\u0438 API \u0443\u0436\u0435 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043e \u0437\u0430 \u043d\u0430\u0441. \u041c\u043e\u0436\u043d\u043e \u0441\u0440\u0430\u0437\u0443 \u043d\u0430\u0447\u0438\u043d\u0430\u0442\u044c \u043f\u0438\u0441\u0430\u0442\u044c \u043a\u043e\u0434! \u0418 \u043f\u0435\u0440\u0432\u044b\u043c \u0431\u0443\u0434\u0435\u0442 \u0442\u0435\u0441\u0442 \u043d\u0430 API. <\/p>\n<p>\u041a\u043e\u0433\u0434\u0430 \u0432\u044b \u043f\u0440\u043e\u0441\u0438\u0442\u0435 \u0410\u043b\u0438\u0441\u0443 \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u043d\u0430\u0432\u044b\u043a, \u043e\u043d\u0430 \u0434\u0435\u043b\u0430\u0435\u0442 POST-\u0437\u0430\u043f\u0440\u043e\u0441 \u043a \u0441\u0435\u0440\u0432\u0438\u0441\u0443, \u043d\u0430 \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043d\u0443\u0436\u043d\u043e \u043e\u0442\u0432\u0435\u0442\u0438\u0442\u044c \u0444\u0440\u0430\u0437\u043e\u0439, \u0440\u0430\u0441\u0441\u043a\u0430\u0437\u044b\u0432\u0430\u044e\u0449\u0435\u0439 \u043e \u043d\u0430\u0432\u044b\u043a\u0435 \u0438 \u043a\u0430\u043a \u0441 \u043d\u0438\u043c \u043e\u0431\u0449\u0430\u0442\u044c\u0441\u044f.<\/p>\n<p>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043a\u043b\u0430\u0441\u0441 <code>com.example.dictionaryservice.api.DictionaryApiTest<\/code> \u0432 src\/test\/java <\/p>\n<pre><code class=\"java\">package com.example.dictionaryservice.api;  import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;  import lombok.SneakyThrows; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.http.MediaType; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.web.servlet.MockMvc;  @SpringBootTest \/\/\u043c\u0430\u0440\u043a\u0435\u0440 \u0442\u0435\u0441\u0442\u0430 @ActiveProfiles(\"test\") \/\/\u043f\u0440\u043e\u0444\u0438\u043b\u044c Spring \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f @AutoConfigureMockMvc \/\/\u0430\u0432\u0442\u043e\u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f MockMvc class DictionaryApiTest {     @Autowired     MockMvc mockMvc; \/\/\u043a\u043b\u0430\u0441\u0441      @Test     @SneakyThrows     void should_accept_request_from_yandex_alice_and_return_valid_response() {         mockMvc.perform(             post(\"\/api\/v1\/dictionary\/yandex-alice-skill\/\") \/\/POST-\u0437\u0430\u043f\u0440\u043e\u0441 \u043f\u043e \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u043e\u043c\u0443 \u043f\u0443\u0442\u0438             .contentType(MediaType.APPLICATION_JSON)              .accept(MediaType.APPLICATION_JSON)             .content(\"{\" \/\/\u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u043e\u0442 \u042f\u043d\u0434\u0435\u043a\u0441.\u0410\u043b\u0438\u0441\u044b                 + \"  \\\"meta\\\": {\"                 + \"  },\"                 + \"  \\\"request\\\": {\"                 + \"    \\\"command\\\": \\\"\u0410\u043b\u0438\u0441\u0430 \u0437\u0430\u043f\u0443\u0441\u0442\u0438 \u043d\u0430\u0432\u044b\u043a \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0447\u0438\u043a\\\",\"                 + \"    \\\"original_utterance\\\": \\\"\u0430\u043b\u0438\u0441\u0430 \u0437\u0430\u043f\u0443\u0441\u0442\u0438 \u043d\u0430\u0432\u044b\u043a \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0447\u0438\u043a\\\",\"                 + \"    \\\"type\\\": \\\"SimpleUtterance\\\",\"                 + \"    \\\"payload\\\": {},\"                 + \"    \\\"nlu\\\": {\"                 + \"      \\\"tokens\\\": [\"                 + \"        \\\"\u0430\u043b\u0438\u0441\u0430\\\",\"                 + \"        \\\"\u0437\u0430\u043f\u0443\u0441\u0442\u0438\\\",\"                 + \"        \\\"\u043d\u0430\u0432\u044b\u043a\\\",\"                 + \"        \\\"\u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0447\u0438\u043a\\\"\"                 + \"      ]\"                 + \"    }\"                 + \"  },\"                 + \"  \\\"session\\\": {\"                 + \"    \\\"message_id\\\": 0,\"                 + \"    \\\"session_id\\\": \\\"2eac4854-fce721f3-b845abba-20d60\\\",\"                 + \"    \\\"user\\\": {\"                 + \"      \\\"user_id\\\": \\\"47C73714B580EE\\\",\"                 + \"      \\\"access_token\\\": \\\"AgAAAAAB4vpbAAApoR1oaCd5yR6eiXSHqOGT8dT\\\"\"                 + \"    },\"                 + \"    \\\"application\\\": {\"                 + \"      \\\"application_id\\\": \\\"47C73714B580ED\\\"\"                 + \"    },\"                 + \"    \\\"new\\\": true\"                 + \"  },\"                 + \"  \\\"version\\\": \\\"1.0\\\"\"                 + \"}\")         )           .andExpect(status().isOk()) \/\/\u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c, \u0447\u0442\u043e \u0441\u0442\u0430\u0442\u0443\u0441 \u043e\u0442\u0432\u0435\u0442\u0430 - HTTP 200 OK           .andExpect(content().json(\"{\" \/\/\u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0433\u043e \u043e\u0442\u0432\u0435\u0442\u0430                 + \"  \\\"response\\\": {\"                 + \"    \\\"text\\\": \\\"\u041f\u0440\u0438\u0432\u0435\u0442! \"                      + \"\u042f \u043f\u043e\u043c\u043e\u0433\u0443 \u0432\u0430\u043c \u0432\u044b\u0443\u0447\u0438\u0442\u044c \u0430\u043d\u0433\u043b\u0438\u0439\u0441\u043a\u0438\u0435 \u0441\u043b\u043e\u0432\u0430.\\\",\"                 + \"    \\\"end_session\\\": false\"                 + \"  },\"                 + \"  \\\"version\\\": \\\"1.0\\\"\"                 + \"}\"));     } }<\/code><\/pre>\n<p>\u0417\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c \u0442\u0435\u0441\u0442, \u0438 \u043e\u043d \u043f\u0430\u0434\u0430\u0435\u0442 \u0441 \u043e\u0448\u0438\u0431\u043a\u043e\u0439.  <\/p>\n<pre><code>[ERROR] Failures: [ERROR]   DictionaryApiTest.should_accept_request_from_yandex_alice_and_return_valid_response:61 Status expected:&lt;200> but was:&lt;404><\/code><\/pre>\n<p>\u042d\u0442\u043e \u0432\u043f\u043e\u043b\u043d\u0435 \u043b\u043e\u0433\u0438\u0447\u043d\u043e, \u0442\u0430\u043a \u043a\u0430\u043a \u043c\u044b \u043d\u0435 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043b\u0438 \u043d\u0438 \u043e\u0434\u043d\u043e\u0433\u043e \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0442\u044c \u0437\u0430\u043f\u0440\u043e\u0441\u044b. \u041c\u044b \u0441\u0435\u0439\u0447\u0430\u0441 \u043d\u0430 \u044d\u0442\u0430\u043f\u0435 Red \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0430 TDD.<\/p>\n<p>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043a\u043b\u0430\u0441\u0441 <code>com.example.dictionaryservice.api.DictionaryController <\/code>\u0432 <code>src\/main\/java<\/code>.<\/p>\n<pre><code class=\"java\">package com.example.dictionaryservice.api;  import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController;  @RestController \/\/\u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 REST-\u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 @Slf4j \/\/\u0430\u043d\u043d\u043e\u0442\u0430\u0446\u0438\u044f Lombok \u0434\u043b\u044f \u0436\u0443\u0440\u043d\u0430\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0447\u0435\u0440\u0435\u0437 Slf4j @RequestMapping(\"\/api\/v1\/dictionary\/yandex-alice-skill\") \/\/\u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u043c\u044b\u0439 \u043f\u0443\u0442\u044c public class DictionaryController {     @PostMapping \/\/\u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 POST-\u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432     public @ResponseBody \/* \u041a\u043b\u0430\u0441\u0441 \u0431\u0443\u0434\u0435\u0442 \u043a\u043e\u043d\u0432\u0435\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u043d \u0432 \u0442\u0435\u043b\u043e HTTP-\u043e\u0442\u0432\u0435\u0442\u0430*\/ YandexAliceResponse         talkYandexAlice(             @RequestBody \/* \u0422\u0435\u043b\u043e HTTP-\u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u0431\u0443\u0434\u0435\u0442  \u0441\u043a\u043e\u043d\u0432\u0435\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u043e \u0432 \u043a\u043b\u0430\u0441\u0441 *\/ YandexAliceRequest request) {         return new YandexAliceRespone(new YASkillResponse(             \"\u041f\u0440\u0438\u0432\u0435\u0442! \u042f \u043f\u043e\u043c\u043e\u0433\u0443 \u0432\u0430\u043c \u0432\u044b\u0443\u0447\u0438\u0442\u044c \u0430\u043d\u0433\u043b\u0438\u0439\u0441\u043a\u0438\u0435 \u0441\u043b\u043e\u0432\u0430.\"));     } }<\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043d\u0430\u043f\u0438\u0448\u0435\u043c \u043a\u043e\u0434 \u0434\u043b\u044f DTO (Data Transfer Object), \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0442\u0441\u044f \u0434\u043b\u044f \u0432\u044b\u0437\u043e\u0432\u043e\u0432 API: YandexAliceRequest, YandexAliceRespone, YASkillResponse. \u0420\u0430\u0441\u043f\u043e\u043b\u043e\u0436\u0438\u043c \u0438\u0445 \u0432 \u043f\u0430\u043a\u0435\u0442\u0435 com.example.dictionaryservice.dto.<\/p>\n<pre><code class=\"java\">package com.example.dictionaryservice.dto;  import lombok.AccessLevel; import lombok.Data; import lombok.NoArgsConstructor; import lombok.NonNull; import lombok.RequiredArgsConstructor; import lombok.experimental.FieldDefaults;  @Data \/\/\u0430\u0432\u0442\u043e\u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u044f \u0441\u0435\u0442\u0442\u0435\u0440\u043e\u0432, \u0433\u0435\u0442\u0442\u0435\u0440\u043e\u0432, hashCode, equals \u0438 toString @NoArgsConstructor \/\/\u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u044f \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u043e\u0440\u0430 \u0431\u0435\u0437 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u043e\u0432                     \/\/ \u043d\u0443\u0436\u043d\u0430 \u0434\u043b\u044f \u0434\u0435\u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0438\u0437 JSON @RequiredArgsConstructor \/\/\u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u044f \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u043e\u0440\u0430 \u0434\u043b\u044f \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0445 \u043f\u043e\u043b\u0435\u0439                   @FieldDefaults(level = AccessLevel.PRIVATE) \/\/\u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u0434\u043e\u0441\u0442\u0443\u043f \u043a \u0447\u043b\u0435\u043d\u0430\u043c - private public class YandexAliceResponse {     @NonNull \/\/\u0430\u0432\u0442\u043e\u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u043e\u0432 \u043d\u0430 null     YASkillResponse response;     @NonNull     String version = \"1.0\"; }<\/code><\/pre>\n<pre><code class=\"java\">import com.fasterxml.jackson.annotation.JsonProperty; import lombok.AccessLevel; import lombok.Data; import lombok.NoArgsConstructor; import lombok.NonNull; import lombok.RequiredArgsConstructor; import lombok.experimental.FieldDefaults;  @Data @NoArgsConstructor @RequiredArgsConstructor @FieldDefaults(level = AccessLevel.PRIVATE) public class YASkillResponse {     @NonNull     String text;  \/\/\u0430\u043d\u043d\u043e\u0442\u0430\u0446\u0438\u044f \u0434\u043b\u044f \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e\u0439 \u0434\u0435\u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u043f\u043e\u043b\u044f \u0438\u0437 JSON     \/\/\u0442\u0430\u043a \u043a\u0430\u043a \u0432 Java \u043f\u0440\u0438\u043d\u044f\u0442\u0430 \u043d\u043e\u0442\u0430\u0446\u0438\u044f CamelCase, \u0430 \u0432 API - snake_case     @JsonProperty(\"end_session\")                           boolean endSession = false; }<\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043c\u043e\u0436\u043d\u043e \u0435\u0449\u0451 \u0440\u0430\u0437 \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u0442\u0435\u0441\u0442, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043e\u0442\u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442. \u042d\u0442\u043e \u0444\u0430\u0437\u0430 Green. \u041c\u043e\u0436\u043d\u043e \u0437\u0430\u043a\u043e\u043c\u043c\u0438\u0442\u0438\u0442\u044c \u043d\u043e\u0432\u044b\u0439 \u043a\u043e\u0434.<\/p>\n<pre><code>git add *.java git commit -m \"API test\"<\/code><\/pre>\n<p>\u041f\u043e\u043f\u0440\u043e\u0431\u0443\u0435\u043c \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u043d\u0430\u0448\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435.<\/p>\n<pre><code>mvnw spring-boot:run -Dspring-boot.run.profiles=test<\/code><\/pre>\n<p>\u041e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u043c \u0437\u0430\u043f\u0440\u043e\u0441<\/p>\n<pre><code>curl -H \"Accept: application\/json\" -H \"Content-Type: application\/json\" \\     http:\/\/localhost:8080\/api\/v1\/dictionary\/yandex-alice-skill -X POST -d \"{}\"  {\"response\":{\"text\":\"\u041f\u0440\u0438\u0432\u0435\u0442! \u042f \u043f\u043e\u043c\u043e\u0433\u0443 \u0432\u0430\u043c \u0432\u044b\u0443\u0447\u0438\u0442\u044c \u0430\u043d\u0433\u043b\u0438\u0439\u0441\u043a\u0438\u0435 \u0441\u043b\u043e\u0432\u0430.\",\"end_session\":false},\"version\":\"1.0\"}<\/code><\/pre>\n<h3>\u0420\u0435\u0444\u0430\u043a\u0442\u043e\u0440\u0438\u043d\u0433 &#8212; \u043e\u0442\u0434\u0435\u043b\u044f\u0435\u043c \u0441\u043b\u043e\u0439 \u0431\u0438\u0437\u043d\u0435\u0441-\u043b\u043e\u0433\u0438\u043a\u0438 \u043e\u0442 API<\/h3>\n<p>\u041d\u0430\u0448 \u0441\u0435\u0440\u0432\u0438\u0441 \u0434\u043e\u043b\u0436\u0435\u043d \u043e\u0442\u0432\u0435\u0447\u0430\u0442\u044c \u043f\u043e-\u0440\u0430\u0437\u043d\u043e\u043c\u0443 \u043d\u0430 \u043a\u0430\u0436\u0434\u044b\u0439 \u0437\u0430\u043f\u0440\u043e\u0441 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f, \u0430 \u0441\u0435\u0439\u0447\u0430\u0441 \u043e\u043d \u0431\u0443\u0434\u0435\u0442 \u0432\u0441\u0435\u0433\u0434\u0430 \u0432\u044b\u0434\u0430\u0432\u0430\u0442\u044c \u043e\u0434\u043d\u0443 \u0438 \u0442\u0443 \u0436\u0435 \u0444\u0440\u0430\u0437\u0443. \u0422\u0430\u043a \u043a\u0430\u043a \u044d\u0442\u043e \u0443\u0436\u0435 \u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0441\u0442\u044c \u0431\u0438\u0437\u043d\u0435\u0441-\u043b\u043e\u0433\u0438\u043a\u0438 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f, \u0432\u044b\u043d\u0435\u0441\u0435\u043c \u044d\u0442\u043e \u0432 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 \u0441\u0435\u0440\u0432\u0438\u0441. \u0422\u0435\u043f\u0435\u0440\u044c \u043a\u043b\u0430\u0441\u0441 DictionaryController \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u0442\u044c \u0442\u0430\u043a:<\/p>\n<pre><code class=\"java\">package com.example.dictionaryservice.api;  import com.example.dictionaryservice.dto.YandexAliceRequest; import com.example.dictionaryservice.dto.YandexAliceResponse; import com.example.dictionaryservice.service.DictionaryService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController;  @RestController @Slf4j @RequestMapping(\"\/api\/v1\/dictionary\/yandex-alice-skill\") @RequiredArgsConstructor \/\/\u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u044f \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u043e\u0440\u0430 \u0434\u043b\u044f \u0441\u0432\u044f\u0437\u044b\u0432\u0430\u043d\u0438\u044f dictionaryService public class DictionaryController { \/\/\u0431\u0438\u0437\u043d\u0435\u0441-\u043b\u043e\u0433\u0438\u043a\u0430 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u0432 \u0441\u0435\u0440\u0432\u0438\u0441\u0435     private final DictionaryService dictionaryService;      @PostMapping     public @ResponseBody YandexAliceResponse talkYandexAlice(         @RequestBody YandexAliceRequest request) {         return dictionaryService.talkYandexAlice(request);     } }<\/code><\/pre>\n<p>\u0418\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 DictionaryService \u0441\u043e\u0437\u0434\u0430\u0451\u043c \u0432 \u043f\u0430\u043a\u0435\u0442\u0435 com.example.dictionaryservice.service<\/p>\n<pre><code class=\"java\">package com.example.dictionaryservice.service;  import com.example.dictionaryservice.dto.YandexAliceRequest; import com.example.dictionaryservice.dto.YandexAliceResponse;  public interface DictionaryService {      YandexAliceResponse talkYandexAlice(YandexAliceRequest request); }<\/code><\/pre>\n<p>\u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f &#8212; \u0432 DictionaryServiceImpl \u0432 \u0442\u043e\u043c \u0436\u0435 \u043f\u0430\u043a\u0435\u0442\u0435.<\/p>\n<pre><code class=\"java\">package com.example.dictionaryservice.service;  import com.example.dictionaryservice.dto.YASkillResponse; import com.example.dictionaryservice.dto.YandexAliceRequest; import com.example.dictionaryservice.dto.YandexAliceResponse; import org.springframework.stereotype.Service;  @Service \/\/\u0430\u043d\u043d\u043e\u0442\u0430\u0446\u0438\u044f \u0434\u043b\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0433\u043e \u0441\u0432\u044f\u0437\u044b\u0432\u0430\u043d\u0438\u044f \u0432 Spring public class DictionaryServiceImpl implements DictionaryService {      @Override     public YandexAliceResponse talkYandexAlice(YandexAliceRequest request) {         return new YandexAliceResponse(new YASkillResponse(             \"\u041f\u0440\u0438\u0432\u0435\u0442! \u042f \u043f\u043e\u043c\u043e\u0433\u0443 \u0432\u0430\u043c \u0432\u044b\u0443\u0447\u0438\u0442\u044c \u0430\u043d\u0433\u043b\u0438\u0439\u0441\u043a\u0438\u0435 \u0441\u043b\u043e\u0432\u0430.\"));     } }<\/code><\/pre>\n<p>\u0417\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c \u0442\u0435\u0441\u0442 &#8212; \u043e\u043d \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442! \u041a\u043e\u043c\u043c\u0438\u0442\u0438\u043c \u043a\u043e\u0434. \u0422\u0435\u043f\u0435\u0440\u044c \u043c\u0435\u043d\u044f\u0442\u044c DictionaryController \u043d\u0435 \u043f\u0440\u0438\u0434\u0451\u0442\u0441\u044f, \u0442\u0430\u043a \u043a\u0430\u043a API \u043d\u0435 \u043c\u0435\u043d\u044f\u0435\u0442\u0441\u044f. \u0418\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u0431\u0438\u0437\u043d\u0435\u0441-\u043b\u043e\u0433\u0438\u043a\u0438 \u0432 \u0441\u0435\u0440\u0432\u0438\u0441\u0435 \u043d\u0430 \u0437\u0430\u0442\u0440\u0430\u0433\u0438\u0432\u0430\u044e\u0442 API, \u0447\u0442\u043e \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043d\u0430\u043c \u0448\u0430\u0433 \u0437\u0430 \u0448\u0430\u0433\u043e\u043c \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0442\u044c \u043d\u043e\u0432\u044b\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u0438.<\/p>\n<h3>\u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c \u0441\u043b\u043e\u0432\u0430<\/h3>\n<p>\u041a\u043e\u0433\u0434\u0430 API \u0433\u043e\u0442\u043e\u0432\u043e, \u0432\u0440\u0435\u043c\u044f \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u043e\u0441\u043d\u043e\u0432\u043d\u0443\u044e \u0431\u0438\u0437\u043d\u0435\u0441-\u043b\u043e\u0433\u0438\u043a\u0443 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f. \u041f\u043e\u0441\u043b\u0435 \u043f\u0440\u0438\u0432\u0435\u0442\u0441\u0442\u0432\u0438\u044f \u043d\u0430\u0432\u044b\u0439 \u0441\u0440\u0430\u0437\u0443 \u043d\u0430\u0447\u043d\u0451\u0442 \u0441\u043f\u0440\u0430\u0448\u0438\u0432\u0430\u0442\u044c \u0441\u043b\u043e\u0432\u0430. \u0414\u0438\u0430\u043b\u043e\u0433 \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u0442\u044c \u0442\u0430\u043a:<\/p>\n<blockquote>\n<p>&#8212; \u041f\u0440\u0438\u0432\u0435\u0442! \u042f \u043f\u043e\u043c\u043e\u0433\u0443 \u0432\u0430\u043c \u0432\u044b\u0443\u0447\u0438\u0442\u044c \u0430\u043d\u0433\u043b\u0438\u0439\u0441\u043a\u0438\u0435 \u0441\u043b\u043e\u0432\u0430. \u041a\u0430\u043a \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0438\u0442\u0441\u044f \u043d\u0430 \u0440\u0443\u0441\u0441\u043a\u0438\u0439 \u044f\u0437\u044b\u043a \u0441\u043b\u043e\u0432\u043e &#171;objection&#187;?<\/p>\n<p>&#8212; \u0412\u043e\u0437\u0440\u0430\u0436\u0435\u043d\u0438\u0435<\/p>\n<p>&#8212; \u0412\u0435\u0440\u043d\u043e! \u041a\u0430\u043a \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0438\u0442\u0441\u044f \u043d\u0430 \u0430\u043d\u0433\u043b\u0438\u0439\u0441\u043a\u0438\u0439 \u044f\u0437\u044b\u043a \u0441\u043b\u043e\u0432\u043e &#171;\u043f\u0435\u0440\u0441\u0438\u043a&#187;?<\/p>\n<p>&#8212; Apple<\/p>\n<p>&#8212; \u041d\u0435 \u0432\u0435\u0440\u043d\u043e. \u0421\u043b\u043e\u0432\u043e &#171;\u043f\u0435\u0440\u0441\u0438\u043a&#187; \u043f\u043e-\u0430\u043d\u0433\u043b\u0438\u0439\u0441\u043a\u0438 \u0431\u0443\u0434\u0435\u0442 &#171;peach&#187;. \u041a\u0430\u043a \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0438\u0442\u0441\u044f \u043d\u0430 \u0430\u043d\u0433\u043b\u0438\u0439\u0441\u043a\u0438\u0439 \u044f\u0437\u044b\u043a \u0441\u043b\u043e\u0432\u043e &#171;\u0447\u0438\u043f\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435&#187;?<\/p>\n<\/blockquote>\n<p>\u0418 \u0442\u0430\u043a \u0434\u0430\u043b\u0435\u0435. \u041f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044e \u043d\u0443\u0436\u043d\u043e \u0438\u043c\u0435\u0442\u044c \u0441\u043b\u043e\u0432\u0430\u0440\u044c \u0438 \u043f\u043e\u043c\u043d\u0438\u0442\u044c, \u043a\u0430\u043a\u043e\u0435 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0435\u0435 \u0441\u043b\u043e\u0432\u043e \u0431\u044b\u043b\u043e \u043f\u0440\u0435\u0434\u043b\u043e\u0436\u0435\u043d\u043e \u0434\u043b\u044f \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044e. \u0421\u043b\u043e\u0432\u0430\u0440\u044c \u0438 \u0441\u0435\u0441\u0441\u0438\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u0431\u0443\u0434\u0443\u0442 \u0445\u0440\u0430\u043d\u0438\u0442\u0441\u044f \u0432 \u0421\u0423\u0411\u0414. <\/p>\n<p>\u041f\u0438\u0448\u0435\u043c \u043d\u043e\u0432\u044b\u0439 \u0442\u0435\u0441\u0442 \u0443\u0436\u0435 \u043d\u0430 \u043b\u043e\u0433\u0438\u043a\u0443 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f. \u0415\u0441\u043b\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043d\u0430\u0447\u0430\u043b \u043d\u043e\u0432\u044b\u0439 \u0434\u0438\u0430\u043b\u043e\u0433 \u0441 \u0410\u043b\u0438\u0441\u043e\u0439, \u0437\u043d\u0430\u0447\u0438\u0442, \u043d\u0430\u0434\u043e \u0435\u0433\u043e \u043f\u043e\u043f\u0440\u0438\u0432\u0435\u0442\u0441\u0442\u0432\u043e\u0432\u0430\u0442\u044c, \u043f\u0440\u0435\u0434\u043b\u043e\u0436\u0438\u0442\u044c \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u043e\u0435 \u0441\u043b\u043e\u0432\u043e \u0434\u043b\u044f \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430 \u0438 \u0437\u0430\u043f\u043e\u043c\u043d\u0438\u0442\u044c \u044d\u0442\u043e \u0441\u043b\u043e\u0432\u043e. \u0421\u043e\u0437\u0434\u0430\u0451\u043c \u043a\u043b\u0430\u0441\u0441 DictionaryServiceTest \u0432 \u043f\u0430\u043a\u0435\u0442\u0435 com.example.dictionaryservice.service.<\/p>\n<pre><code class=\"java\">package com.example.dictionaryservice.service;  import static org.junit.jupiter.api.Assertions.assertEquals;  import com.example.dictionaryservice.dto.YandexAliceRequest; import com.example.dictionaryservice.dto.YandexAliceResponse; import java.util.List; import java.util.Optional; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.test.context.ActiveProfiles;  @SpringBootTest @ActiveProfiles(\"test\") class DictionaryServiceTest {     @Autowired     DictionaryService sut; \/\/ sut = System Under Test     private YandexAliceResponse response;     private final String sessionId = \"2eac4854-fce721f3-b845abba-20d60\";     @MockBean     private SessionRepository sessionRepository;     @MockBean     private DictionaryRepository dictionaryRepository;      @Test     void should_greet_user_and_ask_to_translate_random_word_and_store_new_session_when_new_session_started() {         \/\/ \u0432 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0435 \u043d\u0435\u0442 \u0441\u0435\u0441\u0441\u0438\u0438         when(sessionRepository.findById(sessionId)).thenReturn(Optional.empty());         \/\/ \u0438\u0437 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0430 \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u0438 \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u043e\u0435 \u0441\u043b\u043e\u0432\u043e         when(dictionaryRepository.findAll()).thenReturn(             List.of(new TermEntity(Language.ENGLISH, \"objection\"))         );          response = sut.talkYandexAlice(             new YandexAliceRequest(                 new YASkillRequest(\"\u0410\u043b\u0438\u0441\u0430, \u0437\u0430\u043f\u0443\u0441\u0442\u0438 \u043d\u0430\u0432\u044b\u043a \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0447\u0438\u043a\"),                 new YASession(sessionId)));          \/\/ \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c, \u0447\u0442\u043e \u0410\u043b\u0438\u0441\u0430 \u043f\u0440\u0435\u0434\u043b\u043e\u0436\u0438\u043b\u0430 \u043f\u0435\u0440\u0435\u0432\u0435\u0441\u0442\u0438 \u0441\u043b\u043e\u0432\u043e         assertEquals(\"\u041f\u0440\u0438\u0432\u0435\u0442! \u042f \u043f\u043e\u043c\u043e\u0433\u0443 \u0432\u0430\u043c \u0432\u044b\u0443\u0447\u0438\u0442\u044c \u0430\u043d\u0433\u043b\u0438\u0439\u0441\u043a\u0438\u0435 \u0441\u043b\u043e\u0432\u0430. \"             + \"\u041a\u0430\u043a \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0438\u0442\u0441\u044f \u043d\u0430 \u0440\u0443\u0441\u0441\u043a\u0438\u0439 \u044f\u0437\u044b\u043a \u0441\u043b\u043e\u0432\u043e objection?\",             response.getResponse().getText());         \/\/ \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c, \u0447\u0442\u043e \u0441\u0435\u0440\u0432\u0438\u0441 \u0437\u0430\u043f\u043e\u043c\u043d\u0438\u043b \u0441\u043b\u043e\u0432\u043e         verify(sessionRepository).save(             new SessionEntity(sessionId, Language.ENGLISH, \"objection\"));     } } <\/code><\/pre>\n<p>\u042d\u0442\u043e\u0442 \u043a\u043e\u0434 \u043d\u0435 \u0441\u043a\u043e\u043c\u043f\u0438\u043b\u0438\u0440\u0443\u0435\u0442\u0441\u044f, \u0442\u0430\u043a \u043a\u0430\u043a \u043c\u043d\u043e\u0433\u043e \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432 \u0438\u0437 \u043d\u0435\u0433\u043e \u043c\u044b \u0435\u0449\u0451 \u043d\u0435 \u043d\u0430\u043f\u0438\u0441\u0430\u043b\u0438. \u041d\u043e \u0437\u0430\u0442\u043e \u043c\u044b \u0441\u043f\u0440\u043e\u0435\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u043b\u0438 \u043f\u043e\u043d\u044f\u0442\u043d\u044b\u0439 \u0442\u0435\u0441\u0442. \u041e\u043d \u0441\u043b\u0435\u0434\u0443\u0435\u0442 \u043f\u0430\u0442\u0442\u0435\u0440\u043d\u0443 ARRANGE-ACT-ASSERT: \u0432\u043d\u0430\u0447\u0430\u043b\u0435 \u0433\u043e\u0442\u043e\u0432\u0438\u043c \u0434\u0430\u043d\u043d\u044b\u0435 \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f, \u0437\u0430\u0442\u0435\u043c \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u043c \u0442\u0435\u0441\u0442\u0438\u0440\u0443\u0435\u043c\u044b\u0439 \u043e\u0431\u044a\u0435\u043a\u0442 \u0438 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c \u043e\u0436\u0438\u0434\u0430\u0435\u043c\u044b\u0435 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u044b. \u0427\u0435\u0440\u0435\u0437 \u044d\u0442\u043e\u0442 \u0442\u0435\u0441\u0442 \u0438 \u043d\u0430\u0447\u0438\u043d\u0430\u0435\u043c \u043f\u0440\u043e\u0435\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043d\u0430\u0448\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435. <\/p>\n<p>\u0414\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441\u0442\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0445 \u043c\u0435\u0442\u043e\u0434\u043e\u0432 verify \u0438 when \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u043c \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443 Mockito \u0432 pom.xml.<\/p>\n<pre><code class=\"xml\">&lt;dependencies>   ...   &lt;dependency>   &lt;groupId>org.mockito&lt;\/groupId> &lt;artifactId>mockito-core&lt;\/artifactId> &lt;scope>test&lt;\/scope> &lt;\/dependency> &lt;\/dependencies><\/code><\/pre>\n<p>\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u043c \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0438 (entity) TermEntity \u0438 SessionEntity \u0432 \u043f\u0430\u043a\u0435\u0442\u0435 com.example.dictionaryservice.repository.entity.<\/p>\n<pre><code class=\"java\">package com.example.dictionaryservice.repository.entity;  import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.EnumType; import javax.persistence.Enumerated; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import lombok.AccessLevel; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import lombok.experimental.FieldDefaults;  @Data @NoArgsConstructor @AllArgsConstructor @FieldDefaults(level = AccessLevel.PRIVATE) @Entity(name = \"term\") \/\/\u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u0442\u0430\u0431\u043b\u0438\u0446\u044b public class TermEntity {     @Id \/\/ \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440     @GeneratedValue(strategy = GenerationType.IDENTITY)     Long id;      @Enumerated(EnumType.STRING) \/\/\u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0432 \u0442\u0430\u0431\u043b\u0438\u0446\u0435, \u043a\u0430\u043a \u0441\u0442\u0440\u043e\u043a\u0443     @Column(nullable = false)     Language language;      @Column(nullable = false)     String term;      public enum Language {         ENGLISH, RUSSIAN     } } <\/code><\/pre>\n<pre><code class=\"java\">@Data @NoArgsConstructor @AllArgsConstructor @FieldDefaults(level =  AccessLevel.PRIVATE) @Entity(name = \"session\") public class SessionEntity {     @Id     @Column(nullable = false)     String sessionId;      @Enumerated(EnumType.STRING)     @Column(nullable = false)     Language language;      @Column(nullable = false)     String term; } <\/code><\/pre>\n<p>\u041f\u0438\u0448\u0435\u043c \u043a\u043e\u0434 \u0434\u043b\u044f \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0435\u0432 SessionRepository \u0438 DictionaryRepository \u0432 \u043f\u0430\u043a\u0435\u0442\u0435 <code>com.example.dictionaryservice.repository<\/code>. \u041e\u043d \u0441\u043e\u0432\u0441\u0435\u043c \u043f\u0440\u043e\u0441\u0442\u043e\u0439, \u0442\u0430\u043a \u043a\u0430\u043a \u0443\u0436\u0435 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d \u0432 Spring JPA. <\/p>\n<pre><code class=\"java\">package com.example.dictionaryservice.repository;  import com.example.dictionaryservice.repository.entity.SessionEntity; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository;  @Repository public interface SessionRepository extends JpaRepository&lt;SessionEntity, String> {  }<\/code><\/pre>\n<pre><code class=\"java\">package com.example.dictionaryservice.repository;  import com.example.dictionaryservice.repository.entity.TermEntity; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository;  @Repository public interface DictionaryRepository extends JpaRepository&lt;TermEntity, Long> {     }<\/code><\/pre>\n<p>\u0427\u0442\u043e\u0431\u044b \u0440\u0430\u0431\u043e\u0442\u0430\u043b\u0438 \u0442\u0435\u0441\u0442\u044b \u0435\u0449\u0451, \u043d\u0443\u0436\u043d\u043e \u0438 \u043f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u0438\u0442\u044c \u0441\u0445\u0435\u043c\u0443 \u0434\u043b\u044f \u0421\u0423\u0411\u0414. \u0414\u043e\u0431\u0430\u0432\u0438\u043c changeSet \u0432 \u0436\u0443\u0440\u043d\u0430\u043b \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0439 Liquibase<\/p>\n<pre><code class=\"xml\">&lt;databaseChangeLog xmlns=\"http:\/\/www.liquibase.org\/xml\/ns\/dbchangelog\"     xmlns:xsi=\"http:\/\/www.w3.org\/2001\/XMLSchema-instance\"     xsi:schemaLocation=\"http:\/\/www.liquibase.org\/xml\/ns\/dbchangelog http:\/\/www.liquibase.org\/xml\/ns\/dbchangelog\/dbchangelog-3.3.xsd\">     &lt;changeSet id=\"1\" author=\"author@example.com\" context=\"schema\">         &lt;sql>             -- \u0441\u043a\u0440\u0438\u043f\u0442\u044b \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0442\u0430\u0431\u043b\u0438\u0446             create table dict.term (                 id bigserial not null,                 language varchar(255) not null,                 term varchar(255) not null,                 constraint pk_term primary key (id)             );             create table dict.session (                 session_id varchar(255) not null,                 language varchar(255) not null,                 term varchar(255) not null,                 constraint pk_session primary key (session_id)             );         &lt;\/sql>         &lt;rollback>             -- \u0441\u043a\u0440\u0438\u043f\u0442\u044b \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f \u0442\u0430\u0431\u043b\u0438\u0446 \u0434\u043b\u044f \u043e\u0442\u043a\u0430\u0442\u0430             drop table dict.session;             drop table dict.term;         &lt;\/rollback>     &lt;\/changeSet> &lt;\/databaseChangeLog><\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043c\u043e\u0436\u043d\u043e \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u0431\u0438\u0437\u043d\u0435\u0441-\u043b\u043e\u0433\u0438\u043a\u0443 \u0432 DictionaryServiceImpl<\/p>\n<pre><code>@Service @Slf4j @RequiredArgsConstructor public class DictionaryServiceImpl implements DictionaryService {     private final SessionRepository sessionRepository;     private final DictionaryRepository dictionaryRepository;     private final Random random = new Random();      @Override     public YandexAliceResponse talkYandexAlice(YandexAliceRequest request) {         String responseText = \"\u041f\u0440\u0438\u0432\u0435\u0442! \u042f \u043f\u043e\u043c\u043e\u0433\u0443 \u0432\u0430\u043c \u0432\u044b\u0443\u0447\u0438\u0442\u044c \u0430\u043d\u0433\u043b\u0438\u0439\u0441\u043a\u0438\u0435 \u0441\u043b\u043e\u0432\u0430.\";          Objects.requireNonNull(request);         Objects.requireNonNull(request.getRequest());         Objects.requireNonNull(request.getRequest().getCommand());         Objects.requireNonNull(request.getSession());         String sessionId = request.getSession().getSessionId();         Objects.requireNonNull(sessionId);          if (sessionRepository.findById(sessionId).isEmpty()) {             List&lt;TermEntity> terms = dictionaryRepository.findAll();             if (!terms.isEmpty()) {                 TermEntity term = terms.get(random.nextInt(terms.size()));                 responseText += String.format(\" \u041a\u0430\u043a \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0438\u0442\u0441\u044f \u043d\u0430 %s \u044f\u0437\u044b\u043a \u0441\u043b\u043e\u0432\u043e %s?\",                     Language.ENGLISH.equals(term.getLanguage()) ? \"\u0440\u0443\u0441\u0441\u043a\u0438\u0439\" : \"\u0430\u043d\u0433\u043b\u0438\u0439\u0441\u043a\u0438\u0439\",                     term.getTerm()                 );                  sessionRepository.save(new SessionEntity(sessionId, term.getLanguage(), term.getTerm()));             }         }          return new YandexAliceResponse(new YASkillResponse(responseText));     } }<\/code><\/pre>\n<p>\u0422\u0435\u0441\u0442 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442. \u041a\u043e\u043c\u043c\u0438\u0442\u0438\u043c \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f. <\/p>\n<h3>\u041f\u0440\u043e\u0432\u0435\u0440\u044f\u043c \u043f\u0435\u0440\u0435\u0432\u043e\u0434 \u0441\u043b\u043e\u0432\u0430<\/h3>\n<p>\u0414\u043e\u0431\u0430\u0432\u0438\u043c \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0443 \u0441\u043b\u043e\u0432\u0430, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u043f\u0435\u0440\u0435\u0432\u0451\u043b \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c. \u0421\u043d\u043e\u0432\u0430 \u043d\u0430\u0447\u0438\u043d\u0430\u0435\u043c \u0441 \u0442\u0435\u0441\u0442\u0430.<\/p>\n<pre><code class=\"java\">    @Test     void should_check_term_translation_and_ask_to_translate_another_word_when_session_exists() {         Long termId = 1001L;         Long termTranslationId = 1002L;         \/\/ \u0432 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0435 \u0435\u0441\u0442\u044c \u0441\u0435\u0441\u0441\u0438\u0438         when(sessionRepository.findById(sessionId)).thenReturn(Optional.of(new SessionEntity(sessionId, Language.ENGLISH, \"objection\")));         when(dictionaryRepository.findTranslation(Language.ENGLISH.toString(), \"objection\")).thenReturn(             Optional.of(new TermEntity(termId, Language.RUSSIAN, \"\u0432\u043e\u0437\u0440\u0430\u0436\u0435\u043d\u0438\u0435\"))         );         when(dictionaryRepository.findAll()).thenReturn(             List.of(new TermEntity(null, Language.RUSSIAN, \"\u043f\u0435\u0440\u0441\u0438\u043a\"))         );          response = sut.talkYandexAlice(             new YandexAliceRequest(                 new YASkillRequest(\"\u0432\u043e\u0437\u0440\u0430\u0436\u0435\u043d\u0438\u0435\"),                 new YASession(sessionId)));          \/\/ \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c, \u0447\u0442\u043e \u0410\u043b\u0438\u0441\u0430 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u043b\u0430 \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430 \u0438 \u043f\u0440\u0435\u0434\u043b\u043e\u0436\u0438\u043b\u0430 \u043f\u0435\u0440\u0435\u0432\u0435\u0441\u0442\u0438 \u043d\u043e\u0432\u043e\u0435 \u0441\u043b\u043e\u0432\u043e         assertEquals(\"\u0412\u0435\u0440\u043d\u043e! \u041a\u0430\u043a \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0438\u0442\u0441\u044f \u043d\u0430 \u0430\u043d\u0433\u043b\u0438\u0439\u0441\u043a\u0438\u0439 \u044f\u0437\u044b\u043a \u0441\u043b\u043e\u0432\u043e \u043f\u0435\u0440\u0441\u0438\u043a?\",             response.getResponse().getText());         \/\/ \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c, \u0447\u0442\u043e \u0441\u0435\u0440\u0432\u0438\u0441 \u0437\u0430\u043f\u043e\u043c\u043d\u0438\u043b \u043d\u043e\u0432\u043e\u0435 \u0441\u043b\u043e\u0432\u043e         verify(sessionRepository).save(             new SessionEntity(sessionId, Language.RUSSIAN, \"\u043f\u0435\u0440\u0441\u0438\u043a\"));     }<\/code><\/pre>\n<p>\u0414\u043e\u0431\u0430\u0432\u0438\u043c \u043d\u043e\u0432\u044b\u0439 \u043c\u0435\u0442\u043e\u0434 \u0432 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0439 DictionaryRepository.<\/p>\n<pre><code class=\"java\">@Repository public interface DictionaryRepository extends JpaRepository&lt;TermEntity, Long> {     @Query(value = \" select t1.* \"         + \" from dict.term t1 \"         + \" join dict.term_reference r1 on r1.term1_id = t1.id \"         + \" join dict.term t2 on r1.term2_id = t2.id \"         + \" where t2.term = :termToTranslate and t2.language = :language\", nativeQuery = true)     Optional&lt;TermEntity> findTranslation(String language, String termToTranslate); } <\/code><\/pre>\n<p>\u0414\u043e\u0440\u0430\u0431\u043e\u0442\u0430\u0435\u043c \u043b\u043e\u0433\u0438\u043a\u0443 \u0432 DictionaryServiceImpl.<\/p>\n<pre><code class=\"java\">@Service @Slf4j @RequiredArgsConstructor public class DictionaryServiceImpl implements DictionaryService {     private final SessionRepository sessionRepository;     private final DictionaryRepository dictionaryRepository;     private final Random random = new Random();      @Override     public YandexAliceResponse talkYandexAlice(YandexAliceRequest request) {         String responseText = \"\u041f\u0440\u0438\u0432\u0435\u0442! \u042f \u043f\u043e\u043c\u043e\u0433\u0443 \u0432\u0430\u043c \u0432\u044b\u0443\u0447\u0438\u0442\u044c \u0430\u043d\u0433\u043b\u0438\u0439\u0441\u043a\u0438\u0435 \u0441\u043b\u043e\u0432\u0430.\";          Objects.requireNonNull(request);         Objects.requireNonNull(request.getRequest());         Objects.requireNonNull(request.getRequest().getCommand());         Objects.requireNonNull(request.getSession());         String sessionId = request.getSession().getSessionId();         Objects.requireNonNull(sessionId);          Optional&lt;SessionEntity> session = sessionRepository.findById(sessionId);         if (session.isPresent()) {             Optional&lt;TermEntity> translation = dictionaryRepository.findTranslation(session.get().getLanguage().toString(), session.get().getTerm());             if (translation.isPresent()) {                 if (translation.get().getTerm().equalsIgnoreCase(request.getRequest().getCommand())) {                     responseText = \"\u0412\u0435\u0440\u043d\u043e!\";                 } else {                     responseText = \"\u041d\u0435 \u0432\u0435\u0440\u043d\u043e!\";                 }             }         }         List&lt;TermEntity> terms = dictionaryRepository.findAll();         if (!terms.isEmpty()) {             TermEntity term = terms.get(random.nextInt(terms.size()));             responseText += String.format(\" \u041a\u0430\u043a \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0438\u0442\u0441\u044f \u043d\u0430 %s \u044f\u0437\u044b\u043a \u0441\u043b\u043e\u0432\u043e %s?\",                 Language.ENGLISH.equals(term.getLanguage()) ? \"\u0440\u0443\u0441\u0441\u043a\u0438\u0439\" : \"\u0430\u043d\u0433\u043b\u0438\u0439\u0441\u043a\u0438\u0439\",                 term.getTerm()             );              sessionRepository.save(new SessionEntity(sessionId, term.getLanguage(), term.getTerm()));         }          return new YandexAliceResponse(new YASkillResponse(responseText));     } }<\/code><\/pre>\n<p>\u0422\u0435\u0441\u0442 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 &#8212; \u0434\u0435\u043b\u0430\u0435\u043c \u043a\u043e\u043c\u043c\u0438\u0442.<\/p>\n<p>\u0414\u043e\u0431\u0430\u0432\u0438\u043c \u0432 \u0436\u0443\u0440\u043d\u0430\u043b \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0439 Liquibase \u0442\u0430\u0431\u043b\u0438\u0446\u0443 \u0441\u0432\u044f\u0437\u0435\u0439 \u0438 \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e \u0434\u0430\u043d\u043d\u044b\u0445.<\/p>\n<pre><code class=\"xml\">    &lt;changeSet id=\"2\" author=\"author@example.com\" context=\"schema\">         &lt;sql>             create table dict.term_reference (                 id bigserial not null,                 term1_id bigint not null,                 term2_id bigint not null,                 constraint pk_term_reference primary key (id)             );         &lt;\/sql>         &lt;rollback>             drop table dict.term_reference;         &lt;\/rollback>     &lt;\/changeSet>     &lt;changeSet id=\"3\" author=\"author@example.com\" context=\"data\">         &lt;sql>             insert into dict.term values (default, 'ENGLISH','peach');             insert into dict.term values (default, 'RUSSIAN','\u043f\u0435\u0440\u0441\u0438\u043a');             insert into dict.term_reference (term1_id, term2_id)             select t1.id, t2.id             from dict.term t1             cross join dict.term t2             where t1.term = 'peach' and t2.term = '\u043f\u0435\u0440\u0441\u0438\u043a'             ;             insert into dict.term_reference (term1_id, term2_id)             select t1.id, t2.id             from dict.term t1             cross join dict.term t2             where t2.term = 'peach' and t1.term = '\u043f\u0435\u0440\u0441\u0438\u043a'             ;         &lt;\/sql>         &lt;rollback>             delete from dict.term_reference;             delete from dict.term;         &lt;\/rollback>     &lt;\/changeSet><\/code><\/pre>\n<p>\u041c\u043e\u0436\u043d\u043e \u0441\u043e\u0431\u0440\u0430\u0442\u044c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0438 \u0440\u0430\u0437\u0432\u0435\u0440\u043d\u0443\u0442\u044c \u0435\u0433\u043e \u0432 \u043e\u0431\u043b\u0430\u0447\u043d\u043e\u0439 \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u0435.<\/p>\n<pre><code>mvnw install<\/code><\/pre>\n<h2>\u0420\u0430\u0437\u0432\u0451\u0440\u0442\u044b\u0432\u0430\u043d\u0438\u0435 \u0432 \u042f\u043d\u0434\u0435\u043a\u0441.\u041e\u0431\u043b\u0430\u043a\u0435<\/h2>\n<p>\u0414\u043b\u044f \u0440\u0430\u0437\u0432\u0451\u0440\u0442\u044b\u0432\u0430\u043d\u0438\u044f \u043f\u043e\u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u0441\u0435\u0440\u0432\u0435\u0440 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439 \u0438 \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u043c\u044b\u0439 \u043e\u0431\u043b\u0430\u043a\u043e\u043c \u0441\u0435\u0440\u0432\u0435\u0440 \u0421\u0423\u0411\u0414 PostgreSQL. \u0414\u043b\u044f \u044d\u043a\u0441\u043f\u0435\u0440\u0438\u043c\u0435\u043d\u0442\u043e\u0432 \u043f\u043e\u0434\u043e\u0439\u0434\u0443\u0442 \u043c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u044b\u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438. <\/p>\n<h3>\u0421\u0435\u0440\u0432\u0435\u0440 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439<\/h3>\n<div>\n<div class=\"table\">\n<table>\n<tbody>\n<tr>\n<td data-colwidth=\"223\" width=\"223\">\n<p>\u041f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u0430<\/p>\n<\/td>\n<td>\n<p>Intel Cascase Lake<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td data-colwidth=\"223\" width=\"223\">\n<p>vCPU  <\/p>\n<\/td>\n<td>\n<p>2<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td data-colwidth=\"223\" width=\"223\">\n<p>\u0413\u0430\u0440\u0430\u043d\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u0430\u044f \u0434\u043e\u043b\u044f vCPU  <\/p>\n<\/td>\n<td>\n<p>5%<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td data-colwidth=\"223\" width=\"223\">\n<p>RAM  <\/p>\n<\/td>\n<td>\n<p>2GB<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td data-colwidth=\"223\" width=\"223\">\n<p>OS<\/p>\n<\/td>\n<td>\n<p>CentOS 8<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td data-colwidth=\"223\" width=\"223\">\n<p>HDD<\/p>\n<\/td>\n<td>\n<p>20GB<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<\/div>\n<h3>\u0421\u0435\u0440\u0432\u0435\u0440 PostgreSQL<\/h3>\n<div>\n<div class=\"table\">\n<table>\n<tbody>\n<tr>\n<td>\n<p>\u0422\u0438\u043f  <\/p>\n<\/td>\n<td>\n<p>burstable<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>\u0420\u0430\u0437\u043c\u0435\u0440<\/p>\n<\/td>\n<td>\n<p><strong>b2.nano<\/strong>\u00a0(2 vCPU, 5% vCPU rate, 2\u00a0GB RAM)  <\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>HDD<\/p>\n<\/td>\n<td>\n<p><strong>10\u00a0GB <\/strong>network-hdd  <\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<\/div>\n<h3>\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439<\/h3>\n<p>\u041f\u043e\u0441\u043b\u0435 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0432\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u043e\u0439 \u043c\u0430\u0448\u0438\u043d\u044b \u043b\u043e\u0433\u0438\u043d\u0438\u043c\u0441\u044f \u043d\u0430 \u043d\u0435\u0451 \u0447\u0435\u0440\u0435\u0437 SSH \u043f\u043e \u043f\u0443\u0431\u043b\u0438\u0447\u043d\u043e\u043c\u0443 IP-\u0430\u0434\u0440\u0435\u0441\u0443. \u041d\u0443\u0436\u043d\u043e \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c JVM 11, \u043a\u0430\u043a \u0441\u0440\u0435\u0434\u0443 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f Spring Boot, Nginx \u0434\u043b\u044f \u0442\u0435\u0440\u043c\u0438\u043d\u0430\u0446\u0438\u0438 SSL \u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u0441\u0435\u0440\u0432\u0438\u0441.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/5cb\/420\/e4c\/5cb420e4cae44c67e97bb9cb59a1b78f.png\" alt=\"\u0421\u043e\u0437\u0434\u0430\u043d\u043d\u0430\u044f \u0432\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u0430\u044f \u043c\u0430\u0448\u0438\u043d\u0430\" title=\"\u0421\u043e\u0437\u0434\u0430\u043d\u043d\u0430\u044f \u0432\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u0430\u044f \u043c\u0430\u0448\u0438\u043d\u0430\" width=\"1039\" height=\"202\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/5cb\/420\/e4c\/5cb420e4cae44c67e97bb9cb59a1b78f.png\"\/><figcaption>\u0421\u043e\u0437\u0434\u0430\u043d\u043d\u0430\u044f \u0432\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u0430\u044f \u043c\u0430\u0448\u0438\u043d\u0430<\/figcaption><\/figure>\n<p>\u041a\u043e\u043f\u0438\u0440\u0443\u0435\u043c \u0447\u0435\u0440\u0435\u0437 SCP Spring Boot Jar.<\/p>\n<pre><code class=\"powershell\">scp -i C:\\path\\to\\private_key C:\\app\\target\\dictionary-service-0.0.1-SNAPSHOT.jar user@178.154.206.196:\/home\/user\/app.jar ssh -i C:\\path\\to\\private_key user@178.154.206.196<\/code><\/pre>\n<pre><code class=\"bash\">#\u0422\u0430\u043a \u043a\u0430\u043a CentOS \u0437\u0430\u043a\u043e\u043d\u0447\u0438\u043b \u0441\u0432\u043e\u044e \u0436\u0438\u0437\u043d\u044c 31 \u0434\u0435\u043a\u0430\u0431\u0440\u044f 2021, https:\/\/www.centos.org\/centos-linux-eol\/ #\u0442\u043e \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u0448\u0430\u043c\u0430\u043d\u0441\u0442\u0432\u0430 \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u043c\u0435\u043d\u0435\u0434\u0436\u0435\u0440\u0430 \u043f\u0430\u043a\u0435\u0442\u043e\u0432 yum  sudo sed -i 's\/mirrorlist\/#mirrorlist\/g' \/etc\/yum.repos.d\/CentOS-Linux-* sudo sed -i 's|#baseurl=http:\/\/mirror.centos.org|baseurl=http:\/\/vault.centos.org|g' \/etc\/yum.repos.d\/CentOS-Linux-*  # \u0423\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u043c Java 11 sudo dnf install epel-release sudo yum -y install java-11-openjdk-headless.x86_64 # \u0423\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u043c Nginx \u0434\u043b\u044f \u043f\u0440\u043e\u043a\u0441\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u0438 \u0442\u0435\u0440\u043c\u0438\u043d\u0430\u0446\u0438\u0438 SSL sudo yum -y install nginx # \u043f\u043e\u043b\u0435\u0437\u043d\u044b\u0435 \u0443\u0442\u0438\u043b\u0438\u0442\u044b \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b sudo yum -y install vim sudo yum -y install wget # \u041a\u043b\u0438\u0435\u043d\u0442 PostgreSQL \u0434\u043b\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f \u0441 \u0421\u0423\u0411\u0414 sudo yum -y install postgresql  #\u0441\u043e\u0437\u0434\u0430\u0451\u043c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0438 \u0440\u0430\u0431\u043e\u0447\u0443\u044e \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u044e \u0434\u043b\u044f \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f sudo useradd app sudo mkdir \/opt\/app sudo cp \/home\/user\/app.jar \/opt\/app\/app.jar sudo chown -R app:app \/opt\/app sudo chmod -R 0500 \/opt\/app  #\u043f\u0440\u043e\u0431\u0443\u0435\u043c \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u0441\u0435\u0440\u0432\u0438\u0441 java -Dspring.profiles.active=test -jar app.jar &amp; #\u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u043c \u0442\u0435\u0441\u0442\u043e\u0432\u044b\u0439 \u0437\u0430\u043f\u0440\u043e\u0441 curl -X POST http:\/\/localhost:8080\/api\/v1\/dictionary\/yandex-alice-skill -H 'Content-Type: application\/json' -d '{\"request\":{\"command\":\"test\"},\"session\":{\"session_id\":\"id\"}}' {\"response\":{\"text\":\"\u041f\u0440\u0438\u0432\u0435\u0442! \u042f \u043f\u043e\u043c\u043e\u0433\u0443 \u0432\u0430\u043c \u0432\u044b\u0443\u0447\u0438\u0442\u044c \u0430\u043d\u0433\u043b\u0438\u0439\u0441\u043a\u0438\u0435 \u0441\u043b\u043e\u0432\u0430.\",\"end_session\":false},\"version\":\"1.0\"}  #\u0423\u0441\u043f\u0435\u0448\u043d\u043e! \u041e\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u043c Java. fg #Ctrl+C<\/code><\/pre>\n<h4>\u0423\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f \u0441 \u0421\u0423\u0411\u0414<\/h4>\n<p>\u0421\u043a\u043e\u043f\u0438\u0440\u0443\u0435\u043c \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u0421\u0423\u0411\u0414 \u0438\u0437 \u043a\u043e\u043d\u0441\u043e\u043b\u0438 \u042f\u043d\u0434\u0435\u043a\u0441.\u041e\u0431\u043b\u0430\u043a\u0430.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/d3e\/95d\/fc0\/d3e95dfc0661884e2f2a6e536bc2850a.png\" alt=\"\u041a\u043e\u043d\u0441\u043e\u043b\u044c Managed Service for PostgreSQL\" title=\"\u041a\u043e\u043d\u0441\u043e\u043b\u044c Managed Service for PostgreSQL\" width=\"712\" height=\"231\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/d3e\/95d\/fc0\/d3e95dfc0661884e2f2a6e536bc2850a.png\"\/><figcaption>\u041a\u043e\u043d\u0441\u043e\u043b\u044c Managed Service for PostgreSQL<\/figcaption><\/figure>\n<pre><code class=\"bash\">#ssh -i C:\\path\\to\\private_key user@178.154.206.196  psql -h your-db-host-name.mdb.yandexcloud.net -p 6432 -u your-db-user your-db-name #\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0441\u0445\u0435\u043c\u044b app=> create schema liquibase; CREATE SCHEMA app=> create schema dict; CREATE SCHEMA app => \\q #Ctrl+D  #\u041f\u043e\u043f\u0440\u043e\u0431\u0443\u0435\u043c \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0441 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\u043c\u0438 \u0411\u0414 \u043f\u043e\u0434 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u043c app sudo -s sudo -s -u app  # \u0441\u043a\u0430\u0447\u0438\u0432\u0430\u0435\u043c \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043a\u0430\u0442 \u042f\u043d\u0434\u0435\u043a\u0441\u0430, \u0447\u0442\u043e\u0431\u044b \u043c\u043e\u0436\u043d\u043e \u0431\u044b\u043b\u043e \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0442\u044c\u0441\u044f \u043a \u0411\u0414 \u0447\u0435\u0440\u0435\u0437 SSL # https:\/\/cloud.yandex.ru\/docs\/managed-postgresql\/operations\/connect mkdir -p \/home\/app\/.postgresql wget \"https:\/\/storage.yandexcloud.net\/cloud-certs\/CA.pem\" -O ~\/.postgresql\/root.crt chmod 0600 ~\/.postgresql\/root.crt cd \/opt\/app\/  export \"SPRING_DATASOURCE_URL=jdbc:postgresql:\/\/your-db-host-name.mdb.yandexcloud.net:6432\/your-db-name?targetServerType=master&amp;ssl=true\" export SPRING_DATASOURCE_USERNAME=your-db-user export SPRING_DATASOURCE_PASSWORD=your-db-user-password  java -Dspring.jpa.properties.hibernate.default_schema=dict -jar app.jar  2022-02-13 15:48:38.202  INFO 18807 --- [main] liquibase.lockservice  : Successfully acquired change log lock 2022-02-13 15:48:44.043  INFO 18807 --- [main] liquibase.changelog    : Creating database history table with name: liquibase.databasechangelog 2022-02-13 15:48:44.229  INFO 18807 --- [main] liquibase.changelog    : Reading from liquibase.databasechangelog 2022-02-13 15:48:44.749  INFO 18807 --- [main] liquibase.changelog    : Custom SQL executed 2022-02-13 15:48:44.786  INFO 18807 --- [main] liquibase.changelog    : ChangeSet db.changelog.xml::1::author@example.com ran successfully in 259ms 2022-02-13 15:48:44.983  INFO 18807 --- [main] liquibase.changelog    : Custom SQL executed 2022-02-13 15:48:45.048  INFO 18807 --- [main] liquibase.changelog    : ChangeSet db.changelog.xml::2::author@example.com ran successfully in 167ms 2022-02-13 15:48:45.357  INFO 18807 --- [main] liquibase.changelog    : Custom SQL executed 2022-02-13 15:48:45.375  INFO 18807 --- [main] liquibase.changelog    : ChangeSet db.changelog.xml::3::author@example.com ran successfully in 247ms 2022-02-13 15:48:45.536  INFO 18807 --- [main] liquibase.lockservice  : Successfully released change log lock  # \u041a \u0421\u0423\u0411\u0414 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u043b\u0438\u0441\u044c \u0443\u0441\u043f\u0435\u0448\u043d\u043e, \u043d\u0430\u0436\u0438\u043c\u0430\u0435\u043c Ctrl+C \u0434\u043b\u044f \u043e\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0438 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f<\/code><\/pre>\n<p>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0441\u0435\u0440\u0432\u0438\u0441 \u0434\u043b\u044f \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f, \u0441\u043e\u0437\u0434\u0430\u0432 \u0444\u0430\u0439\u043b <code>\/etc\/systemd\/system\/dict.service <\/code>\u0441\u043e \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u044b\u043c.<\/p>\n<pre><code class=\"bash\">[Unit] Description=Dictionary Spring Boot Application After=syslog.target network.target  [Service] User=app Environment=SPRING_DATASOURCE_URL=jdbc:postgresql:\/\/your-db-host-name.mdb.yandexcloud.net:6432\/your-db-name?targetServerType=master&amp;ssl=true Environment=SPRING_DATASOURCE_USERNAME=your-db-user Environment=SPRING_DATASOURCE_PASSWORD=your-db-user-password WorkingDirectory=\/opt\/app ExecStart=java -Dspring.jpa.properties.hibernate.default_schema=dict -jar app.jar SuccessExitStatus=143  [Install] WantedBy=multi-user.target <\/code><\/pre>\n<p>\u0421\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u043c \u0444\u0430\u0439\u043b \u0438 \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c \u0441\u0438\u0441\u0442\u0435\u043c\u043d\u0443\u044e \u0441\u043b\u0443\u0436\u0431\u0443.<\/p>\n<pre><code class=\"bash\">sudo systemctl daemon-reload # \u0412\u043a\u043b\u044e\u0447\u0430\u0435\u043c \u0441\u0435\u0440\u0432\u0438\u0441 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f sudo systemctl enable dict # \u0417\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c \u0441\u0435\u0440\u0432\u0438\u0441 sudo systemctl start dict # \u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c \u0441\u0442\u0430\u0442\u0443\u0441 \u0441\u0435\u0440\u0432\u0438\u0441\u0430  sudo systemctl status dict  dict.service - Dictionary Spring Boot Application    Loaded: loaded (\/etc\/systemd\/system\/dict.service; enabled; vendor preset: disabled)    Active: active (running) since Sun 2022-02-13 17:27:06 UTC; 1min 24s ago  Main PID: 19448 (java)     Tasks: 24 (limit: 11209)    Memory: 285.8M    CGroup: \/system.slice\/dict.service            \u2514\u250019448 \/usr\/bin\/java -Dspring.jpa.properties.hibernate.default_schema=dict -jar app.jar <\/code><\/pre>\n<h3>\u0422\u0435\u0440\u043c\u0438\u043d\u0430\u0446\u0438\u044f SSL<\/h3>\n<p>\u0421\u0435\u0440\u0432\u0438\u0441 \u0434\u043e\u0441\u0442\u0443\u043f\u0435\u043d \u0447\u0435\u0440\u0435\u0437 \u0418\u043d\u0442\u0435\u0440\u043d\u0435\u0442 \u043f\u043e \u043f\u043e\u0440\u0442\u0443 8080: \u043c\u043e\u0436\u043d\u043e \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u043a\u043e\u043c\u0430\u043d\u0434\u0443 curl \u0441 \u0443\u043a\u0430\u0437\u0430\u043d\u0438\u0435\u043c \u043f\u0443\u0431\u043b\u0438\u0447\u043d\u043e\u0433\u043e IP-\u0430\u0434\u0440\u0435\u0441\u0430 \u0438 \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c \u044d\u0442\u043e. \u041d\u043e \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u0447\u0435\u0440\u0435\u0437 HTTP \u043f\u043e 8080 \u043d\u0435\u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e. \u042f\u043d\u0434\u0435\u043a\u0441.\u0414\u0438\u0430\u043b\u043e\u0433\u0438 \u0442\u0440\u0435\u0431\u0443\u044e\u0442 \u0440\u0430\u0431\u043e\u0442\u044b \u0447\u0435\u0440\u0435\u0437 HTTPS, \u0442\u0430\u043a \u043a\u0430\u043a \u043d\u0435\u0437\u0430\u0448\u0438\u0444\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u0442\u0440\u0430\u0444\u0438\u043a HTTP \u043c\u043e\u0436\u0435\u0442 \u043f\u0435\u0440\u0435\u0445\u0432\u0430\u0442\u044b\u0432\u0430\u0442\u044c\u0441\u044f \u0438 \u043f\u043e\u0434\u043c\u0435\u043d\u044f\u0442\u044c\u0441\u044f, \u0447\u0442\u043e \u043d\u0435\u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u043e. \u0414\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u0447\u0435\u0440\u0435\u0437 HTTPS \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u0432\u044b\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043a\u0430\u0442, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043d\u0430 Let&#8217;s Encrypt.<\/p>\n<p>\u0414\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u043d\u0443\u0436\u0435\u043d \u0434\u043e\u043c\u0435\u043d. \u041a\u0443\u043f\u0438\u0442\u044c \u0435\u0433\u043e \u043c\u043e\u0436\u043d\u043e \u0443 \u0440\u0430\u0437\u043d\u044b\u0445 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0442\u043e\u0440\u043e\u0432. \u041f\u043e\u0441\u043b\u0435 \u043f\u043e\u043a\u0443\u043f\u043a\u0438 \u0434\u043e\u043c\u0435\u043d \u043f\u0440\u0438\u0432\u044f\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u043a \u043f\u0443\u0431\u043b\u0438\u0447\u043d\u043e\u043c IP-\u0430\u0434\u0440\u0435\u0441\u0443 \u0432\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u043e\u0439 \u043c\u0430\u0448\u0438\u043d\u044b \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439 \u0432 \u043a\u043e\u043d\u0441\u043e\u043b\u0438 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0442\u043e\u0440\u0430. <\/p>\n<p>\u0414\u043b\u044f \u0432\u044b\u043f\u0443\u0441\u043a\u0430 \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u0430 \u043d\u0430 Let&#8217;s Encrypt \u043d\u0443\u0436\u0435\u043d \u0440\u0430\u0431\u043e\u0442\u0430\u044e\u0449\u0438\u0439 HTTP \u0441\u0435\u0440\u0432\u0435\u0440. \u041c\u044b \u0440\u0430\u043d\u0435\u0435 \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u043b\u0438 Nginx, \u0438 \u043e\u043d \u0437\u0430\u043f\u0443\u0449\u0435\u043d. \u041c\u043e\u0436\u043d\u043e \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c \u044d\u0442\u043e, \u0437\u0430\u0439\u0434\u044f \u0447\u0435\u0440\u0435\u0437 \u0431\u0440\u0430\u0443\u0437\u0435\u0440, \u0438 \u0443\u0432\u0438\u0434\u0435\u0442\u044c \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e. \u041d\u0430\u0441\u0442\u0440\u043e\u0438\u043c \u0432 nginx \u043f\u0435\u0440\u0435\u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u0441 \u043f\u0443\u0442\u0451\u043c \/api\/v1 \u043d\u0430 \u043f\u043e\u0440\u0442 8080. \u041e\u0442\u043a\u0440\u043e\u0435\u043c \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e <code>sudo vim \/etc\/nginx\/nginx.conf<\/code><\/p>\n<pre><code class=\"bash\">        location \/ {         }         #\u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u0441\u0435\u043a\u0446\u0438\u044e, \u0433\u0434\u0435 \u043d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u043f\u0435\u0440\u0435\u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043d\u0430 \u043f\u043e\u0440\u0442 8080         location \/api\/v1\/ {             proxy_set_header Host $host;             proxy_set_header X-Real-IP $remote_addr;             proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;             proxy_set_header X-Forwarder-Proto $scheme;             proxy_pass http:\/\/127.0.0.1:8080\/api\/v1\/;         }<\/code><\/pre>\n<p>\u041f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c nginx \u043a\u043e\u043c\u0430\u043d\u0434\u043e\u0439 <code>sudo systemctl restart nginx<\/code>. \u0422\u0430\u043a\u0436\u0435 \u043d\u0430\u0434\u043e \u0440\u0430\u0437\u0440\u0435\u0448\u0438\u0442\u044c http-\u0441\u0435\u0440\u0432\u0435\u0440\u0443 \u043f\u0440\u043e\u043a\u0438\u0434\u044b\u0432\u0430\u0442\u044c \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f \u043a\u043e\u043c\u0430\u043d\u0434\u043e\u0439 <\/p>\n<p><code>sudo setsebool -P httpd_can_network_relay 1<\/code><\/p>\n<p>\u041f\u043e\u0441\u043b\u0435 \u044d\u0442\u043e\u0433\u043e \u0431\u0443\u0434\u0435\u0442 \u0434\u043e\u0441\u0442\u0443\u043f \u043a \u0441\u0435\u0440\u0432\u0438\u0441\u0443 \u0447\u0435\u0440\u0435\u0437 \u043f\u043e\u0440\u0442 80.<\/p>\n<pre><code>curl -X POST http:\/\/178.154.206.196\/api\/v1\/dictionary\/yandex-alice-skill -H \"Content-Type: application\/json\" -d \"{\\\"request\\\":{\\\"command\\\":\\\"peach\\\"},\\\"session\\\":{\\\"session_id\\\":\\\"id\\\"}}\" {\"response\":{\"text\":\"\u041d\u0435 \u0432\u0435\u0440\u043d\u043e! \u041a\u0430\u043a \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0438\u0442\u0441\u044f \u043d\u0430 \u0430\u043d\u0433\u043b\u0438\u0439\u0441\u043a\u0438\u0439 \u044f\u0437\u044b\u043a \u0441\u043b\u043e\u0432\u043e \u043f\u0435\u0440\u0441\u0438\u043a?\",\"end_session\":false},\"version\":\"1.0\"}<\/code><\/pre>\n<p>\u041c\u043e\u0436\u043d\u043e \u0437\u0430\u043a\u0440\u044b\u0442\u044c \u0441\u0435\u0442\u0435\u0432\u043e\u0439 \u043f\u043e\u0440\u0442 8080 \u0434\u043b\u044f \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u0438\u0437 \u043f\u0443\u0431\u043b\u0438\u0447\u043d\u044b\u0445 \u0441\u0435\u0442\u0435\u0439. <\/p>\n<pre><code class=\"bash\"># \u0423\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u043c \u0438 \u0432\u043a\u043b\u044e\u0447\u0430\u0435\u043c Firewall sudo yum install -y firewalld sudo systemctl enable firewalld sudo systemctl start firewalld  #\u0440\u0430\u0437\u0440\u0435\u0448\u0438\u043c \u0434\u043e\u0441\u0442\u0443\u043f \u043f\u043e http \u0438 https sudo firewall-cmd --zone=public --add-service=http --permanent sudo firewall-cmd --zone=public --add-service=https --permanent<\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043a\u0430\u0442 \u043d\u0430 \u0441\u0430\u0439\u0442\u0435 Let&#8217;s Encrypt \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e CertBot. \u0414\u0435\u0439\u0441\u0442\u0432\u0443\u0439\u0442\u0435 \u043f\u043e \u0438\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u0438 \u0434\u043b\u044f CentOS 8: <a href=\"https:\/\/certbot.eff.org\/instructions?ws=nginx&amp;os=centosrhel8\" rel=\"noopener noreferrer nofollow\">https:\/\/certbot.eff.org\/instructions?ws=nginx&amp;os=centosrhel8<\/a>. CertBot \u0441\u0430\u043c\u043e\u0441\u0442\u043e\u044f\u0442\u0435\u043b\u044c\u043d\u043e \u0432\u043d\u0435\u0441\u0451\u0442 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0434\u043b\u044f Nginx.<\/p>\n<h2>\u041f\u0443\u0431\u043b\u0438\u043a\u0430\u0446\u0438\u044f \u0432 \u042f\u043d\u0434\u0435\u043a\u0441.\u0414\u0438\u0430\u043b\u043e\u0433\u0430\u0445<\/h2>\n<p>\u0414\u043b\u044f \u043f\u0443\u0431\u043b\u0438\u043a\u0430\u0446\u0438\u0438 \u043d\u0443\u0436\u043d\u043e \u0432\u0441\u0435\u0433\u043e \u043b\u0438\u0448\u044c \u0443\u043a\u0430\u0437\u0430\u0442\u044c \u0430\u0434\u0440\u0435\u0441 \u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u043d\u0430 \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u0435 \u042f\u043d\u0434\u0435\u043a\u0441.\u0414\u0438\u0430\u043b\u043e\u0433\u0438. <\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/894\/2c8\/d25\/8942c8d25e5fb6faf0916a2c43896fc8.png\" alt=\"\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u043d\u0430\u0432\u044b\u043a\u0430\" title=\"\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u043d\u0430\u0432\u044b\u043a\u0430\" width=\"631\" height=\"493\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/894\/2c8\/d25\/8942c8d25e5fb6faf0916a2c43896fc8.png\"\/><figcaption>\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u043d\u0430\u0432\u044b\u043a\u0430<\/figcaption><\/figure>\n<p>\u041f\u043e\u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043d\u0430\u0432\u044b\u043a \u043c\u043e\u0436\u043d\u043e \u0441 \u0410\u043b\u0438\u0441\u043e\u0439 \u0432 \u042f\u043d\u0434\u0435\u043a\u0441.\u0411\u0440\u0430\u0443\u0437\u0435\u0440\u0435, \u0435\u0441\u043b\u0438 \u0443 \u0432\u0430\u0441 \u043d\u0435\u0442 \u043f\u043e\u0434 \u0440\u0443\u043a\u043e\u0439 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432 \u0441 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u043e\u0439 \u0410\u043b\u0438\u0441\u044b. \u041f\u043e\u0441\u043b\u0435 \u0442\u043e\u0433\u043e, \u043a\u0430\u043a \u0432\u044b \u0443\u0431\u0435\u0434\u0438\u043b\u0438\u0441\u044c, \u0447\u0442\u043e \u0432\u0441\u0451 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442, \u043c\u043e\u0436\u043d\u043e \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u043d\u0430 \u043c\u043e\u0434\u0435\u0440\u0430\u0446\u0438\u044e \u0438 \u0437\u0430\u0442\u0435\u043c \u043e\u043f\u0443\u0431\u043b\u0438\u043a\u043e\u0432\u0430\u0442\u044c \u043d\u0430\u0432\u044b\u043a.<\/p>\n<h2>\u0414\u043e\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435<\/h2>\n<p>\u041a\u043e\u0434 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0434\u043e\u0441\u0442\u0443\u043f\u0435\u043d \u0432 <a href=\"https:\/\/github.com\/m4ks1k\/oxford3k\" rel=\"noopener noreferrer nofollow\">GitHub<\/a>. \u0422\u0430\u043c \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u0434\u0440\u0443\u0433\u0430\u044f \u0432\u0435\u0440\u0441\u0438\u044f, \u0442\u0430\u043a \u043a\u0430\u043a \u043d\u0430\u0432\u044b\u043a \u0431\u044b\u043b \u043d\u0430\u043f\u0438\u0441\u0430\u043d \u0433\u043e\u0434 \u043d\u0430\u0437\u0430\u0434, \u0438 \u043f\u043e\u044f\u0432\u0438\u043b\u0438\u0441\u044c \u0434\u0440\u0443\u0433\u0438\u0435 \u0432\u0435\u0440\u0441\u0438\u0438 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a. <\/p>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"v-portal\" style=\"display:none;\"><\/div>\n<\/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\/post\/652057\/\"> https:\/\/habr.com\/ru\/post\/652057\/<\/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_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>\u0412 \u0441\u0442\u0430\u0442\u044c\u0435 \u0440\u0430\u0441\u0441\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f, \u043a\u0430\u043a \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u043d\u0430\u0432\u044b\u043a \u0434\u043b\u044f \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u044b \u042f\u043d\u0434\u0435\u043a\u0441.\u0414\u0438\u0430\u043b\u043e\u0433\u0438, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f Java \u0438 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a Spring Boot, \u0430 \u0437\u0430\u0442\u0435\u043c \u0440\u0430\u0437\u0432\u0435\u0440\u043d\u0443\u0442\u044c \u0435\u0433\u043e \u0432 \u042f\u043d\u0434\u0435\u043a\u0441.\u041e\u0431\u043b\u0430\u043a\u0435. <\/p>\n<p>\u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043d\u0443\u0436\u043d\u043e:<\/p>\n<ol>\n<li>\n<p>\u041d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u043a\u043e\u0434 \u0441\u0435\u0440\u0432\u0438\u0441\u0430, \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0443\u044e\u0449\u0435\u0433\u043e \u0441 \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u043e\u0439.<\/p>\n<\/li>\n<li>\n<p>\u0420\u0430\u0437\u0432\u0435\u0440\u043d\u0443\u0442\u044c \u0435\u0433\u043e \u0432 \u043e\u0431\u043b\u0430\u0447\u043d\u043e\u0439 \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0435, \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0432 \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u043c\u0443\u044e \u043e\u0431\u043b\u0430\u043a\u043e\u043c (Managed Service) \u0431\u0430\u0437\u0443 \u0434\u0430\u043d\u043d\u044b\u0445.<\/p>\n<\/li>\n<li>\n<p>\u041a\u0443\u043f\u0438\u0442\u044c \u0434\u043e\u043c\u0435\u043d \u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u0442\u0435\u0440\u043c\u0438\u043d\u0430\u0446\u0438\u044e SSL.<\/p>\n<\/li>\n<li>\n<p>\u041e\u043f\u0443\u0431\u043b\u0438\u043a\u043e\u0432\u0430\u0442\u044c \u0430\u0434\u0440\u0435\u0441 \u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u043d\u0430 \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u0435.<\/p>\n<\/li>\n<\/ol>\n<p>\u0414\u043b\u044f \u043f\u0440\u0438\u043c\u0435\u0440\u0430 \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u043f\u043e\u043c\u043e\u0433\u0430\u0435\u0442 \u0437\u0430\u043f\u043e\u043c\u0438\u043d\u0430\u0442\u044c \u0430\u043d\u0433\u043b\u0438\u0439\u0441\u043a\u0438\u0435 \u0441\u043b\u043e\u0432\u0430. \u0410\u043b\u0438\u0441\u0430 \u043f\u0440\u043e\u0441\u0438\u0442 \u043f\u0435\u0440\u0435\u0432\u0435\u0441\u0442\u0438 \u0440\u0443\u0441\u0441\u043a\u043e\u0435 \u0438\u043b\u0438 \u0430\u043d\u0433\u043b\u0438\u0439\u0441\u043a\u043e\u0435 \u0441\u043b\u043e\u0432\u043e, \u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0438\u0442 \u044d\u0442\u043e \u0441\u043b\u043e\u0432\u043e. \u0410\u043b\u0438\u0441\u0430 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442, \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e \u043b\u0438 \u0441\u043b\u043e\u0432\u043e \u043f\u0435\u0440\u0435\u0432\u0435\u0434\u0435\u043d\u043e. \u0415\u0441\u043b\u0438 \u043f\u0435\u0440\u0435\u0432\u043e\u0434 \u043d\u0435 \u0432\u0435\u0440\u043d\u044b\u0439, \u0442\u043e \u0410\u043b\u0438\u0441\u0430 \u0433\u043e\u0432\u043e\u0440\u0438\u0442, \u043a\u0430\u043a \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e \u043f\u0435\u0440\u0435\u0432\u0435\u0441\u0442\u0438 \u0441\u043b\u043e\u0432\u043e. \u0421\u043b\u043e\u0432\u0430\u0440\u044c \u0445\u0440\u0430\u043d\u0438\u0442\u0441\u044f \u0432 \u0421\u0423\u0411\u0414.<\/p>\n<p>\u0415\u0441\u043b\u0438 \u0432\u044b \u0437\u043d\u0430\u043a\u043e\u043c\u044b \u0441 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435\u043c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439 \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c Spring Boot, \u0442\u043e \u0440\u0430\u0437\u0434\u0435\u043b\u044b \u043f\u0440\u043e \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0443 \u043c\u043e\u0436\u043d\u043e \u043f\u0440\u043e\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u0438 \u043f\u0435\u0440\u0435\u0439\u0442\u0438 \u043a \u0440\u0430\u0437\u0432\u0451\u0440\u0442\u044b\u0432\u0430\u043d\u0438\u044e \u0441\u0435\u0440\u0432\u0438\u0441\u0430.<\/p>\n<h2>\u041a\u0430\u043a \u0443\u0441\u0442\u0440\u043e\u0435\u043d\u044b \u042f\u043d\u0434\u0435\u043a\u0441.\u0414\u0438\u0430\u043b\u043e\u0433\u0438<\/h2>\n<p>API \u043e\u043f\u0438\u0441\u0430\u043d\u043e \u0432 \u0440\u0430\u0437\u0434\u0435\u043b\u0435 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438 <a href=\"https:\/\/yandex.ru\/dev\/dialogs\/alice\/doc\/request.html\" rel=\"noopener noreferrer nofollow\">\u042f\u043d\u0434\u0435\u043a\u0441.\u0414\u0438\u0430\u043b\u043e\u0433\u043e\u0432<\/a>. \u041f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u0430 \u043f\u043e\u0441\u044b\u043b\u0430\u0435\u0442 POST-\u0437\u0430\u043f\u0440\u043e\u0441\u044b \u043d\u0430 \u0430\u0434\u0440\u0435\u0441 \u0431\u044d\u043a\u0435\u043d\u0434\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0432\u044b \u043d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u0442\u0435 \u0432 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\u0445 \u043d\u0430\u0432\u044b\u043a\u0430. \u0412 \u0437\u0430\u043f\u0440\u043e\u0441\u0435 \u043f\u0435\u0440\u0435\u0434\u0430\u0451\u0442\u0441\u044f \u0444\u0440\u0430\u0437\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0438 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0441\u043b\u0443\u0436\u0435\u0431\u043d\u0430\u044f \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u043f\u043e\u043c\u043e\u0433\u0430\u0435\u0442 \u0441\u0442\u0440\u043e\u0438\u0442\u044c \u043f\u043e\u043b\u043d\u043e\u0446\u0435\u043d\u043d\u044b\u0439 \u0434\u0438\u0430\u043b\u043e\u0433 \u0438 \u0437\u0430\u043f\u043e\u043c\u0438\u043d\u0430\u0442\u044c \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u043c\u0435\u0436\u0434\u0443 \u0441\u0435\u0430\u043d\u0441\u0430\u043c\u0438 \u043e\u0431\u0449\u0435\u043d\u0438\u044f. <\/p>\n<p>\u0412 \u0441\u043e\u0441\u0442\u0430\u0432 \u0441\u043b\u0443\u0436\u0435\u0431\u043d\u043e\u0439 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438 \u0432\u0445\u043e\u0434\u044f\u0442: <\/p>\n<ul>\n<li>\n<p>\u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440 \u0441\u0435\u0441\u0441\u0438\u0438;<\/p>\n<\/li>\n<li>\n<p>\u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f \u043e \u043a\u043e\u043d\u0435\u0447\u043d\u043e\u043c \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0435;<\/p>\n<\/li>\n<li>\n<p>\u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0432 \u042f\u043d\u0434\u0435\u043a\u0441.\u041f\u0430\u0441\u043f\u043e\u0440\u0442\u0435, \u0435\u0441\u043b\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u0446\u0438\u0440\u043e\u0432\u0430\u043d.<\/p>\n<\/li>\n<\/ul>\n<p>\u0424\u0440\u0430\u0437\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u043f\u0435\u0440\u0435\u0434\u0430\u0451\u0442\u0441\u044f \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u043a\u0430\u043a \u0442\u0435\u043a\u0441\u0442, \u043d\u043e \u0438 \u0440\u0430\u0437\u0431\u0438\u0440\u0430\u0435\u0442\u0441\u044f \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u043e\u0439 \u043d\u0430 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0435 <a href=\"https:\/\/yandex.ru\/dev\/dialogs\/alice\/doc\/request-simpleutterance.html\" rel=\"noopener noreferrer nofollow\">\u0442\u0438\u043f\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u0442\u043e\u043a\u0435\u043d\u044b<\/a>, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0434\u0430\u0442\u0430 \u0438 \u0432\u0440\u0435\u043c\u044f, \u0447\u0438\u0441\u043b\u043e, \u0430\u0434\u0440\u0435\u0441 \u0438\u043b\u0438 \u0438\u043c\u044f \u0438 \u0444\u0430\u043c\u0438\u043b\u0438\u044e. \u042d\u0442\u043e \u0441\u043e\u043a\u0440\u0430\u0449\u0430\u0435\u0442 \u0432\u0440\u0435\u043c\u044f \u043f\u0440\u0438 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0435, \u0442\u0430\u043a \u043d\u0435 \u043d\u0443\u0436\u043d\u043e \u0440\u0430\u0437\u0431\u0438\u0440\u0430\u0442\u044c\u0441\u044f \u0432 \u0442\u043e\u043d\u043a\u043e\u0441\u0442\u044f\u0445 \u0440\u0430\u0437\u0431\u043e\u0440\u0430 \u0435\u0441\u0442\u0435\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0445 \u044f\u0437\u044b\u043a\u043e\u0432.<\/p>\n<p>\u0412 \u043e\u0442\u0432\u0435\u0442 \u0441\u0435\u0440\u0432\u0438\u0441 \u043d\u0430\u0432\u044b\u043a\u0430 \u0434\u043e\u043b\u0436\u0435\u043d \u0432\u0435\u0440\u043d\u0443\u0442\u044c \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u0435 \u0444\u0440\u0430\u0437\u0443, \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u043f\u0440\u043e\u0438\u0437\u043d\u0435\u0441\u0451\u0442 \u0410\u043b\u0438\u0441\u0430. \u041c\u043e\u0436\u043d\u043e \u0442\u0430\u043a\u0436\u0435 \u0443\u043a\u0430\u0437\u0430\u0442\u044c \u0443\u0434\u0430\u0440\u0435\u043d\u0438\u044f, \u043f\u043e\u0434\u0441\u043a\u0430\u0437\u044b\u0432\u0430\u044f \u0410\u043b\u0438\u0441\u0435 \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u0443\u044e \u0438\u043d\u0442\u043e\u043d\u0430\u0446\u0438\u044e.<\/p>\n<p>\u0417\u0432\u0443\u0447\u0438\u0442 \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u043d\u0435 \u0441\u043b\u043e\u0436\u043d\u043e, \u0442\u0430\u043a \u0447\u0442\u043e \u043c\u043e\u0436\u043d\u043e \u043d\u0430\u0447\u0430\u0442\u044c \u043f\u0438\u0441\u0430\u0442\u044c \u0441\u0435\u0440\u0432\u0438\u0441.<\/p>\n<h2>\u041f\u0438\u0448\u0435\u043c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u043d\u0430 Sping Boot, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f TDD<\/h2>\n<h3>TDD &#8212; Test Driven Development<\/h3>\n<p>TDD &#8212; \u043e\u0434\u043d\u0430 \u0438\u0437 \u043c\u0435\u0442\u043e\u0434\u0438\u043a \u044d\u043a\u0441\u0442\u0440\u0435\u043c\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u043f\u043e\u043c\u043e\u0433\u0430\u0435\u0442 \u043f\u0438\u0441\u0430\u0442\u044c \u043d\u0430\u0434\u0451\u0436\u043d\u044b\u0439 \u0438 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u043c\u044b\u0439 \u043a\u043e\u0434. \u0427\u0438\u0442\u0430\u0442\u0435\u043b\u0438, \u0437\u043d\u0430\u043a\u043e\u043c\u044b\u0435 \u0441 \u043d\u0435\u0439, \u043c\u043e\u0433\u0443\u0442 \u043f\u0440\u043e\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u044d\u0442\u043e\u0442 \u0440\u0430\u0437\u0434\u0435\u043b. \u041d\u0438\u0436\u0435 \u044f \u043a\u043e\u0440\u043e\u0442\u043a\u043e \u043e\u043f\u0438\u0448\u0443 \u0441\u0443\u0442\u044c \u043c\u0435\u0442\u043e\u0434\u0430, \u0432\u0432\u0435\u0434\u0451\u043d\u043d\u043e\u0433\u043e \u041a\u0435\u043d\u0442\u043e\u043c \u0411\u0435\u043a\u043e\u043c \u0432 2003 \u0433\u043e\u0434\u0443. <\/p>\n<p>\u041a\u0430\u043a\u043e\u0439 \u043f\u043e\u0434\u0445\u043e\u0434 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0432 \u043b\u043e\u0431, \u0447\u0442\u043e\u0431\u044b \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0443? <\/p>\n<ol start=\"1\">\n<li>\n<p>\u041e\u0442\u043a\u0440\u044b\u0432\u0430\u0435\u043c \u0441\u0432\u043e\u0439 \u043b\u044e\u0431\u0438\u043c\u044b\u0439 IDE. <\/p>\n<\/li>\n<li>\n<p>\u041f\u0438\u0448\u0435\u043c \u043a\u043e\u0434 \u0441 \u043f\u0440\u043e\u0446\u0435\u0434\u0443\u0440\u043e\u0439 main.<\/p>\n<\/li>\n<li>\n<p>\u041a\u043e\u043c\u043f\u0438\u043b\u0438\u0440\u0443\u0435\u043c, \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c, \u0432\u0432\u043e\u0434\u0438\u043c \u0442\u0435\u0441\u0442\u043e\u0432\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435.<\/p>\n<\/li>\n<li>\n<p>\u0423\u0431\u0435\u0436\u0434\u0430\u0435\u043c\u0441\u044f, \u0447\u0442\u043e \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0430 \u0434\u0430\u0451\u0442 \u043e\u0436\u0438\u0434\u0430\u0435\u043c\u044b\u0439 \u043e\u0442\u0432\u0435\u0442.<\/p>\n<\/li>\n<li>\n<p>\u0415\u0441\u043b\u0438 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0430 \u0441\u0431\u043e\u0438\u0442, \u0442\u043e \u043f\u0440\u0430\u0432\u0438\u043c \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0443, \u0438 \u043f\u043e\u0432\u0442\u043e\u0440\u044f\u0435\u043c \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u0441 \u0442\u0440\u0435\u0442\u044c\u0435\u0433\u043e \u043f\u043e \u043f\u044f\u0442\u043e\u0435.<\/p>\n<\/li>\n<\/ol>\n<p>\u0422\u0430\u043a\u0438\u043c\u0438 \u043a\u043e\u0440\u043e\u0442\u043a\u0438\u043c\u0438 \u043f\u043e\u0432\u0442\u043e\u0440\u0435\u043d\u0438\u044f\u043c\u0438 \u043e\u0431\u044b\u0447\u043d\u043e \u0432\u0435\u0434\u0451\u0442\u0441\u044f \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0430. \u0420\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0430\u043c \u043f\u043e\u0441\u0442\u043e\u044f\u043d\u043d\u043e \u043f\u0440\u0438\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u043f\u043e\u0432\u0442\u043e\u0440\u044f\u0442\u044c \u043e\u0434\u0438\u043d\u0430\u043a\u043e\u0432\u044b\u0435 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u043f\u0440\u0438 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0438. \u0427\u0435\u043c \u0441\u043b\u043e\u0436\u043d\u0435\u0435 \u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0441\u044f \u043b\u043e\u0433\u0438\u043a\u0430 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u044b, \u0442\u0435\u043c \u0431\u043e\u043b\u044c\u0448\u0435 \u0442\u0435\u0441\u0442\u043e\u0432 \u043f\u0440\u0438\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u043f\u0440\u043e\u0432\u043e\u0434\u0438\u0442\u044c \u0432 \u0440\u0443\u0447\u043d\u0443\u044e. \u0410 \u0432\u0435\u0434\u044c \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0442\u0435\u0441\u0442\u044b \u0442\u0440\u0435\u0431\u0443\u044e\u0442 \u043f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u043a\u0438 \u0442\u0435\u0441\u0442\u043e\u0432\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445 \u0438 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0445 \u043f\u0440\u0435\u0434\u0443\u0441\u043b\u043e\u0432\u0438\u0439!<\/p>\n<p>\u041d\u043e \u043c\u044b \u0436\u0435 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0441\u0442\u044b \u0438 \u043c\u043e\u0436\u0435\u043c \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0434\u0430\u0436\u0435 \u044d\u0442\u0443 \u0440\u0443\u0442\u0438\u043d\u043d\u0443\u044e \u0440\u0430\u0431\u043e\u0442\u0443, \u0438 \u043d\u0430\u043c \u043d\u0430 \u043f\u043e\u043c\u043e\u0449\u044c \u043f\u0440\u0438\u0445\u043e\u0434\u0438\u0442 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u0435 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435. \u0426\u0438\u043a\u043b\u044b \u043f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u043a\u0438 \u0442\u0435\u0441\u0442\u043e\u0432\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445, \u0432\u044b\u0437\u043e\u0432\u044b \u043d\u0430\u0448\u0435\u0439 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u044b \u0438 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0443 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u0432 \u043c\u043e\u0436\u043d\u043e \u0437\u0430\u043d\u0435\u0441\u0442\u0438 \u0432 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u0443\u044e \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0443.<\/p>\n<p>\u041c\u0435\u0442\u043e\u0434\u0438\u043a\u0430 \u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u0435\u0442 \u043d\u0430\u0447\u0430\u0442\u044c \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u044b \u0441 \u0442\u0435\u0441\u0442\u0430, \u0430 \u0437\u0430\u0442\u0435\u043c \u0446\u0438\u043a\u043b\u044b \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0431\u0443\u0434\u0443\u0442 \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u0442\u044c \u043a\u0430\u043a Red, Green, Refactor.<\/p>\n<ul>\n<li>\n<p>Red &#8212; \u0434\u0443\u043c\u0430\u0435\u043c \u043e \u0442\u043e\u043c, \u0447\u0442\u043e \u043d\u0443\u0436\u043d\u043e \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c. \u041f\u0438\u0448\u0435\u043c \u0442\u0435\u0441\u0442, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442 \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b. \u041f\u0440\u0438 \u043f\u0435\u0440\u0432\u043e\u0439 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0435 \u043e\u043d \u043f\u0430\u0434\u0430\u0435\u0442, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0448\u0430\u0433 \u043d\u0430\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f Red.<\/p>\n<\/li>\n<li>\n<p>Green &#8212; \u043f\u0438\u0448\u0435\u043c \u043a\u043e\u0434 \u0441\u0430\u043c\u044b\u043c \u043f\u0440\u043e\u0441\u0442\u044b\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c, \u0447\u0442\u043e\u0431\u044b \u0442\u0435\u0441\u0442 \u0440\u0430\u0431\u043e\u0442\u0430\u043b. <\/p>\n<\/li>\n<li>\n<p>Refactor &#8212; \u0443\u043b\u0443\u0447\u0448\u0430\u0435\u043c \u043d\u0430\u0448 \u043a\u043e\u0434, \u0447\u0442\u043e\u0431\u044b \u043e\u043d \u0431\u044b\u043b \u043b\u0443\u0447\u0448\u0435 \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u043e\u0432\u0430\u043d, \u043d\u043e \u043f\u0440\u0438 \u044d\u0442\u043e\u043c \u0442\u0435\u0441\u0442 \u0440\u0430\u0431\u043e\u0442\u0430\u043b.<\/p>\n<\/li>\n<\/ul>\n<p>\u041f\u043e\u0441\u043b\u0435 \u043a\u0430\u0436\u0434\u043e\u0439 \u0442\u0430\u043a\u043e\u0439 \u0438\u0442\u0435\u0440\u0430\u0446\u0438\u0438 \u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u0435\u0442\u0441\u044f \u0434\u0435\u043b\u0430\u0442\u044c \u043a\u043e\u043c\u043c\u0438\u0442. \u0412 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0435, \u043d\u0430 \u043a\u0430\u0436\u0434\u043e\u043c \u044d\u0442\u0430\u043f\u0435 \u043c\u044b \u0438\u043c\u0435\u0435\u043c \u0440\u0430\u0431\u043e\u0442\u0430\u044e\u0449\u0438\u0439 \u043a\u043e\u0434 (\u043e\u043d \u043f\u0440\u043e\u0432\u0435\u0440\u0435\u043d \u0430\u0432\u0442\u043e\u0442\u0435\u0441\u0442\u0430\u043c\u0438) \u0438 \u043d\u0435 \u043f\u0438\u0448\u0435\u043c \u043b\u0438\u0448\u043d\u0438\u0439 \u043a\u043e\u0434, \u044d\u043a\u043e\u043d\u043e\u043c\u044f \u0441\u0432\u043e\u0451 \u0432\u0440\u0435\u043c\u044f.<\/p>\n<p>\u041f\u0440\u043e\u0432\u043e\u0434\u044f \u0440\u0435\u0444\u0430\u043a\u0442\u043e\u0440\u0438\u043d\u0433 \u043a\u043e\u0434\u0430, \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u043c\u043e\u0436\u0435\u0442 \u0432\u044b\u0434\u0435\u043b\u044f\u0442\u044c \u0430\u0431\u0441\u0442\u0440\u0430\u043a\u0446\u0438\u0438 \u0438 \u0441\u043b\u043e\u0438 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u044b \u0438 \u0443\u0434\u0430\u043b\u044f\u0442\u044c \u0434\u0443\u0431\u043b\u0438\u0440\u0443\u044e\u0449\u0438\u0439\u0441\u044f \u043a\u043e\u0434. <\/p>\n<h2>\u0428\u0430\u0431\u043b\u043e\u043d \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/h2>\n<p>\u0424\u0440\u0435\u0439\u043c\u0444\u043e\u0440\u043a Spring Boot \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0433\u043e\u0442\u043e\u0432\u044b\u0439 \u043f\u0440\u043e\u0435\u043a\u0442 \u0441 \u043d\u0443\u043b\u044f, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f <a href=\"https:\/\/start.spring.io\/\" rel=\"noopener noreferrer nofollow\">Sping Initializr<\/a>. <\/p>\n<figure class=\"full-width\"><figcaption><\/figcaption><\/figure>\n<p>\u0417\u0430 \u0441\u0431\u043e\u0440\u043a\u0443 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u0431\u0443\u0434\u0435\u0442 \u043e\u0442\u0432\u0435\u0447\u0430\u0442\u044c Maven. \u0418\u0437 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439 \u043f\u043e\u0442\u0440\u0435\u0431\u0443\u044e\u0442\u0441\u044f:<\/p>\n<ul>\n<li>\n<p>Lombok &#8212; \u0434\u043b\u044f \u0443\u043c\u0435\u043d\u044c\u0448\u0435\u043d\u0438\u044f \u0432\u0441\u043f\u043e\u043c\u043e\u0433\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0433\u043e (boilerplate) \u043a\u043e\u0434\u0430.<\/p>\n<\/li>\n<li>\n<p>Spring Web &#8212; \u0434\u043b\u044f \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 HTTP-\u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432.<\/p>\n<\/li>\n<li>\n<p>Spring Data JPA &#8212; \u0434\u043b\u044f \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043a \u0421\u0423\u0411\u0414.<\/p>\n<\/li>\n<li>\n<p>PostgreSQL &#8212; \u0434\u0440\u0430\u0439\u0432\u0435\u0440 \u043a \u043f\u0440\u043e\u0434\u0443\u043a\u0442\u0438\u0432\u043d\u043e\u0439 \u0421\u0423\u0411\u0414.<\/p>\n<\/li>\n<li>\n<p>H2 Database &#8212; \u0434\u0440\u0430\u0439\u0432\u0435\u0440 \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u0430 \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0421\u0423\u0411\u0414.<\/p>\n<\/li>\n<li>\n<p>Liquibase Migration &#8212; \u0441\u0440\u0435\u0434\u0441\u0442\u0432\u043e \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0441\u0445\u0435\u043c\u043e\u0439 \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445.<\/p>\n<\/li>\n<\/ul>\n<p>\u041f\u043e\u0441\u043b\u0435 \u0432\u044b\u0431\u043e\u0440\u0430 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439 \u043d\u0430\u0436\u0438\u043c\u0430\u0435\u043c \u043a\u043d\u043e\u043f\u043a\u0443 &#171;Generate&#187; \u0438 \u0441\u043a\u0430\u0447\u0438\u0432\u0430\u0435\u043c \u0430\u0440\u0445\u0438\u0432 \u0441 \u0448\u0430\u0431\u043b\u043e\u043d\u043e\u043c \u043f\u0440\u043e\u0435\u043a\u0442\u0430. \u0410\u0440\u0445\u0438\u0432 \u0440\u0430\u0441\u043f\u0430\u043a\u043e\u0432\u044b\u0432\u0430\u0435\u043c \u0432 \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u0443\u044e \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u044e. \u041e\u0442\u043a\u0440\u044b\u0432\u0430\u0435\u043c \u043a\u043e\u043d\u0441\u043e\u043b\u044c \u0438 \u043f\u0440\u043e\u0431\u0443\u0435\u043c \u0441\u043e\u0431\u0440\u0430\u0442\u044c.<\/p>\n<pre><code>cd C:\\dev\\dictionary-service mvnw package<\/code><\/pre>\n<p>\u041f\u0440\u043e\u0435\u043a\u0442 \u043f\u0430\u0434\u0430\u0435\u0442 \u0441 \u043e\u0448\u0438\u0431\u043a\u043e\u0439.<\/p>\n<pre><code>Caused by: liquibase.exception.ChangeLogParseException: classpath:\/db\/changelog\/db.changelog-master.yaml does not exist  [INFO] [INFO] Results: [INFO] [ERROR] Errors: [ERROR]   DictionaryServiceApplicationTests.contextLoads ? IllegalState Failed to load A... [INFO] [ERROR] Tests run: 1, Failures: 0, Errors: 1, Skipped: 0 [INFO] [INFO] ------------------------------------------------------------------------ [INFO] BUILD FAILURE [INFO] ------------------------------------------------------------------------<\/code><\/pre>\n<p>\u041f\u0440\u0438\u0447\u0438\u043d\u0430 \u0432 \u0442\u043e\u043c, \u0447\u0442\u043e Liquibase \u0432 \u0440\u0435\u0441\u0443\u0440\u0441\u0430\u0445 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043d\u0435 \u043d\u0430\u0448\u0451\u043b \u0436\u0443\u0440\u043d\u0430\u043b \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0439 <code>\/db\/changelog\/db.changelog-master.yaml<\/code>, \u0442\u0440\u0435\u0431\u0443\u0435\u043c\u044b\u0439 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e.<\/p>\n<p>\u0417\u0430\u043c\u0435\u043d\u0438\u043c \u0444\u0430\u0439\u043b <code>src\/main\/resources\/application.properties<\/code> \u043d\u0430 <code>src\/main\/resources\/application.yml<\/code> \u0438 \u043f\u0440\u043e\u043f\u0438\u0448\u0435\u043c \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e Liquibase.<\/p>\n<pre><code class=\"yaml\">spring:   profiles:      default: local #\u043f\u0440\u043e\u0444\u0438\u043b\u044c Spring \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e   datasource: #\u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f \u0441 \u0421\u0423\u0411\u0414 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e     driverClassName: org.postgresql.Driver      url: ${SPRING_DATASOURCE_URL} #\u0431\u0443\u0434\u0443\u0442 \u043e\u043f\u0435\u0440\u0435\u0434\u0435\u043b\u044f\u0442\u044c\u0441\u044f \u0447\u0435\u0440\u0435\u0437 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0443\u044e \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f     username: ${SPRING_DATASOURCE_USERNAME}     password: ${SPRING_DATASOURCE_PASSWORD}   jpa:     show-sql: false #\u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u043e\u0442\u043a\u043b\u044e\u0447\u0438\u043c \u043f\u043e\u043a\u0430\u0437 SQL     hibernate.ddl-auto: validate #Hibernate \u0431\u0443\u0434\u0435\u0442 \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0442\u044c \u0441\u0445\u0435\u043c\u0443      properties.hibernate.dialect: org.hibernate.dialect.PostgreSQLDialect     open-in-view: false   liquibase:     enabled: true #\u0432\u043a\u043b\u044e\u0447\u0451\u043d \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e     contexts: schema, data #\u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u044b \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e     change-log: classpath:db.changelog.xml #\u043f\u0443\u0442\u044c \u043a \u0436\u0443\u0440\u043d\u0430\u043b\u0443 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0439 Liquibase     liquibaseSchema: liquibase #\u0441\u043b\u0443\u0436\u0435\u0431\u043d\u0430\u044f \u0441\u0445\u0435\u043c\u0430 \u0421\u0423\u0411\u0414 \u0434\u043b\u044f liquibase     default-schema: dict #\u0441\u0445\u0435\u043c\u0430 \u0421\u0423\u0411\u0414 \u0434\u043b\u044f \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f     user: ${SPRING_DATASOURCE_USERNAME}     password: ${SPRING_DATASOURCE_PASSWORD} --- spring:   config.activate.on-profile: test #\u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u0435 \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u0445 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u043e\u0432\u043e\u0433\u043e \u043f\u0440\u043e\u0444\u0438\u043b\u044f    jpa:     properties.hibernate.dialect: org.hibernate.dialect.H2Dialect #\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c H2 \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u043e\u0432   datasource:     driverClassName: org.h2.Driver     url: jdbc:h2:mem:domain1-db;MODE=PostgreSQL;CASE_INSENSITIVE_IDENTIFIERS=TRUE;DB_CLOSE_DELAY=-1;INIT=CREATE SCHEMA IF NOT EXISTS LIQUIBASE\\;CREATE SCHEMA IF NOT EXISTS DICT\\;SET SCHEMA DICT;     username: sa     password:   liquibase:     user: sa     password:     contexts: schema<\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0432 \u0440\u0435\u0441\u0443\u0440\u0441\u0430\u0445 (src\/main\/resources) \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043f\u0443\u0441\u0442\u043e\u0439 \u0436\u0443\u0440\u043d\u0430\u043b \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0439 <code>db.changelog.xml<\/code>.<\/p>\n<pre><code class=\"xml\">&lt;databaseChangeLog xmlns=\"http:\/\/www.liquibase.org\/xml\/ns\/dbchangelog\"     xmlns:xsi=\"http:\/\/www.w3.org\/2001\/XMLSchema-instance\"     xsi:schemaLocation=\"http:\/\/www.liquibase.org\/xml\/ns\/dbchangelog http:\/\/www.liquibase.org\/xml\/ns\/dbchangelog\/dbchangelog-3.3.xsd\">  &lt;\/databaseChangeLog><\/code><\/pre>\n<p>\u0427\u0442\u043e\u0431\u044b \u0432 \u0442\u0435\u0441\u0442\u0430\u0445 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b\u0441\u044f \u0442\u0435\u0441\u0442\u043e\u0432\u044b\u0439 \u043f\u0440\u043e\u0444\u0438\u043b\u044c, \u0443\u043a\u0430\u0436\u0435\u043c \u0435\u0433\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u0432 \u0442\u0435\u0441\u0442\u0435 src\/test\/java\/com\/example\/dictionary-service\/DictionaryServiceApplicationTests.java<\/p>\n<pre><code class=\"java\">package com.example.dictionaryservice;  import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.ActiveProfiles;  @SpringBootTest @ActiveProfiles(\"test\") \/\/\u0430\u043a\u0442\u0438\u0432\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0442\u0435\u0441\u0442\u043e\u0432\u044b\u0439 \u043f\u0440\u043e\u0444\u0438\u043b\u044c Spring \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u043e\u0432 class DictionaryServiceApplicationTests { @Test void contextLoads() { } }<\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043f\u0440\u043e\u0435\u043a\u0442 \u0441\u043e\u0431\u0438\u0440\u0430\u0435\u0442\u0441\u044f. \u041c\u043e\u0436\u043d\u043e \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0439 git \u043a\u043e\u043c\u0430\u043d\u0434\u043e\u0439 <code>git init<\/code>. \u041f\u043e\u0441\u043b\u0435 \u044d\u0442\u043e\u0433\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0432 \u0438\u043d\u0434\u0435\u043a\u0441 git \u0444\u0430\u0439\u043b \u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u043c \u043a\u043e\u043c\u043c\u0438\u0442.<\/p>\n<pre><code>git add *.cmd *.xml *.java *.yml *.jar *.properties .gitignore git commit -m \"Init repository\"<\/code><\/pre>\n<p>\u0427\u0442\u043e\u0431\u044b \u043f\u0440\u043e\u0435\u043a\u0442 \u0441\u043e\u0431\u0438\u0440\u0430\u043b\u0441\u044f \u043d\u0430 *nix \u0441\u0438\u0441\u0442\u0435\u043c\u0430\u0445, \u043d\u0443\u0436\u043d\u043e \u0435\u0449\u0451 \u0432\u044b\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u043f\u0440\u0430\u0432\u0430 \u0434\u043b\u044f \u0437\u0430\u043f\u0443\u0441\u043a\u0430 Shell-\u0441\u043a\u0440\u0438\u043f\u0442\u0430 <code>mnvw<\/code>.<\/p>\n<pre><code>git update-index --chmod=+x mvnw <\/code><\/pre>\n<h3>\u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f API<\/h3>\n<p>\u041e\u0434\u0438\u043d \u0438\u0437 \u043f\u043e\u0434\u0445\u043e\u0434\u043e\u0432 \u043a \u043f\u0440\u043e\u0435\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f API First. \u0415\u0433\u043e \u043f\u0440\u0435\u0438\u043c\u0443\u0449\u0435\u0441\u0442\u0432\u043e \u0432 \u0442\u043e\u043c, \u0447\u0442\u043e \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u0444\u043e\u043a\u0443\u0441\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u043d\u0430 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u0441 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435\u043c, \u0430 \u043d\u0435 \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0435. \u0422\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c, \u043c\u044b \u0438\u0437\u0431\u0435\u0433\u0430\u0435\u043c \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043b\u043e\u0433\u0438\u043a\u0438 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043e\u0442 \u0434\u0435\u0442\u0430\u043b\u0435\u0439 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u043e\u0433\u0443\u0442 \u043c\u0435\u043d\u044f\u0442\u044c\u0441\u044f.<\/p>\n<p>\u0412 \u0441\u043b\u0443\u0447\u0430\u0435 \u0441 \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u043e\u0439 \u042f\u043d\u0434\u0435\u043a\u0441.\u0414\u0438\u0430\u043b\u043e\u0433\u0438 API \u0443\u0436\u0435 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043e \u0437\u0430 \u043d\u0430\u0441. \u041c\u043e\u0436\u043d\u043e \u0441\u0440\u0430\u0437\u0443 \u043d\u0430\u0447\u0438\u043d\u0430\u0442\u044c \u043f\u0438\u0441\u0430\u0442\u044c \u043a\u043e\u0434! \u0418 \u043f\u0435\u0440\u0432\u044b\u043c \u0431\u0443\u0434\u0435\u0442 \u0442\u0435\u0441\u0442 \u043d\u0430 API. <\/p>\n<p>\u041a\u043e\u0433\u0434\u0430 \u0432\u044b \u043f\u0440\u043e\u0441\u0438\u0442\u0435 \u0410\u043b\u0438\u0441\u0443 \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u043d\u0430\u0432\u044b\u043a, \u043e\u043d\u0430 \u0434\u0435\u043b\u0430\u0435\u0442 POST-\u0437\u0430\u043f\u0440\u043e\u0441 \u043a \u0441\u0435\u0440\u0432\u0438\u0441\u0443, \u043d\u0430 \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043d\u0443\u0436\u043d\u043e \u043e\u0442\u0432\u0435\u0442\u0438\u0442\u044c \u0444\u0440\u0430\u0437\u043e\u0439, \u0440\u0430\u0441\u0441\u043a\u0430\u0437\u044b\u0432\u0430\u044e\u0449\u0435\u0439 \u043e \u043d\u0430\u0432\u044b\u043a\u0435 \u0438 \u043a\u0430\u043a \u0441 \u043d\u0438\u043c \u043e\u0431\u0449\u0430\u0442\u044c\u0441\u044f.<\/p>\n<p>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043a\u043b\u0430\u0441\u0441 <code>com.example.dictionaryservice.api.DictionaryApiTest<\/code> \u0432 src\/test\/java <\/p>\n<pre><code class=\"java\">package com.example.dictionaryservice.api;  import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;  import lombok.SneakyThrows; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.http.MediaType; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.web.servlet.MockMvc;  @SpringBootTest \/\/\u043c\u0430\u0440\u043a\u0435\u0440 \u0442\u0435\u0441\u0442\u0430 @ActiveProfiles(\"test\") \/\/\u043f\u0440\u043e\u0444\u0438\u043b\u044c Spring \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f @AutoConfigureMockMvc \/\/\u0430\u0432\u0442\u043e\u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f MockMvc class DictionaryApiTest {     @Autowired     MockMvc mockMvc; \/\/\u043a\u043b\u0430\u0441\u0441      @Test     @SneakyThrows     void should_accept_request_from_yandex_alice_and_return_valid_response() {         mockMvc.perform(             post(\"\/api\/v1\/dictionary\/yandex-alice-skill\/\") \/\/POST-\u0437\u0430\u043f\u0440\u043e\u0441 \u043f\u043e \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u043e\u043c\u0443 \u043f\u0443\u0442\u0438             .contentType(MediaType.APPLICATION_JSON)              .accept(MediaType.APPLICATION_JSON)<\/code><\/pre>\n<\/div>\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-329725","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/329725","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=329725"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/329725\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=329725"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=329725"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=329725"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}