Service-oriented architecture, better known as SOA, is a widespread architectural principle in IT. Put simply, individual infrastructure components are implemented as separately encapsulated services. Appropriate interfaces are provided to allow the services to communicate with each other. The resulting benefits include loose coupling, scalability and easier deployment.
In this blog post we will give you an insight into why we opted for this architectural principle at nine.ch, how we implemented it, and what advantages and drawbacks it brings us.
Why Not Microservices?
Before we go into more detail about SOA, we would like to explain what microservices are and how they differ from SOA.
The microservices model is another architectural principle. As with SOA, applications are composed of independent services. However, the microservices philosophy is closer to the Unix world: “Do one thing and do it well”. A microservice should be as straightforward as possible and – as a guideline – something that can be newly created by a handful of developers in less than a month.
So why is this blog post not about microservices? Although we at nine.ch like the philosophy of microservices and can place some of our services in that category, we still feel that it makes no sense to rely on microservices exclusively. From a pragmatic point of view, some cases can involve more costs than benefits.
This is why we describe our infrastructure as service-oriented. Microservices can be regarded as a more specific subset of SOA.
What Is SOA?
The opposite of SOA would be a monolithic application that covers every necessary feature. Suppose, for instance, that we need an application for a hosting company that must be capable of customer and server management.
It seems plausible that these functions belong together. But what if server registration means that there is a virtual server to be set up, emails need to be sent to customers, or customers should be given access via a customer portal? In all these cases, our application will keep on growing.
Now let’s look at the SOA approach. Here it would be useful to subdivide the application into something like the following:
- Customer management
- Server management
- Customer portal
The customer management component is in charge of customer data. The server management module incorporates everything to do with server setup, while the customer portal allows customers to manage their personal data and servers.
The customer portal itself does not need a database; it only accesses the customer and server management components. There is no need for the customer management module to know anything about the servers or for the server management module to know any customer details (except for a global customer number).
What advantages are there to this slightly more complex arrangement?
Because the applications are not closely linked, they are easier to replace, expand and scale.
If one of the applications stops working properly, for example due to errors creeping in, the other applications are not affected.
By separating the application into its areas of responsibility, we avoid the need to maintain an enormous application. This makes the codebase clearer and allows the individual components to be developed independently of each other.
Additional Services and More Communication
Of course, IT infrastructure is rarely this manageable. At nine.ch, for example, we have many daemon-type services. These are services that are always running and wait for specific events to which they then react accordingly.
One example is a daemon that rewrites the DHCP configuration to the DHCP server in the event of a change in a server’s network configuration.
If we want to incorporate daemons of this kind, we must also think about suitable communication channels. In the previous SOA example, the application communicates via HTTP. However, this is a fairly strong coupling where reciprocal calls can even block each other.
At nine.ch, we use a message bus (AMQP). In short, this is a protocol where messages are sent to a broker and then retrieved by a consumer. The consumer can then implement the appropriate logic depending on the message.
As a concrete example, an AMQP message is sent when the network configuration is changed. The daemon that should rewrite the DHCP configuration in this case will do so as soon as it sees the relevant message.
With this structure, neither the daemon nor the server management module know anything about each other. Additional consumers can also be simply attached to a message with no adjustment to the existing structure. An example might be a daemon designed to rewrite firewall rules when the network configuration changes.
Integrating Legacy Applications
We would also like to outline another scenario that we feel is important – dealing with legacy applications, i.e. older applications that have taken root over time. We would prefer to replace them, but often lack the time and resources to do so. Instead we live with them, although we want to expand them as little as possible.
How do we incorporate such applications into SOA infrastructure, then, without adding something like a REST interface?
At nine.ch we have for example an outdated PHP application that we invest very little into but that still contains relevant information and has dependencies. Because we can’t just replace it, we decided to take the approach of integrating it into our infrastructure with a connector application. The diagram below illustrates this:
The connector application is simply an API that can be used to access the data in the legacy application. What brings us the greatest benefit is the fact that there is now just one direct and clearly specified dependency to the legacy application – namely the connector. Should the day come when the legacy application is replaced, only the connector implementation needs to be adapted.
Additional Tips on SOA
At this point we would like to summarise a few tips that will simplify development in an SOA and its maintenance:
Construct All Apps the Same Way
At nine.ch, we use Ruby and Ruby on Rails. The advantage here is that we can instantly find our way around an application because all applications are structured the same way
Keep Monoliths in Check
Having many services doesn’t mean that we have no monoliths. For example, our customer management system has grown to a large scale. But that need not be a bad thing. What is important is that monoliths are kept under control by always carefully considering where new features belong.
Efficient deployment is an essential part of efficient development. At nine.ch, we use ChatOps for this. If we want to distribute a new version of a service, all it takes is a line in the chat and a bot will automatically release the latest version.
Tests, Tests, Tests!
We can’t overstate the fact that tests are vital to sustainable software infrastructure. They guarantee service functionality even after major changes. With a comprehensive test suite, there’s no need to hope that everything will work when developing a new feature or changes to existing functions. It provides a level of security in development that we would no longer want to do without.
We rely heavily on SOA (and microservices) at nine.ch. Our infrastructure now totals more than 25 services. Below is a brief summary of the resulting advantages and disadvantages for us:
- Encapsulation: Small encapsulated applications have a clearly defined scope, making them easy to replace and develop further.
- Deployment: Deployment is simplified, as every change is less extensive and can therefore be rolled out quickly.
- Distributed: In the event of a problem, only a small part of the infrastructure is affected. Individual services can be scaled to requirements.
- Development process: When developing individual components, it is also necessary to run dependent services in the development environment. This can lead to overheads in the development process.
- Overall system complexity: It is not always easy to keep track of the overall infrastructure. In particular, we need to make carefully thought-out decisions as to what services are responsible for which tasks.
- Higher maintenance costs: There are always higher maintenance costs involved for multiple different applications. This is noticeable, for example, when a security vulnerability is closed in a framework and multiple applications must be updated and rolled out.
As you can see, even SOA is not perfect. We must always take care to keep track of the overall infrastructure and not allow growing complexity to make us inefficient. If this can be achieved, service-oriented architecture is the perfect environment for fast, agile and high-quality development.