Creating and managing a complex system, especially an enterprise one, is not a trivial task. There are a variety of approaches that help to organize and standardize the processes of application development, integration, and support.
The most commonly used approach is an “all-in-one” application often called monolithic or monolith architecture. Monolith’s key feature is that all application parts “live” in one place. Essentially there are no application parts – only one big application.
Another option is microservice architecture, which is a very hot topic these days. In short – if take monolith, split it into a number of independent modules, and create an infrastructure for their interaction, you get microservices.
Because microservices are a trendy topic, there are a lot of articles encouraging us to move from monolith to microservices. One might think that microservices are the silver bullet for all possible cases. But that’s not true. Each approach has strengths and weaknesses, and we’re going to consider them all here.
What is monolithic architecture?
Building an application as a single monolith unit has been a standard approach in software development for quite a long time. Usually, a monolithic application consists of three main parts:
- Database containing data split into tables with relations among them. SQL databases like MSSQL, MySQL, Oracle, etc. are commonly used.
- Server side (backend) of the application, which handles HTTP requests, interacts with the database, external services and APIs, performs business logic and composes HTML views displayed in the browser.
Essential simplicity makes monolithic architecture easier to understand and allows development to start quickly. In addition, many web-based frameworks provide blueprints for building all-in-one applications.
Almost all popular programming languages have at least one monolithic framework:
- Laravel, Symfony, Zend (PHP)
- Spring, Play (Java)
- Ruby On Rails, Sinatra (Ruby)
- Django (Python)
What are microservices?
Microservices are a wide-ranging, well-organized system of low-coupled modules that are each responsible for one specific business task within an application.
Examples of such tasks include:
- get currency rates;
- send email;
- create invoice, etc.
Often REST over HTTP protocol is used to interact between the services. Since every service is independent, only input/output format matters, and that means any service can be implemented in any programming language, can keep data in any storage and can use any tech it needs as long as it provides a defined interface for interaction.
As a concept in software development, microservices are not a new idea. They strongly resemble the design principles of UNIX philosophy. Even though they were first formed in the late 1970s, these rules can still be applied when building microservice applications. Here are a few:
- Write programs that do one thing and do it well;
- Write programs to work together;
- Write flexible and open programs that are ready to be changed at any time.
Advantages of microservices over monolithic architecture
Developers simplify things to lower cognitive load and make their code easier to maintain and support. Modularity is one such simplification. Here are the advantages of using isolated microservices over all-in-one applications.
- Variety of technologies
The team working on the microservice application is free to use any technology best suited for the particular business task. For example, Python and its scientific libraries are a de-facto standard for big data computations or AI usage processing. For storing large collections of non-normalized data, one can use MongoDB. Since all microservices are independent, they only need supporting the format of input/output data to be compatible.
- Easier testing of microservices
Keeping services small makes them easy to test, since there are limited input variants. The only tricky part – a service may be unavailable due to network issues or another problem. So, if the service is dependent on its output, this possibility should be considered and tested.
- Continuous delivery and deployment
Making and deploying a small code change iterations to the server is much easier with small separate services than with an entire monolithic application. A large monolith may take a while to test, build and deploy. In contrast, with a microservices, this often takes just minutes. Only the changed microservice needs to be re-deployed, and other stable working services stay untouched, making the release process faster and easier.
- Optimized development process
Since microservices are isolated from each other, different teams can work independently on separate services. This reduces communication time between teams. API documentation for microservice interactions is often all a team needs to begin developing a new service. In contrast, for a monolithic application, a new team must understand how a whole app works before adding or modifying a functionality.
Compared to scaling the whole application, scaling its most resource consuming part is more effective. With microservice architecture, this is easy to achieve by simply adding more instances for the microservice behind the load balancer. With a monolith, the only solution is to add more instances for the whole application, and this may cost much more.
- Explicit interface
Most programming languages do not provide good mechanisms for defining explicit interfaces. In a monolith application, this can prove a real obstacle. When the developer begins work on a new component, they must understand how to work with other parts of the system and find ways to interact properly without breaking other modules encapsulation. This often leads to tight coupling between modules. Microservices help avoid this outcome by providing a good interaction interface without any coupling.
Advantages of monolithic architecture vs. microservices
Despite all the obvious benefits of microservices, monolith design should not be condemned as a bad approach. It still has several advantages some developers definitely appreciate.
- Easier to debug and test
Microservices are much less dependent on each other, and that means there are often many more scenarios where something can go wrong: poor network performance, service is not responding, etc. For monolithic applications, the number of problem scenarios is relatively low.
- Quicker response
Monolithic apps don’t require us to suffer from transferring data between multiple services. While this may sound insignificant in 2018, when even cell phones have a super-fast internet connection, as the number of microservices in the application expands, 200-300 ms for receiving a response may grow to several seconds or more. Users may have to wait for all 30 API calls to finish before they see anything on the page. To some degree, this problem can be solved by applying an API gateway pattern or GraphQL, but these tools simply hide these data flow from the user. On the other hand, Monolith transportation costs between modules are negligible, since they are within one application.
- Faster design and development
Beginning development of a monolithic system, especially from scratch, is much easier than splitting business processes into independent parts, composing interactions between services, etc. Because microservice architecture has only recently been popularized, it has no commonly agreed standards or tools. Monolithic apps employ a variety of frameworks and tools for different use cases, and they’re ready to go right away. In practice, this makes monolith applications faster to design and develop, especially in the initial prototyping stage.
Secure data processing and transferring are obviously easier at the application level than at each service level. While many transferring security challenges can and must be solved via secure transfer protocols (e.g. HTTPS), managing service-to-service access rights can be very tricky. Concentrating this kind of management in one place has time-tested benefits.
- Infrastructure, which is easier to develop and maintain
An application with multiple microservices requires multiple fallback scenarios in case one microservice goes down or fails to respond for a while. The developer must predict all those scenarios and create fallback options for each. For example, if a service does not respond within a given time period, the request needs to be restarted several times. If the required service fails to respond at all, this error must be handled properly.
With a monolithic application, these types of scenarios are simply not possible since all modules are essentially the same program. This makes the developer’s life easier and allows them to concentrate on development rather than on predicting and resolving all worst-case scenarios.
Software development processes with microservices and monolithic architecture
There is an interesting approach widely used in microservices architecture that says microservices should have “smart endpoints and dumb pipes”. In other words, communication between microservices should be as simple as possible and all the “smarts” should be present in the service itself. Otherwise, complexity is switched to the transport level instead of the application level, and this makes testing and integration more difficult.
This “lite” transport can use something as simple as RabbitMQ or ZeroMQ. Essentially, those are fast and reliable asynchronous fabrics.
Amazon AWS provides the most useful and easy to implement services when building both microservices and monoliths. Though it may seem to be a bit of an overkill to use AWS if you need just one server for a monolith. If you don’t need the luxury of load balancers, scaling and a variety of different services, it may be more efficient to simply use VPS hosting like Digitalocean or Linode.
AWS provides all kinds of services a developer might need when building microservices. Typical setup can include:
- Lambda – a platform for building microservices. It’s serverless (runs only when you need it), scalable and a relatively cheap yet powerful service;
- Elastic Beanstalk – a pre-configured web-server with minimum management that supports most popular programming languages;
- API Gateway – a powerful platform for building API routes on top of microservices
- DynamoDB, RDS – database storage;
- EC2 – a virtual server that allows you to run something special that AWS doesn’t provide out of the box;
- Cloudwatch – a monitoring and logging system;
- EBS, S3 – file storage.
AWS offers powerful tools every app can benefit from. Check them out!
As the popularity of microservices rises, their treatment as a “silver bullet” has become more common. But both microservices and monolithic architecture are good tools. Your choice between these two approaches should depend on the task at hand. If you need a quick prototype, monolith might be a better option, since it allows you save time in initial development stages. For large, complex, and rapidly changing applications, microservices architecture offers greater flexibility and ability to split tasks between several developers.
If you need a complex system that manages the delivery of various cargos with path-finding features, driver tracking, and different service provider integrations – and there are a lot of developers involved in building different parts of this application – you’ll have better luck with a microservices architecture. In contrast, if you need a lightweight e-commerce site or your application’s business logic is hard to divide into parts – you’re a perfect candidate for a monolith.
You can begin with a monolithic architecture, keep it modular, and later split it into microservices when managing the monolithic architecture proves problematic. Both approaches have their pros and cons. Your choice should depend on the professional ability of the team and the complexity of your business task.