DEV Community

Arash Zand
Arash Zand

Posted on • Originally published at Medium

Common Patterns in Software Architecture

Software architecture defines the structure and behavior of systems, ensuring they meet requirements like scalability, maintainability, and performance. This guide covers 15 widely used patterns with explanations, trade-offs, and tools across Node.js/TypeScript, C#/.NET, Go, and Java.


1. Layered Architecture

Organizes a system into horizontal layers — presentation, business logic, data access — each depending only on the layer below. Promotes separation of concerns and makes layers independently testable.

Trade-offs: Inter-layer communication adds latency. Over-layering creates unnecessary complexity.

Tools: ASP.NET Core + EF Core + Blazor (C#/.NET) · Express.js + TypeORM + React (Node) · Spring Boot + JPA + Thymeleaf (Java) · Gin + GORM (Go)


2. Microservices Architecture

Divides a system into small, independently deployable services, each owning its data and business capability. Services communicate via APIs or messaging.

Trade-offs: Independent scaling and deployment, but significant distributed systems complexity — network latency, data consistency, service discovery, and DevOps overhead.

Tools: NestJS + MassTransit + Kafka (C#/.NET) · Spring Cloud + Hibernate + Kafka (Java) · Go Micro + gRPC + NATS (Go) · Docker + Kubernetes + Prometheus


3. Event-Driven Architecture (EDA)

Components communicate by producing and consuming events via a message broker. Producers and consumers are fully decoupled — each scales independently.

Trade-offs: High responsiveness and scalability, but eventual consistency and complex error handling (dead-letter queues, retries, schema evolution).

Tools: MassTransit + Azure Event Hubs (C#/.NET) · KafkaJS + EventStoreDB (Node) · Axon + Apache Kafka (Java) · Watermill + NATS (Go)

Example pattern — event sourcing:
Instead of updating a record directly, store events like OrderPlaced, ItemShipped. Replay events to reconstruct current state. Enables auditability and time-travel debugging.


4. Service-Oriented Architecture (SOA)

Organizes a system as a collection of independent, reusable services that communicate over a network via SOAP or REST. An ESB or API Gateway handles orchestration.

Trade-offs: Reusability and interoperability across heterogeneous systems. More complex governance than microservices — service contracts, versioning, security.

Tools: MuleSoft ESB · WSO2 · Kong API Gateway · Spring Boot + Eureka (Java) · ASP.NET Core + Steeltoe (C#/.NET)


5. Monolithic Architecture

All functionality in a single codebase, deployed as one unit. Simple to develop, test, and deploy. Suitable for small teams or early-stage products.

Trade-offs: Fast to start, but tight coupling makes scaling and independent deployment hard as the system grows. Consider a modular monolith — loosely coupled internal modules — as a middle ground before microservices.

Tools: ASP.NET Core + EF Core + Razor Pages (C#/.NET) · Express.js + Sequelize + Next.js (Node) · Spring Boot + JPA (Java) · Go standard library + GORM


6. Client-Server Architecture

Clients request services; servers provide them. The foundation of every networked application.

Key decisions:

  • Stateless vs stateful — stateless servers scale horizontally (REST APIs); stateful servers maintain connections (WebSockets)
  • Caching — CDNs cache static assets; Redis caches session data
  • Load balancing — NGINX/AWS ELB distribute traffic across server instances

Tools: ASP.NET Core + SignalR (C#/.NET) · Express.js + Socket.IO (Node) · Spring Boot + WebSocket (Java) · Go net/http + Gorilla WebSocket


7. Model-View-Controller (MVC)

Separates an application into Model (data/logic), View (UI), and Controller (coordination). Promotes testability — each component can be tested in isolation.

Server-side MVC: ASP.NET Core MVC, Spring MVC, Rails — renders HTML on the server.
Client-side MVC: Angular, React — updates the DOM in the browser.

When it gets complex: Controllers become bloated in large apps. MVVM or Clean Architecture addresses this.

Tools: ASP.NET Core MVC + EF Core (C#/.NET) · Angular/React + Express (Node) · Spring MVC + JPA (Java) · Revel/Beego + GORM (Go)


8. Component-Based Architecture

Structures a system as reusable, self-contained UI components that interact through well-defined props and events. Central to React, Angular, Blazor, and Vue.

Trade-offs: Fast development, parallel team work, easy testing of isolated components. Shared state management adds complexity — Redux (React), NgRx (Angular), Fluxor (Blazor).

Tools: React + Redux + Storybook (Node) · Blazor + Fluxor (C#/.NET) · Vaadin/GWT (Java) · Templ (Go)


9. Domain-Driven Design (DDD)

Aligns software design with the business domain. Key building blocks:

Concept Description
Entity Object with a unique identity (e.g., Customer)
Value Object Describes an attribute, no identity (e.g., Address)
Aggregate Cluster of entities with a root (e.g., Order + OrderItems)
Repository Abstracts data access for aggregates
Bounded Context Clear boundary for a domain model
Domain Event Signals a meaningful state change (e.g., OrderPlaced)

Trade-offs: Ideal for complex domains (finance, healthcare, logistics). Significant upfront investment in domain modeling. Overkill for simple CRUD apps.

Tools: MediatR + EF Core + Ardalis.Specification (C#/.NET) · NestJS + TypeORM (Node) · Axon Framework + JPA (Java) · Go structs + SQLx + Watermill


10. API Gateway Pattern

A single entry point for all client requests in a distributed system. Routes requests to services and handles cross-cutting concerns centrally.

Responsibilities: Authentication · Rate limiting · Request/response transformation · Response aggregation · Caching · Load balancing

Trade-offs: Simplifies client interactions but becomes a single point of failure — cluster it. Adds a network hop — minimize processing overhead.

Tools: Ocelot (C#/.NET) · Spring Cloud Gateway / Zuul (Java) · KrakenD / Traefik (Go) · Kong · Amazon API Gateway · NGINX · Envoy


11. Circuit Breaker Pattern

Prevents cascading failures by halting requests to a failing service and returning fallbacks until the service recovers.

Three states:

  • Closed — normal operation, requests flow through
  • Open — failure threshold exceeded, requests blocked, fallback returned
  • Half-Open — timeout elapsed, one test request allowed; closes on success, reopens on failure

Example fallbacks: Return cached data · Redirect to alternative service · Return a degraded response

Tools: Polly (C#/.NET) · Resilience4j / Hystrix (Java) · Opossum (Node) · GoBreaker (Go) · Envoy (built-in)


12. Saga Pattern

Manages distributed transactions across microservices without two-phase commit. Breaks a transaction into local steps, each emitting an event to trigger the next. Failed steps trigger compensating transactions to undo previous work.

Two coordination styles:

  • Choreography — services react to events, no central coordinator. Simple but hard to trace.
  • Orchestration — a Saga Coordinator sends commands and tracks state. Easier to reason about, more coupling.

Example — order placement:

  1. Order Service: create order → emit OrderCreated
  2. Inventory Service: reserve items → emit ItemsReserved
  3. Payment Service: charge card → emit PaymentProcessed
  4. If payment fails → compensate: release items, cancel order

Tools: MassTransit + NServiceBus (C#/.NET) · Axon Framework (Java) · Eventuate Tram (Node) · Watermill (Go) · AWS Step Functions


13. CQRS (Command Query Responsibility Segregation)

Separates write operations (Commands) from read operations (Queries) into distinct models — each optimized for its workload.

Command side: POST /orders → validates → persists to write DB → emits event
Query side:   GET /orders  → reads from pre-built read model (Redis, MongoDB, Elasticsearch)
Enter fullscreen mode Exit fullscreen mode

Trade-offs: Independent scaling of reads and writes. Paired with Event Sourcing, the read model rebuilds from events. Eventual consistency — read model may lag slightly behind write model. Significant complexity overhead — not worth it for simple CRUD.

Tools: MediatR + EventStoreDB + Marten (C#/.NET) · Axon + Elasticsearch (Java) · NestJS + Redis (Node) · Watermill + MongoDB (Go)


14. Publish-Subscribe Pattern

Publishers send messages to topics on a broker; subscribers consume from those topics. Producers and consumers have no direct knowledge of each other.

Ideal for: Real-time feeds · Stock price updates · IoT sensor data · Notification systems · Log aggregation

Trade-offs: Dynamic, decoupled scaling. Requires schema management (Confluent Schema Registry), dead-letter queues for failures, and monitoring for queue health.

Tools: MassTransit + Azure Service Bus (C#/.NET) · Spring Cloud Stream + Kafka (Java) · KafkaJS + Redis Pub/Sub (Node) · Watermill + NATS (Go)


15. API-First Approach

Design and publish the API specification before writing implementation code. The spec (OpenAPI/Swagger or GraphQL schema) becomes the contract between teams.

Benefits:

  • Frontend and backend teams work in parallel against the same contract
  • Mock servers simulate responses during frontend development
  • Third-party integrations have clear, stable contracts
  • Consistent error codes, schemas, and versioning from day one

Versioning strategy: Use URI versioning (/v1/payments, /v2/payments) to avoid breaking consumers.

Tools: Swashbuckle + HotChocolate (C#/.NET) · Swagger-jsdoc + Apollo Server (Node) · OpenAPI Generator + Spring GraphQL (Java) · oapi-codegen + gqlgen (Go) · Postman · SwaggerHub · Stoplight


Choosing the Right Pattern

Need Consider
Simple app, small team Monolithic or Layered
Independent team scaling Microservices
Real-time, async workflows Event-Driven + Pub/Sub
Complex business domain DDD
High read/write disparity CQRS
Distributed transactions Saga
Resilience in dependencies Circuit Breaker
Multiple client types API Gateway
Parallel frontend/backend API-First

No pattern is universally correct. The best architecture is the simplest one that satisfies your actual constraints.


Originally published on Medium.

Top comments (0)