{"componentChunkName":"component---src-templates-blog-post-js","path":"/microservices-node/","result":{"data":{"markdownRemark":{"html":"<h1>GitTix (A Simple StubHub Clone):</h1>\n<h2>Overview:</h2>\n<ul>\n<li>\n<p>This project is not meant to clone every aspect of stubhub.com, but rather implement some of the features like create tickets, and ordering them by applying a lock over ticket for a certain period of time before it gets available again if the order was cancelled, or the payment was not done in that period of time.</p>\n</li>\n<li>\n<p>The front end is built with <code>next.JS</code> and it does not clone the styles of stubhub.com, it is just a simple front end that allows interaction with the backend.</p>\n</li>\n</ul>\n<h2>Future Enhancements:</h2>\n<ul>\n<li>\n<p>Support https (cert-manager.io) - check <a href=\"https://github.com/mu-majid/docker-k8s-lab\">docker-k8s-lab</a> repository. (As a side note: check also <a href=\"https://letsencrypt.org/docs/\">https://letsencrypt.org/docs/</a>)</p>\n</li>\n<li>\n<p>Add in Email Support (send a user an email after they have paid for an order - new service should be created that integrate with  sendgrid .... ).</p>\n<ol>\n<li>CreateAnother service that assemble emails from nats (payment will send events to nats).</li>\n<li>Use mjml and also create another service (redis) to cache some templates there.</li>\n</ol>\n</li>\n<li>\n<p>Add in 'build' steps for our prod cluster (right now all services+client are running in dev mode, Add in additional Dockerfiles to each service prior to deployment).</p>\n</li>\n<li>\n<p>Create a staging cluster.</p>\n</li>\n<li>\n<p>Instead of publishing event from services to NATS streaming directly, We should save the event in the db instance connected to the service, and then we could have something like a watcher (cronjob) that checks new events (with flag notPublished) in the service db and publish them to NATS.</p>\n</li>\n<li>\n<p>For example:</p>\n<ul>\n<li>Create a ticket scenario:</li>\n</ul>\n<ol>\n<li>Create ticket object &#x26; event object in service DB (has collection for tickets and collection for events) [THIS SHOULD BE A DB TRANSACTION].</li>\n<li>Cronjob (watcher) polling the service's db for newly created events to publish them to NATS.</li>\n</ol>\n</li>\n<li>\n<p>This eliminates data integrity issues that might arise if we created ticket object but failed to publish event for whatever reason.</p>\n</li>\n</ul>\n<h2>DB Resources :</h2>\n<ul>\n<li>Tickets</li>\n<li>Users</li>\n<li>Orders</li>\n<li>Charges</li>\n</ul>\n<p>In this app we will follow a resource-based microservice design, this basically means each resource will has its own microservice.\nOf course a feature based design could be a better approach, but i am trying to start simple a t first.</p>\n<h2>Microservices :</h2>\n<ul>\n<li>Auth Service (sign-in, sign-out, sign-up)</li>\n<li>Orders Service</li>\n<li>Tickets Service</li>\n<li>Expiration Service (Hanlde locking on orders)</li>\n<li>Payments Service (using <code>stripe</code>)</li>\n</ul>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1024px; \"\n    >\n      <a\n    class=\"gatsby-resp-image-link\"\n    href=\"/static/081e49011ba9015439ae1c471bc3c512/62a6a/payment-flow.png\"\n    style=\"display: block\"\n    target=\"_blank\"\n    rel=\"noopener\"\n  >\n    <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 62.890625%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAIAAAAmMtkJAAAACXBIWXMAAAsTAAALEwEAmpwYAAAB4UlEQVQoz22S224aQQyGef+H6UUvWqVplSoqlULTRAUEJFlgF1j2fGDnZHtO1UJD1Bbr18ij+T/bGnmQJHm03mRpkcXxbhvv4jTbp7sw2kTbeBtXaVLmhZCScVE3XdOyk455NwCpWMdYxwSXmjQiglS8qmXdqKYVdavagzXWWefdUafoUzcACUIIKZXWxjpvnLfek9bu7PO9z/k/rLPuqD4ZaNKnZ8gS2m91EsNuA4wB6KzkacnLWhhtsCwo3uB+ZwHPJd5gsZjsbz+HN1fscSSrUkhchWmwTsJthkjVbJwPv5ajoZHyAgzBYnn98fH9O/brvstSBboTtumICYuo6/l0ef0h+X5rpfQXOq+exWzM5xM+n8imBjJJpZabumhJa4Npwl/mEK0s4AUY9lu1fmHBE2xWxPnbd/ne6r1vOpgERdHAv7B3Tsyn9WhY/RgeHu6gqYBs2WJSirrTmozg4nAQZXVQiv6DvWeLaXTzKfhyld99k1XJJeY1hnFTHUhJlab5w/3PKIy0Nv2cJ5iQvPfWWhE8ydlYLaZyPoa2+Wts5zmXaVZUZY1kvH3tbLTRp0AiQAQgQEIyxp5kz7Kul7HOONvLDkD1VuwBDccTkUCBFBIBQQEhHdezh1/X63gz9jfS0OSKuuFCVQAAAABJRU5ErkJggg=='); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"payFlow\"\n        title=\"payFlow\"\n        src=\"/static/081e49011ba9015439ae1c471bc3c512/2bef9/payment-flow.png\"\n        srcset=\"/static/081e49011ba9015439ae1c471bc3c512/6f3f2/payment-flow.png 256w,\n/static/081e49011ba9015439ae1c471bc3c512/01e7c/payment-flow.png 512w,\n/static/081e49011ba9015439ae1c471bc3c512/2bef9/payment-flow.png 1024w,\n/static/081e49011ba9015439ae1c471bc3c512/62a6a/payment-flow.png 1122w\"\n        sizes=\"(max-width: 1024px) 100vw, 1024px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n        decoding=\"async\"\n      />\n  </a>\n    </span>\n<span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1024px; \"\n    >\n      <a\n    class=\"gatsby-resp-image-link\"\n    href=\"/static/fdb0a37d5303efc4bc4174e2a90d567a/73caa/payment-flow2.png\"\n    style=\"display: block\"\n    target=\"_blank\"\n    rel=\"noopener\"\n  >\n    <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 60.546875%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAIAAADtbgqsAAAACXBIWXMAAAsTAAALEwEAmpwYAAABxUlEQVQoz22Q627UMBCF8/6PBOqPUkCIolYg6NKEbZKNb/HaSVxv7Fw8Y+TstgjE6OjIY/uzPCeb/HSyo9b90BtjrLWnpME8S3nS2ip9UtpbC4ghhLD+0bKsGQaMiPFcmISA3nl9VHVVk0PT1AdGqDXWu+l8enGIGQK2/ZrX/bdHWnIn+3VyXraSNGS3+5nneZEX+WMuuDCD+ReGgHs2XX8p39z8+HDflGJ2o+NcCNFKKRljnAsppBDt0A//gQs2Xd8+XX3Kb+4OlVwRQB2VlFIpzbngXCil1FG9wBARkwNmiNGOQbQDbwfVjaMHCMAoIw3hlNVlVVf1ue10l2IBREAIiOeZ42teCBACrMAo51wUebF72OV5UT6VnAmtEgwhXC5DyCJgKkDYnkSMYQ1mMJ3uBBeUUEpYK9q005nZCLP/WN69PXy9stXnDCHGbfpzBhES3OnunBZjjFLKGNNK6W5YrLT1Lfn+jj+8PzX3G4z4GmPECAEooaQhlNDdw27/ay+4qKpaHVX6NuDLjDFL2N8wBnTOeeedc33XPxubutF75xEiBNwECJgt8zJN8zIv87zM0zxN82XhtybZFAJACiXVtr74b/aqqKmoNo1nAAAAAElFTkSuQmCC'); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"payFlow2\"\n        title=\"payFlow2\"\n        src=\"/static/fdb0a37d5303efc4bc4174e2a90d567a/2bef9/payment-flow2.png\"\n        srcset=\"/static/fdb0a37d5303efc4bc4174e2a90d567a/6f3f2/payment-flow2.png 256w,\n/static/fdb0a37d5303efc4bc4174e2a90d567a/01e7c/payment-flow2.png 512w,\n/static/fdb0a37d5303efc4bc4174e2a90d567a/2bef9/payment-flow2.png 1024w,\n/static/fdb0a37d5303efc4bc4174e2a90d567a/73caa/payment-flow2.png 1110w\"\n        sizes=\"(max-width: 1024px) 100vw, 1024px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n        decoding=\"async\"\n      />\n  </a>\n    </span></p>\n<p>This is a microservices application that uses <code>Async</code> communication between services, so it would be a great idea to list the events that we will work with through out the App.</p>\n<h2>Events (Services use for Communication):</h2>\n<ul>\n<li>\n<p>UserCreated</p>\n</li>\n<li>\n<p>User Updated</p>\n</li>\n<li>\n<p>OrderCreated</p>\n</li>\n<li>\n<p>OrderCancelled</p>\n</li>\n<li>\n<p>OrderExpired</p>\n</li>\n<li>\n<p>TicketCreated</p>\n</li>\n<li>\n<p>TicketUpdated</p>\n</li>\n<li>\n<p>ChargeCreated</p>\n</li>\n</ul>\n<h2>Application Architecture Overview:</h2>\n<p>We're going to use <code>Next.js</code> for client (<code>SSR</code>), and <code>mongodb</code> for other backend services, except expiration service, we are going to use <code>Redis</code>.</p>\n<p>Also, for the event bus server, we are going to use <code>NATS</code>.</p>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1024px; \"\n    >\n      <a\n    class=\"gatsby-resp-image-link\"\n    href=\"/static/a975b07b001d6afa53f7cf782979dfbd/29114/OverviewDiagram.png\"\n    style=\"display: block\"\n    target=\"_blank\"\n    rel=\"noopener\"\n  >\n    <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 56.25%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAIAAADwazoUAAAACXBIWXMAAAsTAAALEwEAmpwYAAAB1UlEQVQoz2WS247TMBCG+yZ020UND43Ek4C4gL0oBcIuTQ9JmmRzsh3bsZ3YiWOnkBQhDqOR5mL0af7/txfLtYMgUG3DpxKccSEaijDOihpAUiKGa6k6qXrV9V3fKyF6IQBAy7WzuFs7jLHr9WqMsdaO42jNCGKS+VA1ChIZ5cI95GHKKtZrRpv9NwPyuua/YT7Bg7EzboxNfRh5pWwUxCJ8rlwvO4UFpK2mmH960HHI2Awv1w7nEzyO43Ue1tjMR/EByEYhIi8Zdb0sSKqqVpri1t0OaVzf4Lt7h1CmzRVhzkTX6dEMNgtQ5JWtUCVu/YS4x/x4gZDKnlTg43sVBbRmy9VmsX75KsmpXwz7c7a/YD/Xxtj9F3/34akRspxl756ej7NsDoq3b14nn7eM8RerzWJ171SYsXakTBAuqRhul+MD6GWPaOcFYOuGXgAw1wOjt8v/erbWzo7t5DmoIq8UvIVERGnlBegcFpA0CqP265+e/34qO05wekYXbwqsxOrxVL57+L7z4rKWmlRT2klYM/YfPOPGmCLBaYAa0VaElZCEGXFPT4CSvq6F9ziAYgpslr2BAJnBKKm6+RMpqeSvUrfuVae7QXd9N+21sWOF8M/AfgD8QTDmKVwyqQAAAABJRU5ErkJggg=='); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"OverView\"\n        title=\"OverView\"\n        src=\"/static/a975b07b001d6afa53f7cf782979dfbd/2bef9/OverviewDiagram.png\"\n        srcset=\"/static/a975b07b001d6afa53f7cf782979dfbd/6f3f2/OverviewDiagram.png 256w,\n/static/a975b07b001d6afa53f7cf782979dfbd/01e7c/OverviewDiagram.png 512w,\n/static/a975b07b001d6afa53f7cf782979dfbd/2bef9/OverviewDiagram.png 1024w,\n/static/a975b07b001d6afa53f7cf782979dfbd/71c1d/OverviewDiagram.png 1536w,\n/static/a975b07b001d6afa53f7cf782979dfbd/29114/OverviewDiagram.png 1920w\"\n        sizes=\"(max-width: 1024px) 100vw, 1024px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n        decoding=\"async\"\n      />\n  </a>\n    </span></p>\n<h2>Kubernetes Cluster Config:</h2>\n<ul>\n<li>\n<p>We are going to use google cloud platform to deploy our cluster, also we'll use <code>skaffold</code> to handle all the syncing and building images by connecting it to <code>google cloud builder</code></p>\n</li>\n<li>\n<p>right now <code>kubectl</code> is now connected to our local cluster created by <code>minikube</code>, so we need to create another context that tells <code>kubectl</code> how to connect to the cluster we created on <code>gcp</code>.</p>\n</li>\n<li>\n<p>To create a context for <code>kubectl</code>, we can use GCP UI or use <a href=\"https://cloud.google.com/sdk\">google-cloud-sdk</a>. i will use the sdk :</p>\n<ul>\n<li>Step1: Download and install the SDK</li>\n<li>Step2: run <code>gcloud auth login</code>, and login with your gcp account.</li>\n<li>Step3: run <code>gcloud init</code> and configure the project containing the cluster (should be created from gcp ui)</li>\n<li>Step4: run <code>gcloud container clusters get-credentials CLUSTER_NAME_ON_UI</code>, and this will create a context entry in kubectl config for the cluster we created on gcp.</li>\n<li>Step5: check that cluster entry was added to local <code>kubectl</code> config by running <code>kubectl config view</code></li>\n<li>Step6: switch between different contexts using this command <code>kubectl config use-context CONTEXT_NAME</code></li>\n</ul>\n</li>\n</ul>\n<h2>Creating Secret inside k8s:</h2>\n<pre><code>- We run this command `kubectl create secret generic SECRET_NAME --from-literal KEY=VALUE`\n</code></pre>\n<h2>Password hashing Reminder :</h2>\n<ul>\n<li>When a user tries to signup, hash password and save it to db.</li>\n<li>When a user tries to sign in, we retrieve hashed password from db and match it to password he provided.</li>\n</ul>\n<h2>Is user Logged In (Microservices Architecture):</h2>\n<ul>\n<li>\n<p>How would we know if a user is authenticated in a microservices architecture?</p>\n<ul>\n<li>\n<p>Approach 1.0 : services Sync with auth service (inspect JWT/Cookie in the auth service) - If Auth goes down, all service are down (the request will be like request ==> orders service ==> auth service)</p>\n</li>\n<li>\n<p>Approach 1.1 : Auth service here acts as a gateway and all our requests has to pass through it. (the request will be like request ==> Auth service ==> orders service)</p>\n</li>\n<li>\n<p>Approach 2.0 : Each service should be able to inspect the cookie or the JWT on its own. (No dependency on outer services.) but this has a downside, that we now don't see authorization (if user was banned in auth service).</p>\n</li>\n<li>\n<p>We will implement approach 2 for the sake of separation between services</p>\n</li>\n<li>\n<p>Solution to Approach 2.0 downside : always add expiration on cookies and jwt issued by auth service, when request comes to other services (handling auth logic by itself) and see that the jwt has expired then call auth srv for refresh token refresh or we could reject request with expired token.</p>\n</li>\n<li>\n<p>What if the user is banned but the token is still active ? user/auth service should emit an event telling all services that a user is banned.(cached in mem for ex)</p>\n</li>\n</ul>\n</li>\n</ul>\n<p>Sometimes with each each request we could ask for user permission from auth server (if that was a requirement)</p>\n<ul>\n<li>Browser handle cookie expiration, and a user could copy its content and use it. But JWT encode expiration in itself.</li>\n<li>JWT is supported in all languages with official implementation, but cookies are not.</li>\n<li>Cookies may require some backing datastores (for sessionID) but this is not required.</li>\n<li>Remember Cookie is just a Transport mechanism that may be used for auth (we could use cookie and transport jwt via it).</li>\n<li>Since we are implementing SSR React App, we will use cookies for transporting JWT (think about the first request you issue to the server, and you want to include some data in it (service workers with nextjs is a workaround if cookies are not prefered))</li>\n</ul>\n<h2>Server Side Rendering:</h2>\n<ul>\n<li>\n<p>We are using <code>Next.JS</code> for SSR, It is convenient for SEO optimization and also saving up multiple requests (script tags in normal react app), and also better for mobile app.</p>\n</li>\n<li>\n<p><code>Next.JS</code> does not use <code>Reacr Router</code>, instead it rely on a folder called <code>pages</code>, and each file inside this folder maps to a route.</p>\n</li>\n<li>\n<p><code>getInitialsProps</code> is a special function used by nextjs to fetch data before rendering a component. Executed on the server.</p>\n</li>\n<li>\n<p>REMINDER: in k8s, all services in the same namespace can communicate with each other via cluster ip service, if they lie in different namesapces, we should use a domain like this <code>http://NAMESPACE.SERVICENAME.svc.cluster.local</code></p>\n</li>\n<li>\n<p>External Name Service could be used to map a url mentioned above to a more simpler one.</p>\n</li>\n<li>\n<p><code>getInitialProps</code> executed on server: 1. Hard Refresh, 2.Clicking link from different domain, 3.Typing URL in address bar.</p>\n</li>\n<li>\n<p><code>getInitialProps</code> executed in browser: navigating to other page in same app.</p>\n</li>\n<li>\n<p>This means when we make a request inside getInitialProps we must pay attention to whether it will be executed on server /client</p>\n</li>\n<li>\n<p>Remember, GetInitialsProps arguments are different when used with PageComponent and a CustomApp Component</p>\n</li>\n</ul>\n<h2>NATS Streaming Server:</h2>\n<ul>\n<li>\n<p>docs.nats.io is the documentation for what we are going to use.</p>\n</li>\n<li>\n<p><strong>Important Note</strong>: <code>NATS</code> and <code>NATS Streaming Server</code> are two different things, NATS streaming server is built on top of NATS and gives some advanced functionality related to event sharing.</p>\n</li>\n<li>\n<p>We are going to use the official <code>nats-streaming</code> image from docker hub.</p>\n</li>\n<li>\n<p>Only raw data could be shared accross nats, so when sharing object, we should stringify it.</p>\n</li>\n<li>\n<p>Nats server keeps record of each client connected to the server.</p>\n</li>\n<li>\n<p><code>Queue groups</code> are something that are created inside a <code>channel</code> (topic), and are used to deliver event/msg to only one member of the group (subscribers), so essentially what it does is prevent event from being processed by multiple subscribers of the same type(same logic). Also, if any other listener that is not in any QueueGroup, will recieve the event as well.</p>\n</li>\n<li>\n<p>By default, when an event is recieved by the subscription, it will be marked as processed.</p>\n</li>\n<li>\n<p>If manualAckMode is set to true, and the subscriber did not ack the event, the event will be sent back to nats server, and then sent to another(or same) instance in the queueGroup.</p>\n</li>\n<li>\n<p>When a subscriber restarts, nats will hold the older subscribtion instance for a little period of time, and might also send events to it, whch will cause some events being delayed for processing. So some arguments may be used, like <code>hbi, hbt, hbf</code> to configure the client health check.</p>\n</li>\n<li>\n<p>Another solution would to close client gracefully, but this one doesnot account for server failure (mimic it by killing process from task manager).</p>\n</li>\n</ul>\n<h3>Common Concurrency Issues:</h3>\n<ol>\n<li>\n<p>If a service fails to process event, and other events are processed on other services instances, this means events are processed out of order, which could be unacceptable depending on the business case.</p>\n</li>\n<li>\n<p>Maybe one listener(service) runs more quickly than another. Causing some events being processed out of order.</p>\n</li>\n<li>\n<p>Depending on hearbeat settings (and the service being shut unGracefully), NATS might think a dead service to be alive and send it an event to be processed, causing this event to be delayed.</p>\n</li>\n<li>\n<p>Race condition may occur caused by an event being processed twice. This could occur if one listener is very slow and is about to finish in 29.999s (NATS consider event as failed after 30s and did not recieve ack from service). So after 1ms NATS consider the event as failed and send it to be processed again, while the first service is still (probably finished processing) processing the event.</p>\n</li>\n</ol>\n<h4>Common Questions:</h4>\n<pre><code>1. Are concurrency issues related only to Microservices Architecture? No. it occurs also in Monolith apps, but it's just more prominent in MicroServices Arch.\n\n2. Why not run one instance of the listener server? Still the same issue (processing out of order) might occur, and we end up with a bottleneck in our application that we are not able to scale.\n\n3. Could we share state between listener services (SRV-1 => cache/Queue &#x3C;= SRV-2) and check events sequence number? This solution might seem good, but it strictly process events in sequence and this might be a penalty, if we for example have a multitenant system, tenant(B) is independent from tenant(A), and yet we are processing their events sequencially rather than concurrently, also this issue might occur on one-tenant level application, if we are updating two different records for example. [One Update At A Time]\n\n4. Building on the Idea above, Could we check last processed event by the event # and also the resourceId (or tenantId+resourceId) ? This solution is good (having pool of sequence queues based on resources, in other words, queue per resource that is updated by events) (Meaning, if events are updating 2 resources, we could have one queue for each resource). But we have an issue related to NATS, and that is, the repeated numbering of events is not allowed in the same channel, so we might end up creating many channels which introduces an overhead.\n\n5. A possible solution might be built upon the previous idea, but now the publisher will save the event data.\nThe Steps are: VIDEO{285}\n  a. Pub send event to `NATS`.\n  \n  b. `NATS` send event `seqNum` back to `publisher`, and pub saves this event( `seqNum`, `payload`, `lastSeq` (not used yet) ).\n\n  c. `NATS` forward this event to a `listener`, then it gets processed, and in the database of the `listener`, we check the `lastSeq` attribute of the incoming event and see if it matches the `lastEventIDProcessed` for the resource, then process the event, and finally, we save the `seqNum` of the incoming event as `lastEventIDProcessed` for the resource being updated.\n\n  d. When another event is being published for the same resource (identified by event payload that might have resourceId) publisher checks its database for the last event for this resource, and assign this last event's `seqNum` to the new event's `lastSeq` attribute.\n\n  e. Steps a, b, c are repeated.\n\nNOTE: NATS really does not send he seqNum back, so this solution might work with other solution other than NATS. This could be solved by relying on numbering being from publisher itself.\n</code></pre>\n<h4>A solution:</h4>\n<pre><code>- Re-design the services again - We did not mention anything about the publisher, and began to design based only on NATS.\n- But if we take a closer look at publisher service, we know that it needs a database to save the requests/resources it recieves. So, we could rely on numbering these requests as they come and use this transactionNumber as our indicator for events sequence.\n- Also, the listener's database still has the `lastEventNumber` but it now corresponds to the transactionNumber in the publisher service. And the logic should check this `lastEventNumber` before any processing.\n\n- So, numbering the transactions or events as they come and make the consumers aware of this numbering, solves the ordering of events problem we had previously.\n</code></pre>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1024px; \"\n    >\n      <a\n    class=\"gatsby-resp-image-link\"\n    href=\"/static/87272a46758b8323068bf304ac61a842/471ef/sol1msrv.png\"\n    style=\"display: block\"\n    target=\"_blank\"\n    rel=\"noopener\"\n  >\n    <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 60.546875%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAIAAADtbgqsAAAACXBIWXMAAAsTAAALEwEAmpwYAAAB8UlEQVQoz12RaW/TQBCG/f//QCREBeX4ViFV8JXQ0kaQhiS4vmLHt10nju8jm+yNNiaIdjQarebYd59Z6cevhbL2l7ora9Zcc2TDXuquavl+nLpJodiR4SWqHetOvLJDw4mXqvWompYd3k2m0i7dYgTbugF9fwDgCADY7/dl2aVpm2V9lh3rmmLMCDk55YyRfccpsdeWlBcF55wxxp8Z+xsZf1HgjNNTs++HUhhGXdvVVV1Vddf1GJOygVVPqg7nDTqAIy4yXOa4yGCRU9jT446AlHMihlVFWy7k2XQ2/fngu0FRNjcPzoer8cer8ZcbPXLj+/cX4zevv168un13WcV6pF378ifOKs+LhHJd1dttWuRF13YQkjgFyiqWtUB3cwhgIi/8+Sz+Pd8ojxRUoFwdmjXn2HEDKc/PzExEyuiu3MRpmNfprkwwQf/RClQK0WYxDybfjcm9lOW5SFFKCaWUYYLuFrejy9Ho7ejzt+s97BnlhIgagogy1ndtEYVNkliqelYmp7Uy0Rc8uYatmY6x9k2I4PAiznjbtJqqq4qmaYbrBZq+Og9T9s+f/QsTpeHeAzjE0ZO9dizT2iZbc2W+ZB5MANDTBs6yg8BwGP7ZdT1plwlmjAUXORlGBEGEERZR5AWw2AkZeijGmHPuef4fVw+avtbZzvEAAAAASUVORK5CYII='); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"sol1\"\n        title=\"sol1\"\n        src=\"/static/87272a46758b8323068bf304ac61a842/2bef9/sol1msrv.png\"\n        srcset=\"/static/87272a46758b8323068bf304ac61a842/6f3f2/sol1msrv.png 256w,\n/static/87272a46758b8323068bf304ac61a842/01e7c/sol1msrv.png 512w,\n/static/87272a46758b8323068bf304ac61a842/2bef9/sol1msrv.png 1024w,\n/static/87272a46758b8323068bf304ac61a842/471ef/sol1msrv.png 1185w\"\n        sizes=\"(max-width: 1024px) 100vw, 1024px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n        decoding=\"async\"\n      />\n  </a>\n    </span>\n<span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1024px; \"\n    >\n      <a\n    class=\"gatsby-resp-image-link\"\n    href=\"/static/0f069297a1507b7d611349415f9871c3/471ef/sol2msrv.png\"\n    style=\"display: block\"\n    target=\"_blank\"\n    rel=\"noopener\"\n  >\n    <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 62.5%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAIAAAAmMtkJAAAACXBIWXMAAAsTAAALEwEAmpwYAAACP0lEQVQoz1WQSW/bMBCF9f//SIEmaHPptUWRtU6KxEttWYtjrV5kUuI+HFJyIaeXPgw4h8HjzPuCp0XytHifzJPHafwwS37N4odpukjKuKLzzf4lLN7i+ndYvEXldJ2/htntZPoyC6d/4lX0Hmglm2PTEkJOJ9Yx1lLGuBLcaKOU0kpbsEYbMAa0BrAAVtPWaAAlA0Ts+2FUP5ZFp6WkhFLaGaWMxX44/6fBezgCrzXfj2bvPSI6dA5dQ+UqKpZR8fwaLtdFfWQe0VtAY3p3WeMtLV/S+fc8mgRGKillmZfZNh/PZqKhopgt4tvb5O7+sM2b5fz55vr563Uzez2fz9a6tJTTiIQbEijetR1LkzRcrVvaccYsmHpyv7z5nHy7IZt4jE6p7joQ4jwM1vrdkbWdIJQFoJW1qJXRyliwWmvGOavLerVskoju9v3wL/RHl9I0p5axtiUkGC600KLgsqp2abqNonSbV1wZ6/zoHGu0Dhe3EOq9YvUJ9wSC8zgdHLpNtl8l9TIuV0m1jIooKS3Y3vdo0Vr78XrvpFRJZaLaZ0cXXH49O3SHw4EQKoQUQmitu64TXHQdy7OiLKo8L/K8aGkruEwqHe981rjgcs6AiC1tOWMOnTHGOTdiIpQzXhXlrqrLosyynJ6oVibbye3eVicILijGzc2xIYQiolJqRCCE976eT++uPj1+uVr//CG40Np8ZHQOASAAsGYUCC6kkAAgpVRKCyGkVKSqdnG8T9O2Kr1ziA4MAIwIEPEvLhTT5MMk+MsAAAAASUVORK5CYII='); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"sol2\"\n        title=\"sol2\"\n        src=\"/static/0f069297a1507b7d611349415f9871c3/2bef9/sol2msrv.png\"\n        srcset=\"/static/0f069297a1507b7d611349415f9871c3/6f3f2/sol2msrv.png 256w,\n/static/0f069297a1507b7d611349415f9871c3/01e7c/sol2msrv.png 512w,\n/static/0f069297a1507b7d611349415f9871c3/2bef9/sol2msrv.png 1024w,\n/static/0f069297a1507b7d611349415f9871c3/471ef/sol2msrv.png 1185w\"\n        sizes=\"(max-width: 1024px) 100vw, 1024px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n        decoding=\"async\"\n      />\n  </a>\n    </span></p>\n<h2>Event Re-Delivery:</h2>\n<ul>\n<li>All events are saved by NATS in an event-history.</li>\n<li>We can replay all events when a subscription is created by manipulating the subOpts obj. or only run lastDelivered.</li>\n</ul>\n<h2>Durable Subscribtion:</h2>\n<ul>\n<li>Is created when we create a subscribtion with <code>setDurableName</code> on the subOpts object.</li>\n<li>It means that a subscribtion has recieved and processed an event successfully. So if the service goes down, NATS will send only the events that subscribtion has missed out during its time down.(when used with <code>setDeliverAllAvail</code>).</li>\n<li>Using queueGroups, will make sure that if a client is disconnected, NATS will not delete the subscribtion</li>\n</ul>\n<h2>Common Module Note:</h2>\n<ul>\n<li>\n<p>We have written a common module (npm package) that includes all errors and common middlewares and also all the events that the services communicate with. But this solution is only feasible if all the services are written in typescript.</p>\n</li>\n<li>\n<p>if we have a polygot architecture (using different languages i different services) and want to enforce something like events structure, we could use:</p>\n<ul>\n<li>JSON schema.</li>\n<li>Protobuf.</li>\n<li>Apache Avro</li>\n</ul>\n</li>\n</ul>\n<h2>Optimistic Concurrency Control:</h2>\n<ul>\n<li>\n<p>We are going to use OOC to handle any concurrency issues that might rise due to the async communication between services that might cause event processed out of order.</p>\n</li>\n<li>\n<p>When should we include version field of a record with a event? - Ans. Increment/Include a record version number when the <strong>primary service responsible for the records</strong> emits an event to describe a <strong>create/update/destroy</strong> operation.</p>\n</li>\n</ul>\n<h2>Ticket Locking (reserving):</h2>\n<ul>\n<li>\n<p>Implemented by having <code>orderId</code> property on tickets docs in the <code>tickets-service</code>. So the presence of <code>orderId</code> on a ticket means, it is locked and can not be updated.</p>\n</li>\n<li>\n<p>It could also be implemented by having a route in the <code>orders-service</code>, that queries tickets associated with orders that have <code>awaiting-payments</code> status.</p>\n</li>\n<li>\n<p>Second option require adding extra route to orders-service, so i chose the first option cuz it alot easier.</p>\n</li>\n</ul>\n<h2>Production Deployment Workflow:</h2>\n<ul>\n<li>This picture summarize the workflow</li>\n</ul>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1024px; \"\n    >\n      <a\n    class=\"gatsby-resp-image-link\"\n    href=\"/static/69746ed3d26944a404a170a5091d7693/1c1a4/prod-depl-workflow.png\"\n    style=\"display: block\"\n    target=\"_blank\"\n    rel=\"noopener\"\n  >\n    <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 85.546875%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAARCAIAAABSJhvpAAAACXBIWXMAAAsTAAALEwEAmpwYAAACIklEQVQ4y41SXXObMBD0f+xLXvrQl/7DttNO06QzcWo7xS7+wNhgxyAQGIwxIDBYd1IHnCaOm06zc6OZk7Tau9W1gAPnUBb7guVpmsbxNkvTqiwBAP+HlkCBCEnC1kFsGNZEMwjxt3HGD7w5eg44WRFbiEIK9EJ2q7rtwaozdH6o7sgMAfBVZIGwiYuFW4zmvjp1TZJaXgYcjmRRXxAvPNGQUQp017vuyFEmVNG87pBMF2HdM2DtyIHzAwfecF5QFhBEuaLRnrrqDa3e0F6QBADSNGNZ7hDHskiW5rs4AQ51KWdlh9vCdIolzQ2SLene8nNeHVw/HUw9ZUyUMelrdOUm/1DeFH09vJtQZbrujty5nRaMWTSdO9VADxTNm5HC8nM8/t9pz4hY7qv9vipLXpaHsuRFUUpEx9v1J+5gQgaao4zthbV5cA6e/rmGEEI2rjbRpAKX9uZu7F119Mu21hvR2TIUf7t9mv9BbYQfMpMw0070+2hJCydg8GLZ52SoyU7AblXnuju7bGsd1VlRhq8iPyhnCzc37N3cipcuc/zsnCwbiGOneBxJ0UyOMK3wy4121Z1f9+Zfb2fjmSelPBVoRVG82UTlvmz4p2UjDVLD3pkkmd1Hhp0QLzlXvnj7/s3Fu073p5SSc3jsQkqhL7xP37WPV+qHb78+30yHOpVCPA0pYivL8ozlVXUADs+DhxEj61w3qW5SNyjWEav3AR/jN2htwn8o8wB6AAAAAElFTkSuQmCC'); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"prodwork\"\n        title=\"prodwork\"\n        src=\"/static/69746ed3d26944a404a170a5091d7693/2bef9/prod-depl-workflow.png\"\n        srcset=\"/static/69746ed3d26944a404a170a5091d7693/6f3f2/prod-depl-workflow.png 256w,\n/static/69746ed3d26944a404a170a5091d7693/01e7c/prod-depl-workflow.png 512w,\n/static/69746ed3d26944a404a170a5091d7693/2bef9/prod-depl-workflow.png 1024w,\n/static/69746ed3d26944a404a170a5091d7693/1c1a4/prod-depl-workflow.png 1046w\"\n        sizes=\"(max-width: 1024px) 100vw, 1024px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n        decoding=\"async\"\n      />\n  </a>\n    </span></p>\n<table>\n<thead>\n<tr>\n<th>Git Repositories approaches in Microservices Arch</th>\n</tr>\n</thead>\n</table>\n<ol>\n<li>Mono Repository (<strong>Appraoch used</strong>)</li>\n</ol>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 625px; \"\n    >\n      <a\n    class=\"gatsby-resp-image-link\"\n    href=\"/static/f9877f26ec7beb2e929d81950596904c/80d71/monogit.png\"\n    style=\"display: block\"\n    target=\"_blank\"\n    rel=\"noopener\"\n  >\n    <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 81.640625%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAQCAIAAACZeshMAAAACXBIWXMAAAsTAAALEwEAmpwYAAAC1UlEQVQoz12S22/bNhjF8/+/7WnYyzCgLQoE67rZaZs0bVPPl1q+5LbU9SyRImlSvIikLDuWlVoUB+UyYAMIgjj8fjiHBzzQ2mhtrM0YTa6v/oIAEky+fp2FYYQxgTCGAAIAEMKLRYgRMSbT2hhjjbEHtXN17bz3dLl82z7qd7vzb9+Gg3671ZqMx5fn50ftdjAcdjt/nn38hGDsva9rVzdQfVBVTtmSpju9dps7n+98tq1XhTebZuU7r9f1vejz0puNo+lO2rKq3CMMeQmlb59Ofz8OWqfTd2dXrfeT9unk9XHw6s3gpHNz9PGi9X769uyyd44j3szv903Yg2rvEC+R8u3T8YtXpz/8+Pzl609/nASHrc7x56ufn7d++uW3Z4cnL3798Ozw5M2HKRQuFmW1d772jXPIihu4DdkeCA+Fg7IOkyriDjRnD4SPlYfSx9Iv6PdmkhaPsKscYFvMrJCWshQTjjCXMkNYzvFtjCVjKkYJJhxjLoQhzAK2rar6Aa4jeqtN7l01n816nc58NvO1syab49vVam1S9WXQHwfDQa+3MibPt+Fy455gB5MiwpowDYkCWGJmCNMAqwXdRUgRZiBWaJnGJMVUA6xBUlTVQ2FN7AISnXCDiCRLhYigTMdELZYFwCmhKWEaLRVuriQk+v+xrd1U3++uLy4H3e7N9XW53a5W65DuMKaTYDgdjyaj4Hw8mY4CxhRMnuDqPnaIUkRUjBVAEjXx0hDJBd39DXgEk3nEIFYQyxjLMFaPsR/fzApErUzXQq25zBnPhMwxNQtaIGqkavREZELlQq0Q/U/bLk4KSsXFeBQMBuPRcNjvX04nPJHhssiy3KbpxXT6pdebBEFudb66jf5tu6pcxLaAaIj5ArAYC4A4IjJe6jlpnCm38F6ESDBuEbMhfXKuXW3Xd9w0311mZbPbUphSmF2aNTrXxYMobcl1wXVh13f1PfwPBDtZX3caL2YAAAAASUVORK5CYII='); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"monogit\"\n        title=\"monogit\"\n        src=\"/static/f9877f26ec7beb2e929d81950596904c/80d71/monogit.png\"\n        srcset=\"/static/f9877f26ec7beb2e929d81950596904c/6f3f2/monogit.png 256w,\n/static/f9877f26ec7beb2e929d81950596904c/01e7c/monogit.png 512w,\n/static/f9877f26ec7beb2e929d81950596904c/80d71/monogit.png 625w\"\n        sizes=\"(max-width: 625px) 100vw, 625px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n        decoding=\"async\"\n      />\n  </a>\n    </span></p>\n<ol start=\"2\">\n<li>Repository-Per-Service Approach</li>\n</ol>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1024px; \"\n    >\n      <a\n    class=\"gatsby-resp-image-link\"\n    href=\"/static/64de49a108f6db223b4bbd1360169c8a/66465/per-service.png\"\n    style=\"display: block\"\n    target=\"_blank\"\n    rel=\"noopener\"\n  >\n    <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 44.140625%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAIAAAC9o5sfAAAACXBIWXMAAAsTAAALEwEAmpwYAAABv0lEQVQozz2PW2+bMABG+f+/JaFdtUIWTdrloYWypUpDQoJtbHO1MYZCSGggmCmTtu/x6DsPRxuv1+nvlFLTNMVRtFmvKSYEY3A4UEIQgLvtdudum7r+f1NKDcOg9cOIYwFwgigPQg5xJqo2jIUHwj2MfJR4fiRkk2RyD2NEsoBySJgo22matLa7fv621o3nuWndmdbMsGNx/mEf9MWLbtr3X5yZYftU/nZDfeHMHp/ulzfibKKb3F0UybqmOQNIC1Ey2bGyj8WHELUPMCExL06p7Fk5YJqhgCJEMWWJ6NWotEuvQnYS8ohwyvP3ND8VdZ+KjuUNwikNWZofedXzqsc0RzjBJKWRyOTlJg/DCAjf+jEKCx+zfZAdTz1NCtePAM0ByT2YVnWX8ncPZT7hkIpDwHjRTmrSzt1gfH/TF5ZuWncLe2ZYSX76+eI/fF3pxvOnpTM37T0RKzfSTWduPD0sndmj9cv91xzyrqlb4Ad5XuTVB69uzZSmCBEEcZRKVl0z2cuypTQiJJRlmxS9Uko7d9cNlACz1QZ5IN4hkRbdgVavW7zawFcXrb0k5OcgaTyYve2C9Q57iIGoUeP4B2kT2+y4ZKdJAAAAAElFTkSuQmCC'); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"pergit\"\n        title=\"pergit\"\n        src=\"/static/64de49a108f6db223b4bbd1360169c8a/2bef9/per-service.png\"\n        srcset=\"/static/64de49a108f6db223b4bbd1360169c8a/6f3f2/per-service.png 256w,\n/static/64de49a108f6db223b4bbd1360169c8a/01e7c/per-service.png 512w,\n/static/64de49a108f6db223b4bbd1360169c8a/2bef9/per-service.png 1024w,\n/static/64de49a108f6db223b4bbd1360169c8a/66465/per-service.png 1097w\"\n        sizes=\"(max-width: 1024px) 100vw, 1024px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n        decoding=\"async\"\n      />\n  </a>\n    </span></p>\n<ul>\n<li>\n<p>Per service approach might seem the right one but we are not going to use it as having mutiple repos to maintain is a tedious job.</p>\n</li>\n<li>\n<p>We will use github actions to run our test cases when a pull request is created/updated to our master branch.</p>\n</li>\n</ul>\n<table>\n<thead>\n<tr>\n<th>Digital Ocean Deployment</th>\n</tr>\n</thead>\n</table>\n<ul>\n<li>\n<p>We have seen <code>gcp</code> previously, so I will use digital ocean for prod-deployment (actually it is a lot cheaper :D )</p>\n</li>\n<li>\n<p>Steps:</p>\n<ol>\n<li>install <code>doctl</code> tool.</li>\n<li>from digital ocean panel, get an access token</li>\n<li>run this commad in your terminal <code>doctl auth init</code> and paste your personal access token.</li>\n<li>then connect doctl to our remote cluster by running <code>doctl kubernetes cluster kubeconfig save &#x3C;cluster_name></code>, and the kubectl context will be switched automatically.</li>\n</ol>\n<p>4a. reminder: <code>kubectl config view</code> to list all contexts, then to connect to a cluster(context) run <code>kubectl config use-context &#x3C;context_name></code>.</p>\n</li>\n<li>\n<p>Deployment Plan:</p>\n</li>\n</ul>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1024px; \"\n    >\n      <a\n    class=\"gatsby-resp-image-link\"\n    href=\"/static/eb168641a9f749ad8653dcb56f09ba80/42de8/github-action-depl.png\"\n    style=\"display: block\"\n    target=\"_blank\"\n    rel=\"noopener\"\n  >\n    <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 55.078125%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAIAAADwazoUAAAACXBIWXMAAAsTAAALEwEAmpwYAAABkElEQVQoz32RT4/UMAzF+/0/DkIcEOKOtAc4gLTqNI6dpOlM/rZNUnY7alHT2b2AsHyID857P7/mZ2c+fP726evTxy9Pz9zv+75t237WVvu9/hqbKb0wUN9//LowEcclhtEa66zzzv9eXmKI3nlrnTUuzzmGaG7WmqNLLs19XUvOgx5KyiUl3WsEIhSEIs1Z97q+CQG987rX0AEHzoHHMDbr6zqNsyA5ximnMugroZBCKqnOUQopSAoUwcfrcEWOyIk4jXE6lK1xnKE1binLoAdBQgqlZF8dXZVUxz6KEEJdJkISJMY4Nvf1frua9rk1NzNPqVc96xgwQMB5mpVUHJB1wDpmjVVSXdpL13aXtgs+HraDD8jRu5BzqcxIeGDnlHU/HMAcOWBwQfdD/ZoD4zHEB7MU78xDdfVP5vBgRiRO0/hmGzowN3MyIz+Q3pQ1IXFAAH5em13YyfVQdtYJEt75UpXPUyvV51zOnF3NOc2p5mxsjTrn0qyv9zQnKeQ8pZwyIQED4kewS1n2/9S2/wFwqF3zDAHghQAAAABJRU5ErkJggg=='); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"deplplan\"\n        title=\"deplplan\"\n        src=\"/static/eb168641a9f749ad8653dcb56f09ba80/2bef9/github-action-depl.png\"\n        srcset=\"/static/eb168641a9f749ad8653dcb56f09ba80/6f3f2/github-action-depl.png 256w,\n/static/eb168641a9f749ad8653dcb56f09ba80/01e7c/github-action-depl.png 512w,\n/static/eb168641a9f749ad8653dcb56f09ba80/2bef9/github-action-depl.png 1024w,\n/static/eb168641a9f749ad8653dcb56f09ba80/42de8/github-action-depl.png 1033w\"\n        sizes=\"(max-width: 1024px) 100vw, 1024px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n        decoding=\"async\"\n      />\n  </a>\n    </span></p>\n<ul>\n<li>\n<p>Steps (again):</p>\n<ol start=\"5\">\n<li>Using github workflows we will create a file for each service and a file for all our k8s manifests</li>\n<li>These action files are responsible for each flow per service as indicated in the above picture.</li>\n<li>configure our digital ocean cluster to have all the secrets our app needs like jwt-secret and stripe-secret (MAKE SURE kubectl is using the digital ocean cluster context)</li>\n<li>Configure/enable ingress-nginx for the digital-ocean cluster (<code>kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v0.43.0/deploy/static/provider/do/deploy.yaml</code>)</li>\n</ol>\n<p>Important link (ingress with digital ocean) : <a href=\"https://github.com/digitalocean/digitalocean-cloud-controller-manager/blob/master/docs/controllers/services/examples/README.md#accessing-pods-over-a-managed-load-balancer-from-inside-the-cluster\">https://github.com/digitalocean/digitalocean-cloud-controller-manager/blob/master/docs/controllers/services/examples/README.md#accessing-pods-over-a-managed-load-balancer-from-inside-the-cluster</a></p>\n</li>\n</ul>","frontmatter":{"title":"Building Ticketing System Microservices Notes","featuredImage":null}}},"pageContext":{"slug":"/microservices-node/"}},"staticQueryHashes":["3649515864"]}