Software Engineering

Architectural Patterns

Цели урока

  • Understand the layer separation principle in Layered Architecture
  • Explain the difference between Port and Adapter in Hexagonal Architecture
  • Evaluate the microservices vs monolith trade-off for a specific team
  • Design event-driven interactions between services
  • Choose an architectural pattern based on real requirements

Предварительные знания

  • Requirements and Specifications

Amazon, 2001: developers spend 70% of their time coordinating, a multi-million-line monolith has become unmanageable. Bezos mandates: "All teams through APIs. Violators fired." That single decree births AWS and the microservices revolution. Netflix later migrates to 700+ microservices, walks straight into distributed-systems hell, and invents Chaos Engineering to survive. Meanwhile, Shopify clears USD 150B on a monolith. Architecture is always a trade-off.

  • **Shopify**: a Ruby on Rails monolith handling USD 150B+ in transactions. Proof that a monolith can scale
  • **Netflix**: 700+ microservices, Chaos Monkey kills production services every day - and everything still works
  • **Uber**: started with a monolith, migrated to microservices at 1000+ engineers - when the pain of coordination became unbearable
  • **Banks**: event sourcing for transaction audit - every operation recorded forever

The Bezos Mandate and the Birth of API-first

In 2002, Jeff Bezos sent all Amazon teams an internal memo: all data and functionality must be exposed only through APIs. No direct database access, no direct integrations. Violators fired. This document, known as the "Bezos API Mandate", became the foundation for Amazon SOA and later AWS. In 2011, former Amazon employee Steve Yegge accidentally published a paraphrase of the memo on Google+. The post went viral and became the canonical description of the microservices philosophy.

Layered Architecture

Entire application crammed into one 50,000-line file. HTTP handling, business logic, SQL - all tangled together. A tweak to an API response shape silently breaks database writes. Not a hypothetical - this is the actual state of legacy code at plenty of companies. **Layered Architecture** is the first step out of the swamp: split the code into **layers with clear responsibilities**.

Iron rule: **dependencies flow downward only**. Presentation knows about Business, Business knows about Data Access - never the other way around. Result: swap the database and the controllers do not flinch.

MVC (Model-View-Controller) is a UI-flavored variant of layered architecture. Model = data and business logic, View = rendering, Controller = the glue. Rails, Django, Spring MVC all run on this pattern.

ProsCons
Easy to understandTight coupling between layers
Separation of concernsA change in one layer cascades up/down
Easy to start a projectBusiness Layer is bound to Data Access
Familiar to most developersHard to test without a database

Layered architecture's biggest crack: the Business Layer depends on Data Access. Business logic gets welded to a specific database. Migrating from PostgreSQL to MongoDB means rewriting the business layer.

In layered architecture, a controller directly makes a SQL query to the database. What is violated?

Hexagonal Architecture (Ports & Adapters)

Layered architecture welds business logic to infrastructure. In 2005, Alistair Cockburn proposed **Hexagonal Architecture** (aka Ports & Adapters): **domain at the center, infrastructure on the outside**. Business logic has no idea where the request came from (HTTP? CLI? a test?) or where the data lands (PostgreSQL? MongoDB? in-memory?).

Port - an interface describing a CONTRACT. "Orders must be saveable" (port), not "PostgreSQL required" (implementation). Adapter - a concrete implementation of a port. PostgresOrderRepository and InMemoryOrderRepository are two adapters wired into the same port.

The core principle: **Dependency Inversion**. Business logic defines the interfaces; infrastructure implements them. Dependencies point **inward**, toward the domain. The domain depends on nothing.

LayeredHexagonal
Business depends on DatabaseBusiness defines interface, Database implements it
Tests require a databaseTests use an in-memory adapter
Changing DB = rewrite everythingChanging DB = new adapter
Easy to startMore boilerplate upfront
Suited to CRUDSuited to complex domain

Hexagonal architecture pays off most when the business logic is rich and long-lived. For a basic CRUD application, layered architecture is plenty - do not over-engineer without a reason.

In hexagonal architecture, OrderService has `import { Pool } from 'pg'`. What is violated?

Microservices Architecture

2001. Amazon's monolith had bloated to the point that teams spent **70% of their time coordinating** instead of shipping code. Jeff Bezos dropped the famous mandate: all teams communicate through APIs, no direct calls, violators fired. The microservices philosophy was born. Netflix later split into 700+ microservices, hit distributed-systems hell head-on, and invented Chaos Engineering on the way out.

Microservice - a small, independently deployable service owning one business domain (bounded context). Each service ships with its own database, its own API, its own lifecycle.

Netflix is the canonical example: 700+ microservices. Recommendations, streaming, billing, search - each one independent. Recommendations service down? Users still watch movies.

ProsCons
Independent deployment of each serviceDistributed systems: network latency, partial failures
Scaling a specific serviceData consistency between services - HARD
Freedom to choose technologiesMonitoring, tracing, debugging - exponentially harder
Failure isolationInfrastructure: service discovery, API gateway, message broker
Small teams, fast decisionsCross-service testing - a nightmare

Microservices are not a monolith upgrade. They are a trade: swap code complexity for infrastructure complexity. For a team of 3, microservices are almost always pure overhead.

A 4-developer startup is building an MVP. The CTO proposes splitting into 12 microservices immediately to "scale in the future". Is this a good idea?

Event-Driven Architecture

In microservices, services chain through APIs: OrderService calls PaymentService, then NotificationService, then InventoryService. Every hop is synchronous, every hop a potential failure point. NotificationService stalls for 30 seconds - does the whole order hang? **Event-Driven Architecture** flips the script: services **publish events**, and any interested service **subscribes**.

Event - a fact that has already happened: OrderCreated, PaymentCompleted, UserRegistered. Events are **immutable** - they cannot be cancelled, only neutralized by a compensating event (OrderCancelled).

Advanced event-driven architecture patterns:

PatternDescriptionWhen to use
Event BusSimple bus: publish/subscribeWithin a single service or monolith
Message BrokerKafka, RabbitMQ - delivery guaranteesBetween microservices
CQRSSeparate read and write modelsDifferent loads on read vs write
Event SourcingStore the sequence of events instead of stateAudit, finance, undo operations

Event-driven architecture brings **eventual consistency**: data between services does not sync instantly. A user creates an order, but the "My Orders" page does not show it yet (the event has not been processed). UX has to be designed around that lag, not around it.

Microservices are always better than a monolith. A monolith is legacy that needs to be replaced.

Monolith-first is the recommended approach (Martin Fowler). A well-structured monolith is simpler, cheaper, and faster for small teams. Microservices are justified at Netflix/Amazon scale.

Microservices add enormous accidental complexity: service discovery, distributed tracing, inter-service authentication, eventual consistency, network failures. Shopify is a monolith at USD 150B. Basecamp is a monolith. Stack Overflow serves millions of users with a monolith on a handful of servers. Choose architecture based on real problems, not hype.

OrderService creates an order and synchronously calls NotificationService to send an email. NotificationService crashes. What happens to the order?

Key Takeaways

  • Layered: Presentation → Business → Data Access. Simple, but business logic is tied to infrastructure
  • Hexagonal: Ports & Adapters. Domain in the centre, infrastructure outside. Dependency Inversion. Testability without a database
  • Microservices: independent services, own databases, API contracts. Team scalability, but distributed systems hell
  • Event-Driven: events instead of direct calls. Loose coupling, but eventual consistency
  • Bezos forced Amazon to communicate through APIs - it works for 1.5 million employees. But Shopify makes USD 150B on a monolith. Architecture is chosen for the real problem, not the trend

What's next?

Architectural patterns are the skeleton of the system. Practices fill it in:

  • What is Software Engineering — Foundation: why architecture matters in the engineering process
  • Requirements and Specifications — Requirements determine architectural choices (NFR → architecture)

Вопросы для размышления

  • What architecture does the project currently being worked on use? Is it appropriate for the current scale?
  • When building an e-commerce system for 100 users today and 100,000 in a year - where to start?
  • Name an example of accidental complexity in a project. Which architectural pattern could reduce it?

Связанные уроки

  • se-01 — Foundation: why architecture matters in the engineering process
  • se-02 — Requirements (NFR) determine the architectural choice
  • se-04 — Design patterns implement architectural decisions at code level
  • ds-01 — Microservices are distributed systems with CAP-class problems
  • ds-02 — Event-driven + eventual consistency is AP in CAP theorem
  • se-08 — API Design defines the contracts between microservices
  • db-04-cap
Architectural Patterns

0

1

Sign In