After two decades as a developer, I would like to summarize my thoughts about a topic that crossed my path multiple times: SOA. A high-level definition of SOA is developing and combining services into something greater. Hopefully, this combination is useful as a product, whereas each service should be independent and reusable on its own.
SOA, as a buzzword, has been heavily used in tech articles and many developers probably thought “hey this would look nice on my CV/resume.” In other cases, it has been a management decision to introduce SOA since it seems easy to throw these 3 letters against any problem. After all, we’ve all been taught “Divide and conquer” right?
Before you continue reading, I probably should warn you: I think in many cases SOA is the wrong approach. I have worked for many companies that use SOA and have seen many projects fail or struggle. I had a minor epiphany recently and must say: I really like the idea of having monolithic web applications. I should probably explain…
Normally, a developer works on and starts his application on his local machine. For a Rails developer, basically all you have to do is run
rails server in your project directory. When you slice one service out of your app, you will have to start this service separately before you can begin your work. When something does not work, you will have to grep through the logs of your application AND your service.
Of course, this will work and does not sound like a major overhead, but think of having five or maybe even 10 services involved. Now, you have to start all of them, find the logs if something went wrong and so on. Later you will try to automate things, maybe use some configuration management tools to install all the services automatically and even introduce some centralized log management tool. The bottom line is: You will need more time to do the same thing compared to a monolithic app.
Let’s discuss testing for a second. You want to test your application with automated integration tests, right?. One option would be to use an existing service running on an integration system and test against that. We have done it this way many times and often we forgot to check what version of the service was running there. I can assure you this is a great way to kill a few hours.
However,you could mock everything. This basically doubles your testing work because when changing a service’s behavior, you now have to change your mocks too. Maybe you decide to start new instances of each service when running your integration tests. This is probably the safest way but takes time to implement and time during each test too.
You have to deploy these services and your web application. Some management effort is needed to know what to deploy and what side effects are possible. If you do continuous deployment and every commit with green tests could go live, well, you could be all right most of the time. However, if you deploy once a week it will definitely get harder. Many people think deploying a service is easier than deploying the whole app.
I don’t get that. When you have a cluster of Rails web servers behind a load balancer you can deploy one after another without any user impact. Generally speaking: The deployment of a service in production usually also means having a cluster of this very service to ensure a deployment without user impact. Therefore you will probably need a load balancer for every single service configured. This can get ugly in a hurry.
In a monolithic application scaling is quite easy. If your application runs slow, you will add some servers to scale out or buy some larger machines. Perhaps you split the db and have more db servers (sharding). Having a couple of services means you will have to scale each of these services. What if one service is quite busy? Now this service nees another machine and you will have to monitor which services are getting slow individually.
In operations you want some monitoring of the systems health. You could use Munin to graph response times or Nagios to see all servers are up and running. With SOA you usually have a dozen services to monitor.
Performance and Reliability
Making an HTTP connection to your service is a lot slower than just calling a library/gem or some code inside your application. You have to keep in mind how your service calls are designed. The interface has to be quite complex, since the target should be to call the service only once per user request to keep latency low. Remember, every call to your service will take a couple of milliseconds. That is quite acceptable if you call it once, but if you call it a hundred times to serve a single user- request, you are in deep trouble.
If you think your socket connection to the service is as reliable as a method call inside your app, you will be surprised. A socket connection is handled like an open file in Linux. If one of your services is getting slow, (maybe an index is missing) each service call is running into a timeout. Since your customers are still sending requests, the amount of sockets increase. Eventually, you will run into the socket limit have to figure out which service is responsible for the disaster. In the meantime, your application is down and you enjoy the relaxing melodies of all the sirens and flashing red lights.
That all being said… what are your options?
One option could be to slice your system vertically instead of horizontally. Meaning instead of putting your low level stuff into services, try to slice your app into different domains, maybe one Rails application for managing user profiles, one for doing some interaction with others and so on. These applications could share the database or have different databases and communicate, if necessary, via REST services.
When are services adequate?
In large companies every department has its own set of applications. They maybe even have several programming languages or frameworks in use. If they need to communicate with a service that many applications need, perhaps something like fraud prevention, I don’t beleive there is any way around SOA.
In my opinion the important point for every developer and architect alike is to think about the consequences of having services. We should not do it because it is cool nor merely because other companies do it, as it could lead to future trouble. Keep in mind that, when extracting code to a service, we have yet another application to deploy, maintain, and monitor. Furthermore, we have additional network communication that could fail and will be significantly slower than calling functions or libraries on the same machine.
Simply put: If things are getting too big and ugly, we have other techniques, such as splitting our app horizontally or creating libraries/gems.
What are your opinions or experiences regarding SOA?