├── ReadMe.md
├── apps
├── microservice
│ ├── server.js
│ ├── start.sh
│ └── worker.js
├── monolith
│ └── server.js
└── routes
│ ├── about.js
│ ├── index.js
│ └── signup.js
└── package.json
/ReadMe.md:
--------------------------------------------------------------------------------
1 |
2 |
12 |
13 |
14 | # The Monolith versus The Microservice
15 |
16 | ### *A Tale of Two Applications*
17 |
18 | **Date:** *Wed Jul 29 2015 15:38:30 GMT-0700 (PDT)*
19 | **Author:** [Marak](http://twitter.com/marak)
20 |
21 |
22 | *The original blog post "The Monolith Versus The Microservice" can be found [here](http://hook.io/blog/the-monolith-versus-the-microservice-a-tale-of-two-applications).*
23 |
24 | ## Preface
25 |
26 | The current standard approach to building backend HTTP services is monolithic. An HTTP server process is responsible for any amount of "routes". A route represents a unique url pattern that is associated with a unique piece of functionality ( the route handler ).
27 |
28 | The problem with this monolithic approach is that since every route is sharing the same process and server state, potential issues in one route can affect the state of the entire application or server. This is bad.
29 |
30 | The **Monolith** approach to application design leads to having to worry about the global state of the entire application any time a single route is modified.
31 |
32 | The **Microservice** approach to application design offers a clear path improving the stability and scalability of an application by isolating services into distinct composable units.
33 |
34 | ## A New Company Builds A Web Application
35 |
36 | A brand new technology company emerges. They must build a new web application to power their business.
37 |
38 | **The application must contain the following sections:**
39 |
40 |
/ - Index / Company homepage 41 | /about - Information About the Company 42 | /signup - Sign Up for Company Product 43 |44 | 45 | The new company quickly gets to work, but unfortunately for our new company, their development team is not so great. During development they introduce a few bugs into the application. 46 | 47 | ### Server 48 | 49 | First they build the
server
component. This actually looks pretty good for now. Good job team!
50 |
51 | ```js
52 | var express = require('express');
53 | var app = express();
54 |
55 | app.get('/', require('../routes/index'));
56 | app.get('/about', require('../routes/about'));
57 | app.get('/signup', require('../routes/signup'));
58 |
59 | app.listen(9999);
60 | ```
61 |
62 | ### Index Page
63 |
64 | Next, they move on to building the `index` section. This page looks good. No mistakes yet!
65 |
66 | ```js
67 | module['exports'] = function index (req, res) {
68 | res.write('someHTML
variable in the wrong place and has broken encapsulation. The someHTML
variable will continue to append to itself until the server runs out of memory.
93 |
94 | *Introducing additional state outside the scope of the route handler breaks functional encapsulation and can cause bad things can happen.*
95 |
96 | Let's not worry about this error for now. We'll get to see it in action later.
97 |
98 | ### Signup Page
99 |
100 |
101 | Finally, after several weeks of arduous development the team finishes the last section, `signup`.
102 |
103 | ```js
104 | module['exports'] = function signup (req, res) {
105 | while(true);
106 | };
107 | ```
108 |
109 | Looks like the development team really blew it on the signup page!
110 |
111 | *At least the office feels a bit warmer today.*
112 |
113 | The signup page is another contrived example, but it simulates what can happen with bad code. This route handler both fails to call res.end
and puts the process into an infinite loop. Running this page will cause the server to hit 100% CPU ( and get quite hot ).
114 |
115 | ### Deployment
116 |
117 | Since the new company didn't have a budget for testing or quality assurance, the site was deployed that week on Friday afternoon.
118 |
119 | Unfortunately for the new company, their site went down almost immediately.
120 |
121 | Debugging the issues was complicated, partly because the server kept locking up due to lack of available RAM and CPU.
122 |
123 | Ultimately, a key potential customer was unable to access any sections of the site on launch and choose to signup with a competitor. The company failed.
124 |
125 | ## The Same Application built with Microservices
126 |
127 | Now, the same development story with the same development team. The only difference this time is that instead of building their application as a monolithic service, the company choose to use microservices.
128 |
129 | ### Server
130 |
131 | First, they built the front-facing HTTP server. This acts a load balancer, sending incoming requests to the worker cloud with a round-robin strategy.
132 |
133 | The team chooses to use the run-remote-service npm module, which is a thin component responsible for proxying HTTP requests. They could have just as easily used the request module instead.
134 |
135 | ```js
136 | var express = require('express');
137 | var runRemoteService = require('run-remote-service')({
138 | pool: ["10000", "10001", "10002", "10003", "10004"]
139 | });
140 |
141 | var server = {};
142 |
143 | server.listen = function () {
144 | var app = express();
145 | app.get('/', runRemoteService);
146 | app.get('/about', runRemoteService);
147 | app.get('/signup', runRemoteService);
148 | app.listen(9999);
149 | };
150 |
151 | module['exports'] = server;
152 | ```
153 |
154 | *You may notice the worker pool is hard-coded in this example. With a bit of imagination you can imagine an elastic worker pool where workers can register themselves with the load balancer at run-time.*
155 |
156 | ### Worker
157 |
158 |
159 | Second, they built the `worker` component, which will be used to execute services. Workers receive incoming HTTP requests from the server and execute service source code in response to these requests.
160 |
161 | The team chooses to use the run-service npm module which acts a thin layer of abstraction for executing untrusted source code in response to incoming HTTP requests.
162 |
163 | The `run-service` module provides a level of control over service execution to ensure that any issues within the service remain isolated and always trapped and returned to the `server` in a meaningful way.
164 |
165 | ```js
166 | var argv = require('minimist')(process.argv.slice(2));
167 | var port = argv.p || 10000;
168 | var runService = require('run-service');
169 | var express = require('express');
170 | var worker = {};
171 | worker.start = function (opts, cb) {
172 | var app = express();
173 | app.post('/', runService);
174 | app.post('/about', runService);
175 | app.post('/signup', runService);
176 | app.listen(port);
177 | };
178 | module['exports'] = worker;
179 | ```
180 |
181 | ### Building out the pages
182 |
183 | After completing the `server` and `worker`, the development team builds the same routes for `index`, `about`, and `signup` in the exact same way as before.
184 |
185 | **The second application still contains the exact same bugs that brought down the first application.**
186 |
187 | ### Deployment
188 |
189 | There is still no budget for testing or quality assurance, so the team again deploys the application on Friday afternoon.
190 |
191 | **Surprisingly, the site almost works! Great job team!**
192 |
193 | The `index` and `about` sections are working flawlessly. The out of scope someHTML
variable in `about` causes no issues in this version of the application as every service is now stateless.
194 |
195 | The `signup` page is displaying a timeout error. The error stated that the total amount of execution time for the service had exceeded. It suggested checking for infinite loops or that `res.end()` had not been called. The team was able to quickly identify the issue and had the site working within a few hours.
196 |
197 | The team was also able to fix the `signup` page without ever having to take down the application, or the `index` and `about` sections. Since all routes are isolated, they were able to modify `signup.js` without taking down the front-facing server or any other pages.
198 |
199 | On launch, the same key customer was able to access the site and retrieve information about the product. The customer was unable to signup for the service, but came back the next day and succeeding in signing up. This key customer was crucial to the early success of the business and the new company was able to succeed.
200 |
201 | ## Conclusion
202 |
203 | The tale of these two applications is not about the contrived examples of forgetting to call `res.end`, or accidentally putting the `someHTML` variable in the wrong place.
204 |
205 | **The tale of these two applications is about application design choice.**
206 |
207 | Instead of a programming error, the server could have just as easily ran out of resources. In high-traffic situations, you may find the need to isolate specific routes onto separate servers.
208 |
209 | **Do you really want to have your entire application bound to a single monolith where a minor issue in a single route can take down the entire application?**
210 |
211 | **The Monolith is limited and brittle. The Microservice is scalable and robust.**
212 |
213 | Moving forward, I highly suggest you begin to migrate towards integrating microservices into your stack.
214 |
215 | Looking for an easy way to get your microservices hosted online? Check out [http://hook.io](http://hook.io).
216 |
217 |