Microservices are in vogue since a lot of years ago, also we see a lot of developers talking about serverless and their benefits, and for many people monolith is the past, we should avoid them. But, have you thought which one is better ? Quick answer, It depends.
And that’s the only true, no one is better to the others, because it depends on the scenario and the requirements to satisfy.
Choosing the wrong architecture –just because you choose the most fashionable at the moment – can lead you to unnecessary complexities or constraints the capacities of your application. Therefore, that’s why it’s important to understand the advantages and cons of them, to have a better criteria and take a wise decision.
Creating an application using a monolith architecture doesn’t mean you are developer creating “legacy” artifacts. It means you have decided that architecture fits better the requirements.
No all the companies are Facebook, Google, Amazon, Netflix – you probably are not one of them – and probably don’t have the same requirements as them. So, trying to apply the same architecture of them might be overkilling and unnecessary if you don’t analyze it properly.
Let’s start them by defining what any of those architectures
This is the most basic of the architectures, you application is a single deployment that contains all the code to make it work, the code usually resides in a single repository. Traditional but far from obsolete.
- Monolith doesn’t exclude you can modularized the code. Actually is a good idea to think how to make the code modular, because some monolith applications can grow in number of classes or files.
- Monolith doesn’t means your application will be a silo deployed in a server. It’s not isolated, it can expose APIs to other services, it can consume API from other applications.
- A monolith application can scale horizontally, with some technical decisions, you can make your monolith to be deployed on multiple servers. There are application servers which provides clusterization out of the box.
- You can apply high availability, continuous integration and continuous deployment, blue green deployments, cache, and many other principles used in other architectures.
- Easy, Fast and cheaper development: Because it’s the simplest architecture, it doesn’t involve complexity in decision making during the development. The code resides in the same repository, it’s your code calling your code.
- Easy deployment: Deploying a monolith application is easier, just deploy it in an application server (or web-server) as a whole unit. There is no orchestration among other services.
- Simple Transaction Handling: They usually use a single database for the whole application, therefore it’s easy to handling transactions and keep data integrity.
- Spaguetti code: It doesn’t mean a monolith application is a spaguetti code. However, because usually a single repository contains all the code for your service, there are many developers involved in the code, and separation of concerns between layers and domain is not set properly. You can find code which is coupled in ways we should not have.
An application deployed as a monolith, but modularized internally by domain, and different layers – following a clean architecture pattern – should not face this issue.
- Scaling as a whole unit: As mentioned before scaling a monolith application is possible. However, you should scala the whole application as a unit. In some cases you might need to scale processes which doesn’t need to scale. Making a non optimal or efficient usage of resources.
However, in many of the applications the requirement to scale is minimum. For example, a blog, or an internal application in a company, where the usage is constant and there are not peaks of usage. But if you application is famous and start getting a lot of traffic, this is something to keep in mind.
- Technologies from the past: Because your application is a whole unit, if you need to migrate the language to another one, or between versions of the same languages, it might require to migrate everything.
Assume your application is written in Node.js 11.2.0 How long will it take to migrate the entire monolith with multiple services underneath to Node.js 18.0.0? What should be done with the tasks required to add new functionality? It is possible that the application will never be migrated.
- Lack of adaptability: When you use Monolithic Architecture, you are limited to the technologies that are used within your monolith. Other tools cannot be used, even if they are better suited to the problem at hand.
For Netflix, or Google, or Facebook, adaptability is very important, they need to improve and adapt quickly. However, for most of the applications adaptability is not relevant.
If you paid attention, some of those cons, might not be relevant for your requirements. Therefore they might not have a big impact on your use case.
A micro-service is an application that is structured as a collection of services, where every service has its unique responsibility, and to complete the full business logic the services connect to each other. The name “micro”, is about the business logic capability of a single service and not about the actual serving size, in other words a micro-service might be not a small application, but it’s a unit of the domain for your application.
Some characteristics of the micro-services are:
- Every service is deployed and lives independently.
- Every service should have its own database. Use cases for shared database are limited.
- A single service can scale up or down, independently to the others. Let’s say your app has a service in charge of a functionality which requires a lot of resources, that service can scale to more instances, while the others keep in a minimum desired number of instances.
- Tech stack freedom: Every service can decide its own tech stack, some of them might be in Java, others in .Net, Python or NodeJS. Obviously having to many technologies might require a lot of developers and experts, but the doors are open to adapt according the use cases of your business. Companies like Facebook, Google, Netflix, can do it…
Maybe on your company, there is only one language – Java – but your services can be use the frameworks depending on the needs, one of the using JDBC, other JPA, using Spring Boot, or Micronaut, or using different versions of Java 8, 11, 17.
- Agile Development: Because of the nature of the micro-services, the development lifecycle adapts very well to the agile development, using Scrum or Kanban, and avoid methodologies like waterfall. So, the new features and advances on the services can be deployed very quickly.
- Scalability: Microservices are modular by nature, they should be able to scale independently depending on its own needs.
- Faster Deployment: Having microservices will require to automate deployments by implementing CI/CD pipelines – which a MUST practice -. Therefore, once the feature is ready, it should be easy to run tests on that feature, regression tests on the service, and be ready to deploy by clicking a button on higher environments.
- Reduce the scope of changes: Applying a change should on your services should not impact other not related services. Therefore, in case of running tests, only tests for your services or end to end related services should be done. It’s not required to test the whole application.
- Fault Tolerant development: – Develop with the worst scenario on mind – Your service might be perfect, never fails, it always responses in the expected time, but you need to consume services from others. Those services might be down at any moment, or have a slow response, the communication with them might fail in the middle of an operation. Therefore, you need to make your application Fault Tolerant, by including: HealthChecks, Timeouts, CircuitBreaks, Retries, Default responses on failures and some other patterns.
- Data integrity and transactions: As the usual approach is to have a database per services, if an operation involves more than one service, if an error occurs, transaction handling is hard to resolve. Therefore, strategies to keep data integrity should be considered.
For example: Let’s say you are doing a purchase which involves the “payment”, “inventory” and “article” services. If the payment fails, but the inventory has been modified, how to handle that ?
- Latency: Each service is independent, it’s not your code calling your code anymore. Usually it’s your code calling a RestAPI, or EventBus requesting an operation in another service. Therefore, that will impact in the response time and provoke some latency.
- Complexity: Imagine you need to implement a new feature which involves more than one service, it adds complexity to orchestrate every thing. Negotiate with the teams, prioritize the effort, define if all the changes needs to be implemented as a whole, or they can be incremental.
- DevOps requirements: Having dozens or services and hundreds of instances requires automation, doing everything in a manual way is imposible to handle. A distributed system like micro-services requires skilled orchestration, usually involving Kubernetes and other DevOps tools and processes. That means you will most probably need to employ or contract at least one DevOps engineer. That will add to costs and with demand soaring, DevOps specialists can be hard to recruit.
Just because it’s called serverless doesn’t mean it has “no server”, obviously there is a server, in charge of providing all the infrastructure and runtime to execute your application, but as a developer you don’t care about it. The developer only focus on the code that needs to be executed.
Serverless architecture – also known as FaaS, function as a service – consists of a set of functions, a function has a lifecycle – it is triggered, called, executed, runs and is then “killed” as soon as it has done its job. Serverless functions only run when they are needed, potentially saving significant resources.
- Cloud computing costs: You pay what you use… If your function is not called, there is no cost for it.
In monolith, the application or services are always Running, even if they are not used, therefore, the is a cost.
- No need to provision infrastructure: You usually choice a Cloud Provider which provides the platform to run your functions and create your serverless application. GCP, Azure, or AWS, but you can have some on-premise options like Fn-Project.
- Scalability: Within the confines of concurrency constraints, function instances are automatically added to or withdrawn in response to fluctuations in traffic.
- Triggerable: There are a lot of triggers in third party ecosystems, for example it’s very easy to run a Lambda function as a cron job in AWS, or execute a function when a new file is added to S3.
- Vendor lock-in: Applications that are entirely serverless or include serverless functions are relatively tightly tied to the provider platform. Even though, the structure of “functions” work in similar way in the different FaaS providers, each of them provides different features, and they are not exactly the same and interchangeable.
- Complexity: Despite the serverless provider is taking care of the cloud infrastructure reducing some complexity in the architecture, breaking down the app into independent functions means it still consists of multiple, smaller moving parts making it more complex than a monolithic approach.
- No state: in a serverless architecture, your team doesn’t really have a server, therefore you can not save your global state. You need to design your functions as a stateless unit, and not dependent on states on other steps.
You might get around this problem by adding an in-memory database to your infrastructures like Redis or Memcached to save the global state and each execution can get the global state from there. But it adds complexity to your solution.
- Short-term tasks only—Serverless architecture only works on short real-time processes. If it’s a long-term operation, you may need to plan for additional FaaS functionality. For example AWS Lambda functions has a limit of 15 minutes per execution.
All three approaches bring their strengths and weaknesses and choosing one or the other is always a trade-off. There is no best solution between them, it depends on your needs.
Monolith architecture is ideal for small applications due to its rapid development, ease of testing and debugging, and low cost. Ideal for applications or business where is not required to adapt to changes frequently in a rapid pace. It’s the tradition way to build application, and it still works on many use cases, if the environment is the correct.
Microservice architecture, is ideal for large applications with multiple modules or domain areas. Companies or applications with high volume of users, requiring to scaling dynamically on demand, and adapting to constant changes in the business (new features, new technologies) are a good use case for it. However, it introduces the complexity of orchestration, there are too many elements working together as a unit.
Serverless architecture, is ideal for startups trying to reduce costs. Ideal also for machine learning, business intelligence workloads, or quick tasks that can be reduced a couple of functions. It also requires some design in the orchestration or the steps because of the stateless nature. If your server needs to be running most of the time, maybe this is not the best choice, it’s suitable for workloads.
Now, it’s up to you to analyze and decide which one is better for your needs, think wisely and about using the wrong architecture just because it’s fashionable. If you take the best decision at the right moment, you will avoid a lot of headaches.