Table of Contents
Chapter 1: Architecture Fundamentals
Web application architecture defines how components of your application interact: the frontend (what users see), the backend (business logic and data), the database, and external services.
Architecture decisions should be driven by requirements, not trends. A well-built monolith is better than a poorly implemented microservices architecture. Choose complexity only when it solves a real problem.
Key architectural qualities to evaluate: scalability (handling increased load), availability (uptime guarantees), maintainability (ease of making changes), and security (protecting data and access).
The technology stack decision impacts everything downstream. Choose technologies your team knows well. Productivity with a familiar stack almost always beats theoretical advantages of an unfamiliar one.
Chapter 2: Monolithic Architecture
A monolith is a single deployable unit containing all application functionality. Despite its reputation, a well-structured monolith is the right choice for most applications, especially in early stages.
Advantages: simpler development and debugging, easier deployment, straightforward testing, better performance for inter-module communication, and lower operational overhead.
Modular monolith: the best of both worlds. Organize code into well-defined modules with clear boundaries. This gives you the simplicity of a monolith with the option to extract microservices later if needed.
When a monolith struggles: when different parts need to scale independently, when teams need to deploy different components on different schedules, or when the codebase becomes too large for any developer to understand.
Chapter 3: Microservices Architecture
Microservices decompose an application into small, independently deployable services. Each service owns its data, runs in its own process, and communicates via APIs or message queues.
Benefits: independent scaling of services, technology flexibility (different services can use different languages), independent deployment, and fault isolation (one service failure does not crash the entire application).
Costs: network complexity, distributed transaction challenges, service discovery, monitoring complexity, and the need for sophisticated DevOps practices and tooling.
When to adopt: you have 50+ developers who need to work independently, specific components have dramatically different scaling needs, or you need to use different technology stacks for different capabilities.
Microservices anti-patterns: distributed monolith (microservices with tight coupling), nano-services (services too small to justify the overhead), and shared databases (violates service independence).
Chapter 4: Serverless and JAMstack
Serverless architecture runs code in response to events without managing servers. AWS Lambda, Google Cloud Functions, and Azure Functions execute code on demand and charge only for execution time.
Best for: event-driven processing (file uploads, webhooks), APIs with variable traffic, scheduled tasks, and rapid prototyping where infrastructure management would slow development.
JAMstack (JavaScript, APIs, Markup) pre-renders pages at build time and serves them from a CDN. Dynamic functionality comes from JavaScript calling APIs. Ideal for content sites, marketing sites, and e-commerce frontends.
JAMstack frameworks: Next.js (React), Nuxt (Vue), and Astro (framework-agnostic) are the leading options. They support static generation, server-side rendering, and hybrid approaches.
Limitations of serverless: cold start latency, execution time limits, vendor lock-in, and difficulty with stateful operations. Not ideal for long-running processes or applications requiring persistent connections.
Chapter 5: Making Architecture Decisions
Start simple, evolve as needed. Begin with a modular monolith, extract services when specific modules need independent scaling or deployment. This approach avoids premature complexity.
Database selection: PostgreSQL for most use cases (relational data, JSON support, full-text search). Add specialized databases (Redis for caching, Elasticsearch for search) only when PostgreSQL is not sufficient.
API design: REST for external APIs and simple CRUD operations. GraphQL when clients need flexible queries. gRPC for internal service-to-service communication where performance matters.
Frontend architecture: server-rendered (Next.js) for SEO-critical sites, SPA (React/Vite) for dashboard applications, and static generation for content-heavy sites with infrequent updates.
Document your architecture decisions in Architecture Decision Records (ADRs). Each ADR captures what was decided, why, what alternatives were considered, and what trade-offs were accepted.
