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
Предварительные знания
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.
| Pros | Cons |
|---|---|
| Easy to understand | Tight coupling between layers |
| Separation of concerns | A change in one layer cascades up/down |
| Easy to start a project | Business Layer is bound to Data Access |
| Familiar to most developers | Hard 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.
| Layered | Hexagonal |
|---|---|
| Business depends on Database | Business defines interface, Database implements it |
| Tests require a database | Tests use an in-memory adapter |
| Changing DB = rewrite everything | Changing DB = new adapter |
| Easy to start | More boilerplate upfront |
| Suited to CRUD | Suited 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.
| Pros | Cons |
|---|---|
| Independent deployment of each service | Distributed systems: network latency, partial failures |
| Scaling a specific service | Data consistency between services - HARD |
| Freedom to choose technologies | Monitoring, tracing, debugging - exponentially harder |
| Failure isolation | Infrastructure: service discovery, API gateway, message broker |
| Small teams, fast decisions | Cross-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:
| Pattern | Description | When to use |
|---|---|---|
| Event Bus | Simple bus: publish/subscribe | Within a single service or monolith |
| Message Broker | Kafka, RabbitMQ - delivery guarantees | Between microservices |
| CQRS | Separate read and write models | Different loads on read vs write |
| Event Sourcing | Store the sequence of events instead of state | Audit, 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