{"id":94393,"date":"2020-09-16T07:43:43","date_gmt":"2020-09-16T05:43:43","guid":{"rendered":"https:\/\/prohoster.info\/blog\/administrirovanie\/mikroservisy-s-kommunikacziej-cherez-axon"},"modified":"2020-09-16T07:43:43","modified_gmt":"2020-09-16T05:43:43","slug":"mikroservisy-s-kommunikacziej-cherez-axon","status":"publish","type":"post","link":"https:\/\/prohoster.info\/en\/blog\/administrirovanie\/mikroservisy-s-kommunikacziej-cherez-axon","title":{"rendered":"Microservices with communication via Axon","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p>In this simple tutorial, we will create a couple of microservices using Spring Boot and organize their interaction through the Axon framework.<\/p>\n<p><img decoding=\"async\" alt=\"Microservices with communication via Axon\" src=\"\/wp-content\/uploads\/2020\/09\/d570cdb88653309c2f5b382509e5c2a9.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\n<noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><br \/>\n<i>Let's assume we have the following task.<\/i><\/p>\n<p>There is a source of trades in the stock market. This source sends us trades via a REST interface. <\/p>\n<p>We need to obtain these trades, store them in a database, and create a convenient in-memory storage. <\/p>\n<p>This storage must perform the following functions:<\/p>\n<ul>\n<li> return a list of trades;<\/li>\n<li> return the full position, i.e., a table of \"instrument\" \u2014 \"current quantity of papers\";<\/li>\n<li> return the position for a given instrument.<\/li>\n<\/ul>\n<p>\n<i>How will we approach solving this task? <\/i><\/p>\n<p>Following the principles of microservice architecture, we need to divide the task into component microservices:<\/p>\n<ul>\n<li> fetch the trade via REST;<\/li>\n<li> save the trade to the database;<\/li>\n<li> an in-memory storage for data representation of the position.<\/li>\n<\/ul>\n<p>\nIn this tutorial, let's implement the first and third services, leaving the second for the next part (let us know in the comments if you're interested).<\/p>\n<p>So, we have two microservices. <\/p>\n<p>The first one receives data from the outside. <\/p>\n<p>The second processes this data and responds to incoming requests. <\/p>\n<p>Of course, we want to achieve horizontal scalability, zero-downtime updates, and other advantages of microservices.<\/p>\n<p><i>What, quite a challenging, task lies ahead of us?<\/i><\/p>\n<p>In fact, there are many, but right now, let's discuss how data will flow between these microservices. We can set up REST between them, implement a queue, or come up with various options, each with its own pros and cons.<\/p>\n<p>Let's consider one possible approach \u2013 asynchronous interaction via <b>the Axon framework.<\/b>.<\/p>\n<p><i>What are the advantages of this solution?<\/i><\/p>\n<p>Firstly, asynchronous interaction increases flexibility (yes, there is a downside, but for now, we will focus on the positives). <\/p>\n<p>Secondly, out of the box, we get <b>Event Sourcing<\/b> and <b>CQRS<\/b>. <br \/>\nThirdly, Axon provides a ready-made infrastructure, allowing us to concentrate solely on developing business logic.<\/p>\n<p>Let's get started.<\/p>\n<p>Our project will be based on Gradle. It will consist of three modules: <\/p>\n<ul>\n<li> common. a module with shared data structures (we dislike copy-pasting);<\/li>\n<li> tradeCreator. a module with a microservice for receiving trades via REST;<\/li>\n<li> tradeQueries. a module with a microservice for displaying the position.<\/li>\n<\/ul>\n<p>\nLet's take Spring Boot as the foundation and connect the Axon starter.<\/p>\n<p>Axon works perfectly even without Spring, but we will use them together.<\/p>\n<p>Here we need to pause and say a few words about Axon. <\/p>\n<p>It is a client-server system. There is a server \u2013 a separate application that we will run in Docker. <\/p>\n<p>And there are clients that are embedded in microservices. <br \/>\nThis is what the picture looks like. First, the Axon server (in Docker) is started, then our microservices. <\/p>\n<p>When starting, microservices look for the server and begin to interact with it. This interaction can be conditionally divided into two types: technical and business. <\/p>\n<p>Technical is the exchange of messages like \"I'm alive\" (such messages can be seen in debug logging mode). <\/p>\n<p>Business is the exchange of messages like \"new deal.\"<\/p>\n<p>An important feature is that after starting, a microservice can ask the Axon server \"what happened,\" and the server sends the accumulated events to the microservice. This way, the microservice can be restarted relatively safely without data loss. <br \/>\nWith this exchange scheme, we can easily start many instances of microservices, <br \/>\neven on different hosts.<\/p>\n<p>Yes, having a single instance of the Axon server is not reliable, but for now, that's how it is.<\/p>\n<p>We work within the paradigms of Event Sourcing and CQRS. This means we should have \"commands,\" \"events,\" and \"queries.\" <\/p>\n<p>We will have one command: \"create deal,\" one event: \"deal created,\" and three queries: \"show all deals,\" \"show position,\" \"show position by instrument.\"<\/p>\n<p>The workflow looks like this: <\/p>\n<ol>\n<li> The tradeCreator microservice receives a deal via REST.<\/li>\n<li> The tradeCreator microservice creates the command \"create deal\" and sends it to the Axon server.<\/li>\n<li> The Axon server receives the command and forwards it to the interested recipient, in our case, this is the tradeCreator microservice.<\/li>\n<li> The tradeCreator microservice receives the command, creates the event \"deal created,\" and sends it to the Axon server.<\/li>\n<li> The Axon server receives the event and forwards it to interested subscribers.<\/li>\n<li> Currently, there is only one interested recipient \u2013 the tradeQueries microservice.<\/li>\n<li> The tradeQueries microservice receives the event and updates its internal data.<\/li>\n<\/ol>\n<p>\n(It\u2019s important that at the moment of the event creation, the tradeQueries microservice may be unavailable, but as soon as it starts, it will immediately receive the event).<\/p>\n<p>Yes, the Axon server is at the center of communications, all messages go through it.<\/p>\n<p><i>Let's move on to coding.<\/i><\/p>\n<p>To avoid cluttering the post with code, I will provide only snippets below, and a link to the complete example will be included later.<\/p>\n<p>Let's start with the common module.<\/p>\n<p>In it, the common parts include an event (class CreatedTradeEvent). Note the naming; essentially, this is the name of the command that generated this event, but in the past tense. It's in the past because the command emerges first, leading to the creation of the event.<\/p>\n<p>Other common structures include classes for describing the position (class Position), the trade (class Trade), and the trade side (enum Side), meaning buying or selling.<\/p>\n<p>Now let's move on to the tradeCreator module. <\/p>\n<p>This module has a Rest interface (class TradeController) for accepting trades. <br \/>\nFrom the received trade, a command to 'create a trade' is formed and sent to the axon server.<\/p>\n<pre><code class=\"java\">    @PostMapping(\"\/trade\")\n    public ResponseEntity create(@RequestBody Trade trade) {\n        var createTradeCommand = CreateTradeCommand.builder()\n                .tradeId(trade.getTradeId())\n\t...\n                .build();\n        var result = commandGateway.sendAndWait(createTradeCommand, 3, TimeUnit.SECONDS);\n        return ResponseEntity.ok(result.get().toString());\n    }\n<\/code><\/pre>\n<p>\nThe command is processed using the class class TradeAggregate. <br \/>\nTo let Axon find it, we apply the @Aggregate annotation.<br \/>\nThe method for handling the command looks like this (with some parts shortened):<\/p>\n<pre><code class=\"java\">    @CommandHandler\n    public TradeAggregate(CreateTradeCommand command) {\n        log.info(\"command: {}\", command);\n        var event = CreatedTradeEvent.builder()\n                .tradeId(command.tradeId())\n\t\t....\n                .build();\n        AggregateLifecycle.apply(event);\n    }\n<\/code><\/pre>\n<p>\nAn event is generated from the command and sent to the server.<br \/>\nThe command is located in the class CreateTradeCommand.<\/p>\n<p>Now let's look at the last module, tradeQueries.<\/p>\n<p>Queries are described in the queries package.<br \/>\nThis module also has a Rest interface.<br \/>\npublic class TradeController.<\/p>\n<p>For example, let's look at the handling of the request: 'show all trades'.<\/p>\n<pre><code class=\"java\">    @GetMapping(\"\/trade\/all\")\n    public List findAllTrades() {\n        return queryGateway.query(new FindAllTradesQuery(),\n                ResponseTypes.multipleInstancesOf(Trade.class)).join();\n    }\n<\/code><\/pre>\n<p>\nA request for retrieval is created and sent to the server.<\/p>\n<p>The class TradesEventHandler is used to process the retrieval request.<br \/>\nIt contains a method marked with the annotation.<\/p>\n<pre><code class=\"java\">   @QueryHandler\n    public List handleFindCurrentPositionQuery(FindCurrentPositionQuery query)\n<\/code><\/pre>\n<p>\nThis method is responsible for fetching data from the in-memory storage.<\/p>\n<p>The question arises: how is the information updated in this storage? <\/p>\n<p>To start, it\u2019s simply a set of ConcurrentHashMaps tailored for specific queries. <br \/>\nThe method is used to update them:<\/p>\n<pre><code class=\"java\">    @EventHandler\n    public void on(CreatedTradeEvent event) {\n        log.info(\"event:{}\", event);\n\n        var trade = Trade.builder()\n\t...\n                .build();\n        trades.put(event.tradeId(), trade);\n        position.merge(event.shortName(), event.size(),\n                (oldValue, value) -&gt; event.side() == Side.BUY ? oldValue + value : oldValue - value);\n    }<\/code><\/pre>\n<p>\nIt handles the \"trade created\" event and updates the Maps.<\/p>\n<p>These are the key points in microservices development.<\/p>\n<p><i>What can be said about the disadvantages of Axon?<\/i><\/p>\n<p>First of all, it complicates the infrastructure, creating a single point of failure - the Axon server, with all communications routed through it. <\/p>\n<p>Secondly, a significant drawback of such distributed systems arises - temporal inconsistency of data. In our case, an unacceptable amount of time may pass between receiving a new trade and updating the data for queries.<\/p>\n<p><i>What was left unsaid?<\/i><\/p>\n<p>There has been no mention of Event Sourcing and CQRS, what they are, and why they are needed.<br \/>\nWithout explaining these concepts, some points may not be clear.<\/p>\n<p>Perhaps some sections of code also require clarification.<\/p>\n<p>We will discuss this at <noindex><a rel=\"nofollow\" href=\"https:\/\/otus.pw\/WrwW\/\">the open webinar<\/a><\/noindex> on September 21.<\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/petrelevich\/jvm-digging\/tree\/master\/eventSourcing\/axonTrader\">Full example<\/a><\/noindex>.<br \/>\n<br \/>Source: <a content=\"nofollow\" rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/otus\/blog\/519144\/\">habr.com<\/a> <\/p>","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"excerpt":{"rendered":"<p>\u0412 \u044d\u0442\u043e\u043c \u043f\u0440\u043e\u0441\u0442\u043e\u043c \u0442\u0443\u0442\u043e\u0440\u0438\u0430\u043b\u0435 \u043c\u044b \u0441\u0434\u0435\u043b\u0430\u0435\u043c \u043f\u0430\u0440\u0443 \u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441\u043e\u0432 \u043d\u0430 Spring Boot \u0438 \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u0443\u0435\u043c \u043c\u0435\u0436\u0434\u0443 \u043d\u0438\u043c\u0438 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u0447\u0435\u0440\u0435\u0437 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a Axon. \u0414\u043e\u043f\u0443\u0441\u0442\u0438\u043c \u0443 \u043d\u0430\u0441 \u0442\u0430\u043a\u0430\u044f \u0437\u0430\u0434\u0430\u0447\u0430. \u0415\u0441\u0442\u044c \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a \u0441\u0434\u0435\u043b\u043e\u043a \u043d\u0430 \u0444\u043e\u043d\u0434\u043e\u0432\u043e\u043c \u0440\u044b\u043d\u043a\u0435. \u042d\u0442\u043e\u0442 \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a \u043f\u0435\u0440\u0435\u0434\u0430\u0435\u0442 \u043d\u0430\u043c \u0441\u0434\u0435\u043b\u043a\u0438 \u043f\u043e Rest-\u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0443. \u041d\u0430\u043c \u043d\u0430\u0434\u043e \u044d\u0442\u0438 \u0441\u0434\u0435\u043b\u043a\u0438 \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c, \u0441\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0432 \u0431\u0430\u0437\u0443 \u0434\u0430\u043d\u043d\u044b\u0445 \u0438 \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0443\u0434\u043e\u0431\u043d\u043e\u0435 in-memory \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0435. \u042d\u0442\u043e \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0435 \u0434\u043e\u043b\u0436\u043d\u044b \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c [&hellip;]<\/p>\n","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"author":1,"featured_media":94394,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[688],"tags":[],"class_list":["post-94393","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-administrirovanie"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 5.0.2 - aioseo.com -->\n\t<meta name=\"description\" content=\"\u0412 \u044d\u0442\u043e\u043c \u043f\u0440\u043e\u0441\u0442\u043e\u043c \u0442\u0443\u0442\u043e\u0440\u0438\u0430\u043b\u0435 \u043c\u044b \u0441\u0434\u0435\u043b\u0430\u0435\u043c \u043f\u0430\u0440\u0443 \u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441\u043e\u0432 \u043d\u0430 Spring Boot \u0438 \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u0443\u0435\u043c \u043c\u0435\u0436\u0434\u0443 \u043d\u0438\u043c\u0438 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u0447\u0435\u0440\u0435\u0437 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a Axon.\" \/>\n\t<meta name=\"robots\" content=\"max-image-preview:large\" \/>\n\t<meta name=\"author\" content=\"Yuri Gagarin\"\/>\n\t<link rel=\"canonical\" href=\"https:\/\/prohoster.info\/en\/blog\/administrirovanie\/mikroservisy-s-kommunikacziej-cherez-axon\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.2\" \/>\n\t\t<meta property=\"og:locale\" content=\"en_US\" \/>\n\t\t<meta property=\"og:site_name\" content=\"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b\" \/>\n\t\t<meta property=\"og:type\" content=\"article\" \/>\n\t\t<meta property=\"og:title\" content=\"\ud83e\udd47\u041c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441\u044b \u0441 \u043a\u043e\u043c\u043c\u0443\u043d\u0438\u043a\u0430\u0446\u0438\u0435\u0439 \u0447\u0435\u0440\u0435\u0437 Axon | ProHoster\" \/>\n\t\t<meta property=\"og:description\" content=\"\u0412 \u044d\u0442\u043e\u043c \u043f\u0440\u043e\u0441\u0442\u043e\u043c \u0442\u0443\u0442\u043e\u0440\u0438\u0430\u043b\u0435 \u043c\u044b \u0441\u0434\u0435\u043b\u0430\u0435\u043c \u043f\u0430\u0440\u0443 \u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441\u043e\u0432 \u043d\u0430 Spring Boot \u0438 \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u0443\u0435\u043c \u043c\u0435\u0436\u0434\u0443 \u043d\u0438\u043c\u0438 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u0447\u0435\u0440\u0435\u0437 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a Axon.\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/prohoster.info\/en\/blog\/administrirovanie\/mikroservisy-s-kommunikacziej-cherez-axon\" \/>\n\t\t<meta property=\"og:image\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:secure_url\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:width\" content=\"350\" \/>\n\t\t<meta property=\"og:image:height\" content=\"350\" \/>\n\t\t<meta property=\"article:published_time\" content=\"2020-09-16T05:43:43+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2020-09-16T05:43:43+00:00\" \/>\n\t\t<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<meta property=\"article:author\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<!-- All in One SEO -->\n\n","aioseo_head_json":{"title":"\ud83e\udd47Microservices with communication via Axon | ProHoster","description":"In this simple tutorial, we will create a couple of microservices using Spring Boot and organize their interaction through the Axon framework.","canonical_url":"https:\/\/prohoster.info\/en\/blog\/administrirovanie\/mikroservisy-s-kommunikacziej-cherez-axon","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"en_US","og:site_name":"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b","og:type":"article","og:title":"\ud83e\udd47\u041c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441\u044b \u0441 \u043a\u043e\u043c\u043c\u0443\u043d\u0438\u043a\u0430\u0446\u0438\u0435\u0439 \u0447\u0435\u0440\u0435\u0437 Axon | ProHoster","og:description":"\u0412 \u044d\u0442\u043e\u043c \u043f\u0440\u043e\u0441\u0442\u043e\u043c \u0442\u0443\u0442\u043e\u0440\u0438\u0430\u043b\u0435 \u043c\u044b \u0441\u0434\u0435\u043b\u0430\u0435\u043c \u043f\u0430\u0440\u0443 \u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441\u043e\u0432 \u043d\u0430 Spring Boot \u0438 \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u0443\u0435\u043c \u043c\u0435\u0436\u0434\u0443 \u043d\u0438\u043c\u0438 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u0447\u0435\u0440\u0435\u0437 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a Axon.","og:url":"https:\/\/prohoster.info\/en\/blog\/administrirovanie\/mikroservisy-s-kommunikacziej-cherez-axon","og:image":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:secure_url":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:width":350,"og:image:height":350,"article:published_time":"2020-09-16T05:43:43+00:00","article:modified_time":"2020-09-16T05:43:43+00:00","article:publisher":"https:\/\/www.facebook.com\/prohoster","article:author":"https:\/\/www.facebook.com\/prohoster"},"aioseo_meta_data":{"post_id":"94393","title":null,"description":null,"keywords":null,"keyphrases":null,"primary_term":null,"canonical_url":null,"og_title":null,"og_description":null,"og_object_type":"default","og_image_type":"default","og_image_url":null,"og_image_width":null,"og_image_height":null,"og_image_custom_url":null,"og_image_custom_fields":null,"og_video":null,"og_custom_url":null,"og_article_section":null,"og_article_tags":null,"twitter_use_og":false,"twitter_card":"default","twitter_image_type":"default","twitter_image_url":null,"twitter_image_custom_url":null,"twitter_image_custom_fields":null,"twitter_title":null,"twitter_description":null,"schema":{"blockGraphs":[],"customGraphs":[],"default":{"data":{"Article":[],"Course":[],"Dataset":[],"FAQPage":[],"Movie":[],"Person":[],"Product":[],"ProductReview":[],"Car":[],"Recipe":[],"Service":[],"SoftwareApplication":[],"WebPage":[]},"graphName":"","isEnabled":true},"graphs":[]},"schema_type":null,"schema_type_options":null,"pillar_content":false,"robots_default":true,"robots_noindex":false,"robots_noarchive":false,"robots_nosnippet":false,"robots_nofollow":false,"robots_noimageindex":false,"robots_noodp":false,"robots_notranslate":false,"robots_max_snippet":null,"robots_max_videopreview":null,"robots_max_imagepreview":"large","priority":null,"frequency":null,"local_seo":null,"seo_analyzer_scan_date":null,"breadcrumb_settings":null,"limit_modified_date":false,"reviewed_by":null,"ai":null,"created":"2021-02-28 11:27:34","updated":"2022-10-02 06:08:26","focus_keyword":null,"additional_keywords":null,"truseo_locale":null},"gt_translate_keys":[{"key":"link","format":"url"}],"_links":{"self":[{"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/posts\/94393","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/comments?post=94393"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/posts\/94393\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/media\/94394"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/media?parent=94393"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/categories?post=94393"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/tags?post=94393"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}