From Monolithic Architecture to Microservices
- Part 2 -
“Honestly, microservices seem like a terrible idea, except for all the good stuff” Sam Newman, 2019; Monolith to Microservices.
In our previous article (From Monolithic Architecture to Microservices Part 1) we talked about the evolution of some important paradigms in software development, especially those that led to important modernizations in IT architectures. These modernizations require a significant effort in resources, both human and monetary, and because of this it’s important to have a clear and objective strategy for a successful implementation to take place.
In Part 1, we discussed some important ideas worth considering, before any technical action that we have to implement in order to adopt microservices architecture:
IT systems support business. What’s the business vision for short, medium and long term? There are companies whose systems are a technological and strategic asset (e.g. SaaS). These are usually the first candidates to modernize, but upgrading them is still an operational challenge. There are many other systems that are backoffice (e.g. intranets, back office systems) for internal users and they are usually left out or left for later when it comes to update priority.
Current technological architecture.
We must create (if it does not already exist) or update the technical documentation of our systems, their architectures, infrastructure, technologies, databases, APIs, etc. That is, having an ‘’X-ray’’ of our IT architecture that allows us to run an effective diagnosis of where we stand.
Current operative capacity.
This topic is about analyzing installed capacity; both technical (how our current team is made up) and support (e.g. help desk, 1st/2nd level). This allows us to determine whether if executing and supporting an eventual modernization we’ll have to use our current resources or whether we’ll have to outsource an effort or project. If we have to outsource, we must take the selection of suppliers with experience in modernizing solutions into account.
Current pain points and challenges.
How are our service level indicators performing? What is the current technical debt in our systems? Do we have availability problems (downtimes) in critical systems? What’s the “lead time” to meet business requests (improvements, maintenance)? These factors must be recorded, in terms of indicators (KPIs) and money.
Everything costs something: current operations, technical debt and changes. What’s the budget to modernize solutions? What’s the lifetime of current solutions that need to be modernized? What would the return on investment be?
From the previous points we just mentioned, we have to conclude whether it’s necessary to modernize certain (or all) applications. If we already use certain platforms (eg .NET, Java, LAMP, MEAN) what technological stack should I implement with? Which components or systems should I migrate first? What’s the data architecture I should consider (fully isolated, shared, hybrid)? Having a diagnosis, we must now propose a strategy.
Figure 1. From diagnosis to technology strategy
Technology strategy will define the architectures, technologies and patterns required to enable successful modernization.
We have to determine which projects or solutions we’ll be migrating to microservices. At this point we can make decisions based on certain rules that are based on current problems, business vision and sense of urgency.
We suggest listing the projects that are NOT going to be migrated. Having a microservices architecture should not be the main goal of modernization. One must be very objective about the desired problems to solve and leave out projects such as:
– Backoffice solutions that do not require continuous maintenance or that have an obsolescence period of more than 2-3 years
-Modular and stable solutions that do not have scalability and availability problems; especially those that are already deployed in elastic infrastructure (public or private clouds)
-Solutions that have scalability problems, but that can be solved with either vertical or horizontal scaling, where this solution approach is the best in the medium term
-Stable solutions with a well-defined development process, and whose “lead time” to implement changes is neatly aligned with business KPIs. A stable, layered solution with a robust development process for its maintenance can have a long life
-Solutions that do not project much growth in use for the medium term
-Box solutions that are installed and managed by end customers
-Relatively new solutions for which we still do not have relevant problems or indicators
With that being said, these are the recommended solutions for successful migration:
-Core business systems (products/services/SaaS) that are going to experience exponential growth in the short term and that have scalability and maintainability issues (time-to-market).
-Systems with monolithic legacy technologies that have very long or complicated maintenance times and are likely to grow in the medium term.
-New products with growth potential that depend on rapid scaling of the number of developers
-Solutions already segmented by APIs (mobile/frontend/backend) whose low coupling allows the data of each service to be segmented at the domain level.
Once the portfolio of projects to be migrated has been defined, the next recommended step is to define the technological architecture. That is, defining both the technological stack with which the new solutions will be implemented as well as the infrastructure where it will be hosted. It is common to aim to continue using certain knowledge of previous technologies. For example, if our stack is mostly Microsoft (e.g. applications with NET Framework, or if we already use Azure), it would be advisable to start developing microservices in .NET Core.
Figure 2. It’s important to define the technology stack
However, one of the advantages of a microservices-oriented architecture is that you are not limited to a certain development language. There are implementations where there is a mix of languages, where services in .NET Core, Node.js, Python, Go and Java coexist. This allows access to a broader market of programmers and with fewer restrictions on specific skills. Additionally, it’s highly recommended that these platforms be supported by container technologies and/or that they can be executed in serverless services.
Figure 3. What technologies should I modernize my applications with?
Regarding the issue of infrastructure, it’s a strategic decision that the organization must make. Some clients even have a hybrid infrastructure, using Azure, GCP, AWS and/or On-Premise.
One of the core considerations in migrating to microservices is data architecture. In a purist sense, a microservice should be totally independent and decoupled. This includes any datasource dependencies with other microservices or components, so you should have your own domain and datasource.
Figure 4. Multilayer monolithic applications
However, achieving this paradigm is complex, even in newly created solutions. The design of monolithic systems contemplates very few data sources, and even on many occasions, several systems point to a single database of stratospheric dimensions. This design clearly allows two important benefits (which disappear when implementing pure microservices): high transactionality (ACID) and the power of the SQL language throughout the database. In non-purist models of microservices, it is still permissible to have several services pointing to the same data sources. However, this implementation does not allow complete independence in deployment, one of the main advantages of microservices.
Figure 5. Microservices pointing to a Database
One of the most used patterns to model data architecture in microservices is Bounded Context, from Domain-based Design (DDD), and it consists of logically dividing our data model into data domain contexts, in order to segment our overall scheme.
Figure 6. Decomposition by domain
Other important patterns when implementing a data architecture in microservices are:
A facade-type pattern that allows the aggregation of multiple services, from different domains and even origins. Existing tools are commonly used on both cloud and on-premise platforms.
CQRS (Command and Query Responsibility Segregation)
A pattern also known as “materialized view” which allows for the preparation of denormalized data in a single table. This allows you to aggregate a query result from multiple microservices.
“Cold data ” and central BDs
This is proposed for complex reports and queries that do not require data in real time. The approach is to export transactional data from microservices to databases, specifically for reporting.
Of course data independence per microservice is the holy grail in a microservice-oriented design. To achieve a certain level of transactionality (see the CAP theorem) between data in a microservices architecture, it’s common to implement an asynchronous communication system, such as queues or messages (AMQP or gRPC). Some open source products such as RabbitMQ, Apache Kafka or cloud-based bus systems can be found in reference architectures and their implementations.
Figure 7. Implementation of asynchronous communication in microservices
The design of the data architecture is a key factor when migrating to a microservices architecture. We must place special emphasis on the independence of both the development and deployment of each service.
Once we have defined our technological architecture, we can then design the implementation strategy which takes us to the final part of the modernization roadmap. This strategy will define some elements which we’ll be able to execute migration projects with while not affecting business operations. They include: Modernization Patterns, Processes and Government, Monitoring, among others.
We’ll be discussing this implementation strategy in the third and last part of our series, ‘’Roadmap: From Monolithic Architecture to Microservices.’’
To be continued…
This article was originally posted in spanish on Mijail’s LinkedIn.