Choosing the Right Architecture
There is no perfect software architecture.
Architecture, like economics and life, is all about trade-offs. The solution you’re building has to deliver both the functional and non-functional requirements the stakeholders need. It doesn’t get said enough, but the engineering team is also a stakeholder in the project. The functional requirements are easily understood: they are the specific things about your application that delivers the business value and makes your company money. But what about the often overlooked non-functional requirements?
The best way to capture the non-functional requirements of a system is to examine the quality attributes that are most important to you. Quality attributes may not be something you’ve thought about and can be something hard to wrap your head around. There is an ISO Standard definition, but here it is in simple language.
- Performance. Typically defined as the latency or channel capacity of the system.
- Interoperability. The part of the system responsible for data transfer and communication with external systems.
- Usability. How usable is the system? Are there too many interactions with the system required to get the result?<
- Reliability. The ability of the system to continue operation under predefined circumstances.
- Availability. Essentially uptime.
- Security. The ability of the system to resist malicious actions as well as the possibility of theft or loss of information.
- Maintainability. The ability of the system to support changes.
- Modifiability. How many places in the system need to be changed in order to change a single feature.
- Testability. How well the system allows performing tests.
- Scalability. The ability of the system to handle load increases without performance degradation.
- Reusability. The change using a component of the system in other components/systems with small or no change.
- Supportability. The ability of the system to provide useful information for identifying and solving problems.
Now that we’ve defined our terms, let’s use them to evaluate two possible architectures for a given project.
You’ve been tasked with creating a new API that aggregates information about places from two sources and returns the result to the consumer. One of the sources your company owns and one is from a third party. There may be additional location sources in the future. Customers can either get data from all providers or specify a single provider to get data from.
You can either go with the classic monolith like this:
Or, you can use a microservice pattern:
Which do you choose?
You can say “monoliths are the devil” and go with microservices.
You can say “microservices require us to build more pieces and are more complex than we need” and go with the monolith.
But the correct answer is: “It depends.”
What you should do is go down your list of quality attributes, evaluate each architecture based on the attribute, and determine which attributes are most important to your solution.
(You may come up with different answers, but these are mine for each solution).
- Performance: Monolith wins. With the microservices solution, the network hops from your main API to your worker APIs add latency. No matter how much you tune your operations, this latency with exist. You can parallelize the monolith and do many things async to improve your throughput.
- Interoperability. Push.
- Usability. Push.
- Reliability. Slight edge to microservices. You can design your main API in such a way that if one of the two providers is down you can return partial results to your customer.
- Security. Monolith. There is more surface area you have to secure for microservices.
- Maintainability. Edge to microservices. If the 3rd party web service’s API definition changes or you decide to change the database for your internal service, you only need to deploy a new worker service and leave the main API untouched.
- Modifiability. Monolith. If you change your data model in the microservice, you’d have to update both worker services and the main service and plan your deployment carefully.
- Testability. Push. Most people will give this to microservices, but there are techniques to build “modular monoliths”. (A future blog topic for me)
- Scalability. Microservices. A microservice architecture allows you to scale horizontally. Say the 3rd party source gets more traffic, you can throw more instances of your 3rd party API wrapper on to handle the load. With the monolith, you’d have to scale your main service which could waste capacity.
- Reusability. Microservices. Sure, you can build a modular monolith (here’s the concept again) and share the libraries in other projects. But with microservices, you’ll just need to point your new system to the service you want to access.
- Supportability. Push.
By my evaluation, microservices has a 4-3 edge over monolith. Does that mean microservices wins? Not necessarily. The answer depends on which of the quality attributes is most important to you.
Let’s say one of your functional requirements says the response from your API should always be under 10 milliseconds. That means performance is your most important attribute. In the microservice approach, the network latency between the main service and your worker services can only be decreased so far. There will always be a higher latency floor than in the monolith solution. The more performant solution is the monolith.
Let’s say that your customers are mostly going to pick data from one provider and not get the aggregate response. The traffic going to that provider is going to be 10x or more than to the other providers. That means scalabilty is your most important attribute. The more scalable solution is the microservices one.
As I have shown, choosing the right architecture is a matter of asking the right questions. It’s not about following the latest trends or trying out a new technology. It’s about asking what qualities are most important to your project and determining how best to achieve the desired outcome.