Nowadays, scalable architectures form a key part of any successful large-scale application. The complexities of managing such applications are exacerbated when the application runs on cloud infrastructure. This has led to the development of architecture design patterns. We'll focus in particular on the monolithic and the more recent microservices (similar but distinct to Service-Oriented) architecture patterns.

The monolithic architecture pattern is the most straightforward of the two patterns and it is how software is traditionally developed. In a monolithic system, functionally distinguishable aspects of a system are interwoven into the whole application. The diagram below illustrates the architecture of a monolithic application. Every component of the application is packaged together and communicates to one central database. For example, a single Java WAR file or executable file is created for the whole system.

Monolithic architecture

A benefit of monolithic applications is that communication between components is fast. This is because components communicate through method invocation or function calls. The communication pattern between monolithic and microservices architectures is a major differentiator between the two patterns.

Although for pure performance, the monolithic communication pattern is great, it makes application components tightly-coupled. This means different components of the system have to be written in the same programming language and changes to one component can prevent another part of the system from functioning correctly. An example of such an issue was given by Sudhir Tonse of Netflix at AWS re:Invent in 2014 where a missing semi-colon brought down [1].

In addition, because by design monolithic apps are packaged into one big application, it is not possible to scale one component independently of the others. Different components typically have different usage patterns and so you would ideally like to only scale the component that requires more resources.

The microservices architecture pattern decomposes the application into collaborating services. Each service has a clear scope of narrow and related functions. This is akin to the Single Responsibility Principle of Object-Oriented programming or the 'Do One Thing and Do it Well' Unix philosophy [2].

Services can be deployed independently and communicate with each other via language agnostic APIs. Examples of protocols to support communication are synchronous protocols such as HTTP/REST or asynchronous protocols such as Advanced Message Queuing Protocol (AMQP) - implemented by among others, the open-source project RabbitMQ.

Microservices architecture

The diagram above provides an example of a simple microservices architecture. Each service is responsible for its own data. This allows the services to be loosely coupled. The API gateway provides a single point of entry for all clients. The purpose of the API gateway is to route traffic to the appropriate service(s). The gateway is also responsible for authorizing requests that come in from clients.

There are many advantages to microservices. As already stated, each service can be deployed independently of other services. This allows for much shorter release cycles as the team managing one service no longer have to wait for all other services in order to make a release. Due to this loose coupling nature, there is increased fault isolation as an issue in one service does not directly impact another. Finally, 'best tool for the job' means that microservices can use whatever technology stack is deemed appropriate for the task.

However, there are also some disadvantages. Microservices add additional complexity to a distributed system. Communication between services now becomes an issue. In addition, resource usage can be higher for microservices. For example, if you have multiple Java-based microservices, each running its own JVM, this would consume more memory than if you had just one monolithic application running.

Conway's law - organisations which design systems ... are constrained to produce designs which are copies of the communication structures of these organisations

Microservices allow an organisation to meet the demands of the business whilst allowing development teams to make faster and simpler deployments. However, for microservices to fully function at their best, the organisation itself has to be structured with team autonomy and alignment in mind [3].

  1. Sudhir Tonse of Netflix at AWS re:Invent ↩︎

  2. Basics of the Unix Philosophy ↩︎

  3. Spotify and its engineering team structure ↩︎