{"id":273,"date":"2022-08-19T09:59:00","date_gmt":"2022-08-19T07:59:00","guid":{"rendered":"https:\/\/www.zellot.at\/blogs\/?p=273"},"modified":"2022-08-19T16:54:08","modified_gmt":"2022-08-19T14:54:08","slug":"microfrontends-with-vue","status":"publish","type":"post","link":"https:\/\/www.zellot.at\/blogs\/2022\/08\/19\/microfrontends-with-vue\/","title":{"rendered":"MicroFrontends with Vue"},"content":{"rendered":"\n<p>Microservices are a well known concept nowadays. Handling encapsulated logic in different applications, can be a nice fit for bigger teams to avoid problems in deployments and development speed. Most of the time though, people are talking about the backend, where the UI part to some extend stays as a black box. One of the solutions to also further break down the UI is the concept of microfrontends which we will further explore here.<\/p>\n\n\n\n<p>There are obviously also other ways to solve issues (I can highly recommend reading Sam Newmans awesome book on this <a href=\"https:\/\/samnewman.io\/books\/building_microservices_2nd_edition\/\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/samnewman.io\/books\/building_microservices_2nd_edition\/<\/a> ), but one way to decompose your UI might be as following.<\/p>\n\n\n\n<p>Each service not only contains the backend logic which then gets called the from one service to render the UI, but rather each service comes with its own UI which then gets loaded into an application which keeps an overview of all the systems to load. This allows us to deploy the full service UI and backend on its own without the need to deploy one big system for each change. This avoids keeping two systems too much in sync in terms of deployments. <\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/www.zellot.at\/blogs\/wp-content\/uploads\/2022\/08\/image.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"668\" src=\"https:\/\/www.zellot.at\/blogs\/wp-content\/uploads\/2022\/08\/image-1024x668.png\" alt=\"\" class=\"wp-image-275\" srcset=\"https:\/\/www.zellot.at\/blogs\/wp-content\/uploads\/2022\/08\/image-1024x668.png 1024w, https:\/\/www.zellot.at\/blogs\/wp-content\/uploads\/2022\/08\/image-300x196.png 300w, https:\/\/www.zellot.at\/blogs\/wp-content\/uploads\/2022\/08\/image-768x501.png 768w, https:\/\/www.zellot.at\/blogs\/wp-content\/uploads\/2022\/08\/image-1536x1002.png 1536w, https:\/\/www.zellot.at\/blogs\/wp-content\/uploads\/2022\/08\/image-650x424.png 650w, https:\/\/www.zellot.at\/blogs\/wp-content\/uploads\/2022\/08\/image.png 1754w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><figcaption>Basic structure<\/figcaption><\/figure>\n\n\n\n<p>In order to do this I used a vue js setup, where we have one container app, which further loads asynchronously applications into the main container (this is heavily inspired by <a href=\"https:\/\/blog.bitsrc.io\/how-to-develop-microfrontends-using-react-step-by-step-guide-47ebb479cacd\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/blog.bitsrc.io\/how-to-develop-microfrontends-using-react-step-by-step-guide-47ebb479cacd<\/a><\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/www.zellot.at\/blogs\/wp-content\/uploads\/2022\/08\/image-1.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"791\" src=\"https:\/\/www.zellot.at\/blogs\/wp-content\/uploads\/2022\/08\/image-1-1024x791.png\" alt=\"\" class=\"wp-image-277\" srcset=\"https:\/\/www.zellot.at\/blogs\/wp-content\/uploads\/2022\/08\/image-1-1024x791.png 1024w, https:\/\/www.zellot.at\/blogs\/wp-content\/uploads\/2022\/08\/image-1-300x232.png 300w, https:\/\/www.zellot.at\/blogs\/wp-content\/uploads\/2022\/08\/image-1-768x594.png 768w, https:\/\/www.zellot.at\/blogs\/wp-content\/uploads\/2022\/08\/image-1-1536x1187.png 1536w, https:\/\/www.zellot.at\/blogs\/wp-content\/uploads\/2022\/08\/image-1-650x502.png 650w, https:\/\/www.zellot.at\/blogs\/wp-content\/uploads\/2022\/08\/image-1.png 1699w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><figcaption>flow of data<\/figcaption><\/figure>\n\n\n\n<p>To achieve this one component was written:<\/p>\n\n\n\n<pre class=\"wp-block-code has-white-background-color has-background\"><code>&lt;template&gt;\n  &lt;div :id=\"this.containerName()\" \/&gt;\n&lt;\/template&gt;\n\n&lt;script&gt;\n\/\/utility function to generate a uuid (I didn't wanted to import the uuid\n\/\/lib just for a random string\nfunction uuidv4() {\n  return (&#91;1e7]+-1e3+-4e3+-8e3+-1e11).replace(\/&#91;018]\/g, c =&gt;\n      (c ^ crypto.getRandomValues(new Uint8Array(1))&#91;0] &amp; 15 &gt;&gt; c \/ 4).toString(16)\n  );\n}\nexport default {\n  name: \"MicroFrontend\",\n  props: {\n    \/\/ name of the microfrontend like `wisdom` or `dicreoll`\n    name: {\n      type: String,\n      required: true\n    },\n    \/\/ host where the script should be loaded from\n    host: {\n      type: String,\n      required: true\n    },\n    \/\/ define a static container name rather than a generic one\n    \/\/ not needed if you are ok with random divs all the time\n    staticContainerName: {\n      type: String,\n      required: false,\n      default: null\n    }\n  },\n  data: function () {\n    return {\n      uuid: uuidv4()\n    }\n  },\n  methods: {\n    \/\/ calculates the container id we load the microfrontend app into\n    containerName() {\n      if(this.staticContainerName != null) {\n        return this.staticContainerName;\n      }\n      return `${ this.name }-${this.uuid}-container`;\n    }\n  },\n  mounted() {\/\/\n    \/\/ id is generated by frontend - this will avoid loading the same\n    \/\/ script twice\n    const scriptId = `micro-frontend-script-${this.name}`;\n    const renderMicroFrontend = () =&gt; {\n      const fnName = `render${this.name}`;\n      const containerName = `#${this.containerName()}`;\n      \/\/load the render function per convention and handover the container id\n      window&#91;fnName](containerName);\n    };\n    if (document.getElementById(scriptId)) {\n      renderMicroFrontend();\n      return;\n    }\n    \/\/first load the manifest.json this contains the way forward\n    fetch(`${this.host}\/manifest.json`)\n        .then((res) =&gt; res.json())\n        .then((manifest) =&gt; {\n          const script = document.createElement(\"script\");\n          script.id = scriptId;\n          \n          script.crossOrigin = \"\";\n          \/\/load out the path to the main.js file\n          script.src = `${this.host}\/${manifest&#91;\"src\/main.js\"]&#91;\"file\"]}`;\n          script.onload = () =&gt; {\n            \/\/ call the function defined on top which will resolve then the app\n            renderMicroFrontend();\n          };\n          document.head.appendChild(script);\n        });\n  }\n};\n&lt;\/script&gt;\n\n&lt;style scoped&gt;\n&lt;\/style&gt;<\/code><\/pre>\n\n\n\n<p>At the <em>mounted<\/em> stage we will trigger a check and if necessary a load of the corresponding script files.<\/p>\n\n\n\n<p>Each script is ensured to be loaded only once, where the script id is the identifier here. Each microfrontend must follow a convention here and offer in its own application a function called <em>render&lt;name><\/em> (e.g. <em>renderDiceRoll<\/em> ).  The manifest json must per convention offer a file with <em>src\/main.js<\/em> which consists of the render functions. The script is than attached to the document header section so that it can be loaded. Afterwards the function is called, which calls the actual render function of the microfrontend app.<\/p>\n\n\n\n<p>Calling one microfrontend is than as simple as including this in the main container.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;vue-micro-frontend name=\"diceroll\" host=\"http:\/\/localhost:8000\"\/&gt;<\/code><\/pre>\n\n\n\n<p>The full sample can be found here <a rel=\"noreferrer noopener\" href=\"https:\/\/github.com\/zelle7\/vue3_microfrontend_poc\" target=\"_blank\">https:\/\/github.com\/zelle7\/vue3_microfrontend_poc<\/a><\/p>\n\n\n\n<p class=\"has-large-font-size\">Not solved here<\/p>\n\n\n\n<p>Things which are left out here: <\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>Authentication<\/li><li>Communication between services<\/li><li>Different technologies (like loading vue3 and vue2 or even things like react)<\/li><\/ul>\n\n\n\n<p class=\"has-large-font-size\">Links<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>Sam Newman on Microservices <a rel=\"noreferrer noopener\" href=\"https:\/\/samnewman.io\/books\/building_microservices_2nd_edition\/\" target=\"_blank\">https:\/\/samnewman.io\/books\/building_microservices_2nd_edition\/<\/a><\/li><li>Same concept but with React <a rel=\"noreferrer noopener\" href=\"https:\/\/blog.bitsrc.io\/how-to-develop-microfrontends-using-react-step-by-step-guide-47ebb479cacd\" target=\"_blank\">https:\/\/blog.bitsrc.io\/how-to-develop-microfrontends-using-react-step-by-step-guide-47ebb479cacd<\/a><\/li><li>Github Repo with the code &#8211; <a href=\"https:\/\/github.com\/zelle7\/vue3_microfrontend_poc\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/github.com\/zelle7\/vue3_microfrontend_poc<\/a><\/li><\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Microservices are a well known concept nowadays. Handling encapsulated logic in different applications, can be a nice fit for bigger teams to avoid problems in deployments and development speed. Most of the time though, people are talking about the backend, where the UI part to some extend stays as a black box. One of the [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1,10,105,72],"tags":[76,104,103],"class_list":["post-273","post","type-post","status-publish","format-standard","hentry","category-allgemein","category-javascript","category-microservices","category-web","tag-java-2","tag-microfrontend","tag-vue"],"_links":{"self":[{"href":"https:\/\/www.zellot.at\/blogs\/wp-json\/wp\/v2\/posts\/273","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.zellot.at\/blogs\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.zellot.at\/blogs\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.zellot.at\/blogs\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.zellot.at\/blogs\/wp-json\/wp\/v2\/comments?post=273"}],"version-history":[{"count":4,"href":"https:\/\/www.zellot.at\/blogs\/wp-json\/wp\/v2\/posts\/273\/revisions"}],"predecessor-version":[{"id":281,"href":"https:\/\/www.zellot.at\/blogs\/wp-json\/wp\/v2\/posts\/273\/revisions\/281"}],"wp:attachment":[{"href":"https:\/\/www.zellot.at\/blogs\/wp-json\/wp\/v2\/media?parent=273"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.zellot.at\/blogs\/wp-json\/wp\/v2\/categories?post=273"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.zellot.at\/blogs\/wp-json\/wp\/v2\/tags?post=273"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}