Skip to main content
Architecture10 min readMarch 3, 2026

Software Architecture Patterns Every Architect Should Know

Software architecture patterns are the vocabulary of system design. This guide breaks down layered, microservices, event-driven, hexagonal, and CQRS — with honest guidance on when to use each.

James Ross Jr.

James Ross Jr.

Strategic Systems Architect & Enterprise Software Developer

Pattern Knowledge Is Only Useful With Judgment

Architecture patterns are frequently taught as if knowing them is the goal. It isn't. The goal is knowing when to apply each one — and equally important, when not to. Every pattern in this list solves a real problem. Every pattern in this list has also been misapplied in production systems I've had to untangle.

What follows is a practical breakdown of the patterns I reach for most often, including the context where they make sense and the warning signs that you're applying them incorrectly.


Layered Architecture

What It Is

The layered pattern (also called N-tier) divides a system into horizontal layers where each layer only communicates with the layer immediately below it. The classic breakdown: Presentation → Application → Domain → Infrastructure.

Why It Works

Layered architecture provides clear separation of concerns. Your business logic doesn't know anything about your database. Your API controllers don't contain business rules. Each layer has a defined responsibility and a defined boundary.

For most applications, this is the right starting point. It's well-understood, straightforward to implement, and easy to test. Most modern frameworks enforce some version of it.

When It Falls Apart

Layered architectures have a tendency to develop "fat" middle layers, particularly the application/service layer, which becomes a dumping ground for business logic that doesn't have an obvious home. They can also encourage "lasagna code" — so many thin, indirection-heavy layers that simple operations require traversing the entire stack.

Use it when: You're building a standard web application with CRUD operations and moderate business complexity. This covers a lot of real-world software.

Reconsider when: Your domain logic is genuinely complex, your system needs to support multiple interfaces (API, event-driven, CLI), or you're building something that will evolve significantly over time.


Microservices Architecture

What It Is

Microservices decomposes a system into independently deployable services, each owning its own data and being responsible for a specific business capability. Services communicate via APIs or messaging.

Why It Works

Done well, microservices enable independent scaling, independent deployment, and organizational alignment — each team owns one or a few services and can ship without coordinating with every other team.

When It Falls Apart

Microservices are one of the most misapplied patterns in modern software. The problems I see most often:

  • Distributed monolith: Services are fine-grained at the technical level but tightly coupled at the business level. Deploying Service A requires deploying Service B and C simultaneously. You've taken on all the costs of microservices with none of the independence.
  • Wrong service boundaries: Services carved by technical function (UserService, DatabaseService) instead of business capability. These create constant cross-service coordination for real features.
  • Premature adoption: A team of 5 engineers building a startup adopts microservices because Netflix uses them. Netflix has thousands of engineers and multiple years of organic growth that led to that architecture organically.

Use it when: You have distinct, bounded business domains, teams large enough to own services independently, and operational maturity to handle distributed systems complexity. The system is already large and growing.

Avoid it when: You're early-stage, your team is small, or your domain boundaries aren't yet clear. Start with a well-structured monolith.


Event-Driven Architecture

What It Is

Services communicate by publishing and subscribing to events rather than calling each other directly. A service emits an event when something noteworthy happens; other services react to those events asynchronously.

Why It Works

Event-driven systems achieve loose coupling. The publisher doesn't know or care who's listening. Adding a new downstream consumer requires no changes to the upstream service. This is powerful for systems that need to evolve independently and scale different components at different rates.

When It Falls Apart

Event-driven systems introduce significant complexity: eventual consistency, event ordering, duplicate delivery, schema evolution of event contracts, and distributed tracing across async flows. Debugging a production issue that spans five event consumers is genuinely hard.

Use it when: You have genuinely asynchronous workflows, you need to decouple producers from consumers, or you need to support fan-out (one event, multiple consumers).

Avoid it when: You need immediate consistency, the workflow is inherently synchronous, or your team isn't equipped for the operational complexity.


Hexagonal Architecture (Ports and Adapters)

What It Is

Hexagonal architecture puts your domain logic at the center, surrounded by "ports" (interfaces your domain exposes or depends on) and "adapters" (implementations that connect your domain to the outside world: databases, APIs, UIs, message queues).

Why It Works

Your domain logic becomes truly independent of infrastructure. You can swap out the database without touching business rules. You can test business logic in complete isolation from the network. The same domain core can serve a REST API, a GraphQL API, and a message consumer simultaneously.

When It Falls Apart

The pattern adds boilerplate. For every external dependency, you're writing an interface plus an implementation. For simple CRUD systems, this overhead rarely pays off. It's also commonly misunderstood — I've seen teams create "adapters" that are just thin wrappers around ORMs, with the actual data mapping logic bleeding into the domain layer anyway.

Use it when: Your domain logic is complex and you want to test it independently. When you're building something long-lived that will outlast your current infrastructure choices.

Avoid it when: Your application is primarily data access with thin business logic. The pattern creates overhead that won't pay off.


CQRS (Command Query Responsibility Segregation)

What It Is

CQRS separates the read path from the write path. Commands change state. Queries read state. These can be handled by different models, different services, even different databases.

Why It Works

Complex domains often have asymmetric read and write requirements. You might write data through a rich domain model with complex validation and business rules, but read it through flat, denormalized projections optimized for display. Combining these in a single model creates constant tension. CQRS eliminates that tension by making the separation explicit.

When It Falls Apart

CQRS significantly increases architectural complexity. You now have two models to maintain, potentially two data stores to keep in sync, and eventual consistency between them. For most applications, this complexity is not justified.

Use it when: You have a domain with complex business rules on the write side and diverse, performance-sensitive read requirements. Often paired with Event Sourcing.

Avoid it when: Your read and write requirements are symmetric, your domain is simple, or your team isn't equipped to manage the operational overhead.


Choosing the Right Pattern

No architecture pattern is universally correct. The decision depends on:

  • Team size and structure: Small teams can't afford the overhead of microservices or CQRS. Large, federated teams can't coordinate around a monolith.
  • Domain complexity: Complex domains justify sophisticated patterns. Simple domains don't.
  • Operational maturity: Distributed systems require sophisticated observability, deployment pipelines, and incident response. If you don't have these, adopt simpler patterns until you do.
  • Stage of the business: Early-stage products need to move fast and pivot. Heavyweight patterns add friction. Mature products with known domains can afford to invest in structural clarity.

The pattern I reach for most often for new systems is a well-structured modular monolith with hexagonal architecture inside. It provides the separation of concerns and testability of the more complex patterns without the operational overhead. If the system outgrows it, the modular boundaries make it straightforward to extract services.

Start simple. Add complexity when the problem demands it, not when the pattern looks interesting.


If you're evaluating which architectural pattern fits your current system — or trying to untangle one that's grown beyond its pattern — let's talk.


Keep Reading