Modern-Day Architecture Design Patterns for Software Professionals

Source —

Many modern-day applications need to be built at an enterprise scale, sometimes even at an internet scale. Each application needs to meet scalability, availability, security, reliability, and resiliency demands.

In this article, I’m going to talk about some design patterns that can help you achieve the above-mentioned abilities with ease. I’ll be talking about each pattern, how to use that pattern in a cloud-native environment, and when to use it and when not.

Some of these patterns aren’t so new but are very useful in the current internet-scale cloud world.

Here’s the list of patterns I’ll be discussing in this article:

1. Circuit Breaker
2. Command and Query Responsibility Segregation (CQRS)
3. Event Sourcing
4. Sidecar
5. Backend-for-Frontend
6. Strangler

So let’s get started.

Circuit Breaker

Distributed systems should be designed by taking failures into consideration. These days the world has adopted microservices, and these services are mostly dependent on other remote services. These remote services could fail to respond in time due to various reasons like network, application load, etc. In most cases, implementing retries should be able to solve the issues.

But sometimes there may be major issues like service degradation or complete service failure in and of itself. It’s pointless to keep retrying in such cases. That’s where the Circuit Breaker pattern can be useful.

Circuit Breaker. Image by the author.

The above diagram showcases the implementation of the Circuit Breaker pattern, where when Service 1 understands there are continuous failures/ timeouts when Service 2 is called, instead of retrying, Service 1 trips the calls to Service 2 and returns the fallback response.

There are popular open-source libraries, like Netflix’s Hystrix or Reselience4J, that can be used to implement this pattern very easily.

If you’re using API gateways or sidecar proxies like Envoy, then this can be achieved at the proxy level itself.

Note: It’s very important that there’s sufficient logging and alerting implemented when the circuit is open in order to keep track of requests received during this time and that the operations team is aware of this.

You can also implement a circuit breaker with the half circuit open to continue to service clients with degraded service.

When to use this pattern

  • When a service is dependent on another remote service, and it’s likely to fail in some scenarios
  • When a service has a very high dependency (for example, master data services)

When not to use this pattern

  • When you’re dealing with local dependencies — a Circuit Breaker might create overhead

Command and Query Responsibility Segregation (CQRS)

CQRS is a very useful pattern for modern-day applications that involve using data stores. It’s based on a principle of segregating the read (query) and write/updates (command) operations in a data store.

Say you’re building an application that requires you to store data in a database like MySQL/PostgreSQL etc. As everyone knows, when writing data into the data store, an operation needs to take several steps — like validation, model, and persistence — and hence typical write/update operations take longer than the simple read operations.

When you’re using a single data store to perform both read and write operations at the same time and at scale, then you may start seeing performance issues.

In such cases, the CQRS pattern can be useful. CQRS pattern suggests using different data models for the read and writes operations. Some variations also suggest using separate data stores for these models.

CQRS. Image by the author.

Note: Most PaaS databases these days provide the ability to create read replicas (Google Cloud SQL, Azure SQL DB, Amazon RDS, etc.) of the data stores which help to achieve replications of data much easier.

A lot of enterprise databases also provide this capability if you’re dealing with on-prem databases.

Note: These days some people also prefer to implement read replicas as fast and performant NoSQL databases, like MongoDB and Elasticsearch.

When to use this pattern

  • When you’re looking at scaling an application expecting a huge number of reads and writes
  • When you want to tune the performance of the read and write operations separately
  • When your read operations are OK with near real-time or eventually consistent nature

When not to use this pattern

  • When you’re building a regular CRUD application that doesn’t expect a huge number of reads and writes at a time

Event Sourcing

Event Sourcing is an interesting design pattern where a sequence of domain events is stored as a journal, and an aggregated view of the journal gives the current state of the application.

This pattern is typically used for systems that can’t afford data store locks and that need to maintain the audit and history of the events — for instance, applications like hotel/conference/seat bookings.

Event Sourcing. Image by the author.

Considering a hotel room reservation system in which the users are expected to book or cancel reservations. Here you need to store the bookings and cancellations as a series of events. Before every booking, an aggregated view shows the available rooms by looking at the events journals.

Note: Most cloud-service providers support messaging services like Google Pub/Sub, Azure Service Bus, AWS SQS, etc. These services, in combination with strong consistent data stores, can be used to implement this pattern.

When to use this pattern

  • When regular CRUD operations aren’t good enough to meet the demands
  • Typically suitable for seat-reservation systems — like bus, train, conferences, movie halls, etc. — or e-commerce systems that consist of events like cart operations, payments, etc.
  • When there’s a requirement of strong auditing and events replay to create a current and past state of applications

When not to use this pattern

  • When regular CRUD operations are good enough to meet the user demands.


The Sidecar pattern became popular with the rise of microservices. In this pattern, you deploy a component of an application into a separate process or a container. This helps to achieve abstraction and encapsulation.

Envoy Proxy is one of the most popular sidecar proxies and is widely used. It helps you keep the core functionality of the application separate, using the sidecar to isolate common features like networking, observability, and security.

Sidecar. Image by the author.

Such type of sidecars can help abstract L4/L7 type of communication. Sidecars like Envoy Proxies even help achieve higher security by implementing mutual TLS.

You can use this in a combination with a service mesh to achieve better communication and security among the various microservices. You can read more about it in my previous article.

When to use this pattern

  • When you’re dealing with multiple and heterogenous microservices in product scope
  • When you’re dealing with legacy applications that typically fall short of coping up with new-age communication and security challenges

When not to use this pattern

  • When you’re dealing with a limited number of services that need to communicate with each other
  • Small applications where sidecar deployments might not be economical or operations-friendly

Backend-for-Frontend (BFF)

In a typical product development cycle, back-end engineers are responsible for creating services that interact with data stores, and front-end engineers take care of building user interfaces. These days applications need to be built keeping mobile as well as desktop usage in mind.

Even though the gap between mobile and desktop devices in terms of hardware is getting closer, connectivity and usage continue to be challenging for mobile devices.

In such scenarios, BFF patterns become quite handy. Here, you’re expected to build/customize back-end services for the specific front end.

Backend-for-Frontend. Image by the author.

To optimize the performance of mobile clients, you may want to build a separate back-end service that responds with lightweight and paginated responses.

You may also want to use this pattern for the aggregation of various services in order to reduce the chatty communication.

Note: These days if you’re using an API gateway, the BFF pattern can be easily implemented in the gateway itself, and you don’t need to maintain separate services.

When to use this pattern

  • When you want to deliver a product/service for different clients, like desktop and mobile clients
  • When you want to optimize the response for a particular type of client
  • When you want to reduce chatty communication between mobile clients and various services

When not to use this pattern

  • When the application users are expected to use a single user interface
  • When mobile and desktop applications are expected to showcase similar information and provide similar functionality


If you’re working in an organization that’s on a journey toward application modernization, then the Strangler design pattern is a must. The Strangler design pattern advocates creating a facade on top of your legacy and a new application, providing an abstracted view to the consumers.

Strangler. Image by the author.

This pattern decouples the consumers from the migration activities.

Note: In a typical IT organization, if you’re migrating from one ERP to another, this type of pattern is extremely useful. If you’re using an API gateway, it becomes even easier to implement this in the gateway proxy itself.

You need to decide if you want to keep the facade at the end of migration or remove it.

When to use this pattern

  • When you’re migrating or modernizing a complex, highly dependent application like ERP migrations

When not to use this pattern

  • When the migration is simple and direct replacement is a better option

Better Programming

Advice for programmers.




Thanks to Zack Shapiro.