Back to blog
System design patterns overview showing layered architecture
System Design

3 System Design Patterns Every Engineer Should Know

May 16, 2025 8 min read Avinash Tyagi
system design design patterns layered architecture pub/sub cqrs microservices software architecture system design interview distributed systems event-driven architecture

If you're preparing for system design interviews or building production systems, understanding core design patterns is essential. These patterns solve recurring problems in distributed systems and help you make better architectural decisions. At Levelop, we break down complex engineering concepts into practical, actionable knowledge.

In this post, we'll explore three fundamental patterns that every software engineer should know: Layered Architecture, Pub/Sub Messaging, and CQRS.

Layered Architecture

Layered architecture is the most common pattern for organizing backend systems. It separates concerns into distinct tiers, each with a clear responsibility.

Diagram showing layered architecture: Client, API Gateway, Service Layer, and Data Layer
System design layered architecture overview

Each layer only communicates with the layer directly below it. This creates a clean separation of concerns and makes the system easier to test and maintain.

How Layered Architecture Works

The typical layers are: Client (UI), API Gateway, Service (business logic), and Data (persistence). This is the same pattern used by frameworks like Spring Boot, NestJS, and Django.

layered-example.tstypescript
// Express.js layered structure
const app = express();

// Route -> Controller -> Service -> Repository
app.get("/users/:id", userController.getById);

class UserService {
  constructor(private repo: UserRepository) {}
  async getById(id: string) {
    return this.repo.findById(id);
  }
}

Pub/Sub Messaging Pattern

The Publish/Subscribe pattern decouples services by introducing a message broker between producers and consumers. Services communicate through topics rather than direct API calls.

Pub/Sub messaging pattern diagram showing publishers, message broker with topics, and subscribers
Pub/Sub messaging pattern with topics and subscribers

Why Pub/Sub Matters for Microservices

In a microservices architecture, direct service-to-service calls create tight coupling. Pub/Sub solves this by routing messages through a broker like Apache Kafka, RabbitMQ, or AWS SNS/SQS.

pubsub-example.tstypescript
// Publishing an event
await messageBroker.publish("order.created", {
  orderId: order.id,
  userId: order.userId,
  items: order.items,
  total: order.total,
  timestamp: new Date().toISOString()
});

// Subscribing to events
messageBroker.subscribe("order.created", async (event) => {
  await emailService.sendOrderConfirmation(event.userId, event.orderId);
  await analyticsService.trackPurchase(event);
});

CQRS — Command Query Responsibility Segregation

CQRS separates read and write operations into different models. The write side handles commands through a domain model, while the read side uses optimized projections for queries.

CQRS pattern diagram showing write side with command handler and read side with query handler
CQRS pattern separating command and query responsibilities

When CQRS Makes Sense

This pattern shines when read and write workloads have very different scaling requirements. Your write database (PostgreSQL) can be normalized for consistency, while read replicas (Elasticsearch, Redis) are denormalized for fast queries. CQRS is often paired with Event Sourcing for a complete audit trail.

cqrs-example.tstypescript
// Command side
class CreateOrderCommand {
  async execute(dto: CreateOrderDTO) {
    const order = Order.create(dto);
    await this.writeRepo.save(order);
    await this.eventBus.publish(new OrderCreatedEvent(order));
  }
}

// Query side
class OrderQueryHandler {
  async getOrderSummary(userId: string) {
    return this.readRepo.findByUserId(userId);
  }
}

When to Use Each Pattern

Choose Layered Architecture for straightforward CRUD applications. Use Pub/Sub when you need event-driven communication and loose coupling. Adopt CQRS when read and write operations have vastly different performance requirements.

In practice, production systems often combine these patterns. You might use layered architecture within each microservice, Pub/Sub for inter-service communication, and CQRS for your highest-traffic read paths.

Key Takeaways

Understanding these patterns gives you a vocabulary for discussing system design and a toolkit for solving distributed systems problems. Start simple, add complexity only when the data access pattern demands it. Stay tuned for more deep dives on the Levelop blog.

Frequently Asked Questions

What is the easiest system design pattern to start with?

Layered Architecture is the simplest and most widely used. Most web frameworks like Express, Django, and Spring Boot use it by default. Start here and add more advanced patterns as your system grows.

Can I use multiple design patterns in one system?

Yes. Production systems almost always combine patterns. A common setup is layered architecture within each service, Pub/Sub for inter-service messaging, and CQRS for high-read endpoints like dashboards or search.

What is the difference between Pub/Sub and a message queue?

A message queue delivers each message to exactly one consumer (point-to-point), while Pub/Sub broadcasts messages to all subscribers of a topic. Use queues for task distribution and Pub/Sub for event notification across multiple services.

When should I avoid CQRS?

Avoid CQRS for simple CRUD applications where reads and writes have similar load. The extra complexity of maintaining separate read and write models is not worth it unless you have clear scaling asymmetry or need an audit trail via Event Sourcing.

How do these patterns help in system design interviews?

Interviewers expect you to pick the right pattern for the constraints given. Mentioning trade-offs like eventual consistency in Pub/Sub or operational overhead in CQRS shows depth beyond just knowing pattern names.

Keep reading

System Design

Modular Monolith: Why Teams Are Leaving Microservices

42% of organizations are consolidating microservices back into modular monoliths. Here is why the economics shifted, what a modular monolith looks like in practice, and how to decide which architecture fits your team.

Read article
System Design

Vanishing Links: I Designed a URL Shortener and the Expiry Logic Was the Hard Part

A URL shortener is a lifecycle problem, not a mapping one. Full system design with expiry logic, three services, and the data layer that ties it all together.

Read article
Interview Prep

Google Coding Interview 2026: What Changed

Google added an AI-assisted code comprehension round, started scoring AI fluency, and made the Googleyness round technical. Here's what changed, what stayed, and how to prepare.

Read article