CodeNewbie Community 🌱

Cover image for Service Oriented Architecture: A Naïve Solution
Marlon Esparza
Marlon Esparza

Posted on

Service Oriented Architecture: A Naïve Solution

Last week, for my software-engineering boot camp, I had to work on a team of four. Each teammate was required to develop their own service for a product page, and develop a reverse-proxy that would serve each service.

Before going further, I'll briefly define “Service Oriented Architecture” and “Reverse-Proxies”. Each service in a S.O.A. embodies the code, dependencies, and data integrations necessary for the service to run independently of every other service in the architecture. Of course, some of these services may need to communicate with each other for data, but this data shouldn't be the determining factor as to whether or not the service successfully functions. “Reverse-Proxies” are servers that act as middle-men between the client and the web servers providing each service. R.P.s can have many features that go beyond the scope of this post- like balancing the load between internal servers, caching content to further reduce the load on internal servers, or adding compression/ TLS encryption to the communication channel between the client and R.P.– but all you need to know for this post is the practice of R.P.s hiding the existence and characteristics of the origin servers for each service being rendered.

Now that we've briefly defined S.O.A. and R.P.s, I'll discuss how I was able to develop my own service, and then how I was able to not only render my service, but each of teammates' services' through a R.P.. The functionality of my service, or the purpose it served, isn't relevant to this post. What matters is understanding that my service was built using React.js, and that it could stand entirely alone and be rendered through its own local server without failure. There is no coupling at all of state, logic, and uptime between mine or any of my teammates' services'. In the case that my service makes a request to another service for data, and the services' are not yet brought together by a R.P., then the request simply fails with an error code, of 404, and my service is still able to display the rest of the data it owns- rather than collapsing.

If you've ever used React.js without CDNs, content delivery networks, then you know that you need to have the necessary dependencies installed and imported. One of the dependencies that's required for a React.js development environment is Webpack. Webpack is responsible for taking all of the code and dependencies required for a React.js application to run, compiling it all, and bundling it all into one big Javascript file. When this Javascript file gets sourced in a properly set up HTML file, through a script tag, then the React.js application will render onto the browser's screen. This entire React.js application, with all of its own logic and styling is my service- and each my teammates' would have to create their own as well.

With all of that in mind, imagine we start an Express server at port 8001 and type into our browser's search bar- “localhost:8001/33”. This request is responded to with an HTML file that sources our Webpack's bundled Javascript file, as mentioned above, and displays my service on the screen with all the relevant data for product “33”. This is perfectly fine for this server, and its local repository, but what if we wanted to display this service from our R.P.'s server? Imagine we start another Express server at port 8000 and type into our browser's search bar- “localhost:8000/33”. This request is responded to with an error message “Cannot GET /”. So, the first step would be to set up the R.P.'s “home” url to respond with an HTML file- just like the server running on port 8001 does- but unlike the server running on port 8001, the R.P.'s server local repository has no awareness of there being any HTML file, or bundled Javascript files for each service. The question then becomes, how do I get or create an HTML file that has sourced all the necessary bundled Javascript files that come from the other service's servers? The answer here is “CORS”– cross origin research sharing. “CORS” plays a critical role here, and if “CORS” is not enabled on the other servers, then the R.P. can't communicate with them, and every request sent from the R.P. to the other servers, attempting to obtain a resource, will be blocked.

After “CORS” has been enabled, we will be able to send requests to each service's respective server to obtain whatever resource we need. In this case, we need to retrieve each bundled Javascript file from every server. Going back to the server running at port 8001, if you inspect Google's Developer Tool's network tab then you'll see a loaded resource named “bundle.js”. Why? This is because the HTML file that's sent back to the client sources the bunde.js file in a script tag. So, theoretically, if my R.P. sends an internal request to “localhost:8001/33”, and then sends this response to the browser, the response will be the exact same HTML file, but this would result in an error, code 404, message “Cannot GET “localhost:8000/bundle.js” displayed in the R.P.'s browser console. Why? Again, the R.P.'s local repository has no awareness of there being any HTML file, or bundled Javascript files for each service. What's the solution to this problem? Rather than my service's server only responding with an HTML file, it can also respond with the bundled Javascript file itself. How can my service's server do this dynamically? All I need to include in the internal GET request being sent from my R.P. to the service's server are query parameters. A query parameter that I set on this internal request is: {proxy: true}. This allows my service's server, running on port 8001, to parse the incoming request, and if req.query.proxy is equal to true, then it'll only send back the bundled Javascript file- otherwise, it will send back the HTML file that sources the bundled Javascript file. Voila! The R.P.'s internal request is finally being responded to with the bundled Javascript file, but what now?

We've finally arrived to the point where my R.P. is receiving the bundled Javascript file from my service's server. We can't just send this file back to the browser though because what good is a standalone Javascript file on the browser without an HTML file to source it? Also, where's this HTML file going to come from, and how will it know to source the bundled Javascript files? Ultimately, it's up to me to make this happen. My solution was inspired by Webpack's way of dynamically creating an HTML file with an injected template string. When my R.P. gets the bundled Javascript file, it then uses the Node file system API to write the file contents to a new file named “[service]Bundle.js”, and then uses the file system API again to write a new HTML file that sources “[service]Bundle.js”. At the end of this promise chain, after having written all services' to “[service]Bundle.js”, and then sourcing each “[service]Bundle.js” in the HTML file using script tags, my R.P. responds to the browser with the HTML file properly set up to render each service. As you can see, without the R.P. ever having prior awareness of any of these files existing, it is still able to create them on an initializing request to the “home” url.

There you have it- the R.P. is now rendering each service dynamically, but to optimize the performance of our R.P., we'll need to add one last thing. If a request from a browser has already been sent to “localhost:8000/33”, then we can assume we no longer need to re-write each bundled Javascript file, nor do we need to re-write the HTML file that gets sent back- we can simply send back the HTML file while avoiding costly and unnecessary operations.

On top of this, the AJAX requests that get sent from the client, in search of needed data for it's component, get intercepted by the R.P. and then forwarded to the respective server which owns the needed data. Since “CORS” in enabled, these internal requests being sent from the R.P. should be a breeze. Once the data gets sent back to the R.P. from the respective server, the R.P. then sends this data back to the client to fill the respective components.

If you enjoyed this post and found it to be helpful in anyway, then please like, share this post, and follow me. That would be highly appreciated it!

Top comments (0)