Chapter 02 · Background

Application Architecture


Application architecture is the arrangement of responsibilities, state, and communication—not a contest to draw the most boxes. Senior candidates choose boundaries that contain change and failure, keep invariants enforceable, and allow the organization to operate the system. A modular monolith can be more scalable and reliable than poorly divided microservices; an event-driven system can be more coupled through schemas and timing than a synchronous one.

Level: foundation → senior/staff Primary skill: choosing boundaries, communication styles, and deployment units that fit the domain Companions: Design Requirements · API Design · Message Queues · Replication and Sharding

How to use this chapter

  1. Read the mental model and mechanics without taking notes.
  2. Close the page and explain the topic aloud in five minutes.
  3. Work the quantitative example on paper.
  4. Answer the question ladder without revealing the answer signals.
  5. Complete one design drill and score yourself with the senior checklist.

Learning objectives

  • Compare monoliths, modular monoliths, services, event-driven systems, and serverless functions without ideology.
  • Identify domain, data, deployment, team, and failure boundaries.
  • Choose synchronous versus asynchronous communication and manage their failure semantics.
  • Separate stateless compute from durable state while recognizing genuinely stateful services.
  • Design for evolution, migration, observability, and operational ownership.
  • Explain control planes, data planes, and multi-tenant isolation at a senior level.

Mental model

Start with the domain and its invariants. Group behavior that changes together and data that must be updated atomically. Split when independent scaling, security isolation, ownership, or release cadence is worth the distributed-systems cost. Architecture should make the common path simple and the failure path explicit.

Core mechanics

Architecture styles and their real trade-offs

A monolith is one deployable unit; it may still have excellent internal modules. A modular monolith enforces dependency direction and ownership while retaining local transactions and simple debugging. Microservices create independently deployable processes around capabilities. Event-driven architecture uses durable or ephemeral events to decouple time and fan-out. Functions/serverless package short-lived handlers behind a managed runtime.

The style does not guarantee quality. A distributed monolith has many services that must deploy together, share databases, and call each other synchronously. A well-designed monolith can scale horizontally behind a load balancer and use queues for expensive work. Choose based on constraints, not fashion.

Bounded contexts, ownership, and invariants

A useful service boundary usually owns a coherent business capability, its code, and its data. It exposes contracts rather than tables. Strong invariants—such as “a payment capture cannot exceed the authorized amount”—are easiest to enforce inside one transactional boundary. Splitting that invariant across services requires a protocol, idempotency, compensation, and explicit intermediate states.

Data ownership does not mean no one else can read the data; it means changes flow through the owning contract and the owner defines semantics. Read models, change streams, and replicated views can serve other domains without giving them write access to internal tables.

Synchronous request paths

Synchronous calls are easy to understand and appropriate when a caller needs an immediate answer. They also compose latency and availability. A request that fans out to ten dependencies has more opportunities for a slow or failed branch, and retries can multiply load.

Senior designs use deadlines propagated end to end, bounded retries with jitter and budgets, circuit breaking, bulkheads, connection pooling, and fallbacks. They avoid deep call chains and distinguish required from optional dependencies. “Service A calls B” is incomplete without timeout, idempotency, overload, and failure behavior.

Asynchronous workflows

Queues and logs decouple producer rate from consumer rate and allow independent retries. They do not remove coupling: producers and consumers remain coupled through schema, meaning, ordering, retention, and operational expectations. Every handler should define its idempotency key, acknowledgment point, retry classification, dead-letter policy, and observability.

Long-running business transactions often use sagas: a sequence of local transactions with compensating actions or a central orchestrator. Compensation is not database rollback; it is a new business action that may itself fail. Explicit workflow state is usually safer than hiding a distributed transaction in ad hoc callbacks.

Stateless and stateful compute

Stateless request handlers are easy to replace and scale because durable truth lives elsewhere. Local caches and connection state can still exist, but the service must tolerate their loss. Session affinity may improve locality but should not become the only copy of important state.

Some components are inherently stateful: databases, stream processors, coordination services, and WebSocket gateways with live connections. They require placement, replication, recovery, and rebalancing plans. Calling everything stateless simply moves state into an undocumented place.

Control plane and data plane

The data plane handles the high-volume user or workload path. The control plane configures, schedules, discovers, authorizes, or manages the data plane. Keeping them separate can protect serving from administrative slowness and allow different consistency and availability goals.

A control-plane outage should not necessarily stop an already configured data plane. Cache last-known-good configuration, version changes, make rollouts idempotent, and design reconciliation. Conversely, stale control state can be dangerous, so define leases, expiry, and emergency disable paths.

Evolution and migration

Architecture is exercised most during change. Safe evolution uses backward-compatible contracts, expand-and-contract database migrations, dual reads/writes only with reconciliation, shadow traffic, canaries, and explicit rollback. A boundary that cannot be migrated independently is not truly independent.

Strangler migrations incrementally route capabilities from an old system to a new one. The hard parts are identity, duplicated state, source of truth, consistency windows, and retirement criteria—not the routing proxy itself.

Decision table

DecisionPrefer the first option when…Prefer the second option when…Senior caveat
Modular monolith vs microservicesThe domain/team is small, invariants span modules, and deployment independence has low value.Teams need independent ownership/scaling or strong isolation justifies network boundaries.Start with enforceable module boundaries; extraction should follow measured pain, not anticipated prestige.
Synchronous vs asynchronousThe caller needs an immediate result and the dependency is fast/reliable enough for the latency budget.Work can complete later, bursts must be buffered, or fan-out/retry isolation matters.Async shifts complexity to ordering, duplicates, lag, workflow state, and user-visible status.
Orchestration vs choreographyA workflow needs explicit state, auditability, deadlines, and centralized policy.The flow is simple and independent subscribers react without global sequencing.Unbounded choreography becomes hard to discover and reason about; orchestration can become a central bottleneck.
Shared database vs database per serviceAtomic cross-module invariants and operational simplicity dominate.Independent schema ownership, scaling, and isolation dominate.A shared server with separate schemas can be an intermediate step; ownership rules matter more than slogans.
Serverless vs long-running serviceBursty, event-driven work fits execution limits and managed scaling reduces toil.Steady traffic, long-lived connections, low latency, or runtime control matters.Model cold starts, concurrency limits, state, observability, and provider-specific failure modes.
Single region vs multi-region activeRTO permits regional recovery and consistency/simplicity are more valuable.Latency or availability requires serving through a regional loss.Multi-region is a data-consistency and operations program, not merely duplicate compute.

Quantitative reasoning

Failure modes and production signals

Failure modeWhat users seeLikely causeMitigation / design responseUseful signals
Cascading synchronous failureBroad latency spike and timeoutsOne dependency slows; callers retry and exhaust poolsDeadlines, retry budgets, circuit breakers, bulkheads, shed optional callsDependency latency, retry rate, pool saturation, request fan-out
Distributed monolithSmall change requires coordinated release; incidents span many teamsTight runtime/data coupling despite many servicesClarify ownership, remove cycles, introduce contracts, merge or decouple servicesDeployment coupling, call graph, shared-table access, change failure rate
Queue backlogDelayed workflows and stale stateConsumer capacity loss, poison messages, traffic burstAutoscale on age, isolate poison messages, backpressure producers, replay safelyOldest message age, lag by partition, retry/DLQ rate
Split source of truthConflicting values and irreconcilable user stateDual writes without transaction or reconciliationChoose authority, outbox/change capture, idempotent repair, audit logMismatch counters, reconciliation lag, duplicate events
Control-plane outageNew changes fail; data plane may or may not continueCentral config/discovery unavailableLast-known-good config, leases, local caching, degraded operationsConfig freshness, reconciliation errors, lease expiry
Noisy tenantOne customer degrades othersShared pools and unbounded per-tenant demandQuotas, fair queues, isolated shards/pools, admission controlPer-tenant cost, saturation, queue age, throttles

Senior-level lenses

Boundaries must align across dimensions

Code, data, deployment, on-call, and business ownership can disagree. A service owned by Team A but dependent on Team B’s table is not autonomous. During design, state who owns the contract, data, migrations, capacity, and incidents. Sometimes the right answer is organizational change rather than another API.

Localize invariants

Strong invariants that cross network boundaries become protocols. Before introducing a saga or distributed lock, ask whether the boundary should move so the invariant is local. When it cannot, model intermediate states and recovery as first-class domain concepts.

Design the degraded product

Reliability is often achieved by deciding what can be stale, omitted, queued, or read-only. A home page may omit recommendations; checkout may not omit payment authorization. Senior candidates classify dependencies by criticality and describe a user-coherent degraded mode.

Architecture includes operations

Every component adds dashboards, alerts, capacity models, deployments, security patches, and on-call paths. Estimate the operational surface area. Prefer a boring component when it meets requirements and the team can run it well.

Multi-tenancy is a policy problem

Isolation spans identity, authorization, data partitioning, encryption, quotas, billing, observability, and incident containment. Decide whether tenants share tables, shards, clusters, accounts, or regions and how they move between tiers without downtime.

Evolution is the proof of modularity

Ask how to change schemas, split a service, rotate an API, or migrate a tenant while both versions run. Backward compatibility and reconciliation are stronger evidence of architecture quality than a static diagram.

Interview question ladder

Q2.1 foundation

What is the difference between a monolith and a modular monolith?

Show strong-answer signals

A monolith is one deployable unit; a modular monolith additionally enforces internal boundaries, ownership, and dependency direction. It can still scale horizontally.

Q2.2 foundation

Why are stateless application servers easier to scale?

Show strong-answer signals

Any instance can handle a request; replacement and load balancing are simple. Clarify that durable/session state must live in a replicated store or recoverable layer.

Q2.3 foundation

When is asynchronous communication useful?

Show strong-answer signals

Burst absorption, decoupled timing, fan-out, retry isolation, and long work. Also mention duplicates, lag, ordering, and user-visible status.

Q2.4 intermediate

What makes a microservice boundary good or bad?

Show strong-answer signals

Good boundaries align capability, data ownership, invariants, team ownership, scaling, and failure isolation. Bad boundaries are entity CRUD services, shared tables, chatty calls, and coordinated releases.

Q2.5 intermediate

How do you prevent retries from causing a cascade?

Show strong-answer signals

End-to-end deadlines, retry only transient/idempotent operations, exponential backoff with jitter, retry budgets, circuit breakers, and load shedding.

Q2.6 intermediate

Explain saga orchestration versus choreography.

Show strong-answer signals

Orchestrator stores workflow state and commands steps; choreography reacts to events. Compare auditability, coupling, discoverability, centralization, and compensation.

Q2.7 senior

A company wants to split a checkout monolith into services. How would you choose the first extraction?

Show strong-answer signals

Map domain/invariants and change pain; choose a capability with clear ownership, low cross-transaction coupling, and measurable scaling or release benefit. Describe strangler routing, data ownership, compatibility, observability, and rollback.

Q2.8 senior

Design a request path that depends on inventory, pricing, recommendations, and fraud. Which calls are synchronous?

Show strong-answer signals

Classify correctness-critical versus optional, define latency budget/deadlines, cache or precompute where possible, queue noncritical work, and give coherent degraded behavior.

Q2.9 senior

How would you design a control plane so serving continues during its outage?

Show strong-answer signals

Versioned desired state, local cache/last-known-good, reconciliation, leases/expiry, idempotent rollout, scoped blast radius, emergency controls, and audit.

Q2.10 senior

A queue-backed workflow occasionally executes a side effect twice. What architecture changes do you make?

Show strong-answer signals

Idempotency key and dedupe store, transactional outbox/inbox, atomic state transition with acknowledgment, effect-specific reconciliation, and clear exactly-once scope.

Q2.11 staff / stretch

Propose a multi-tenant architecture that supports shared, isolated, and dedicated tiers.

Show strong-answer signals

Common identity/control plane; placement metadata; tenant-aware routing; shared shards with quotas; isolated clusters for higher tiers; encryption and observability; online migration and billing.

Q2.12 staff / stretch

How do you decide whether to merge two services rather than further decouple them?

Show strong-answer signals

Use change/deploy coupling, cross-service invariants, synchronous chatter, incident ownership, scaling profiles, and team structure. Explain migration and contract simplification, not only organization preference.

Design drills

Drill 1 Modular monolith to services

You inherit a 60-developer commerce monolith with weekly releases, a shared relational database, and frequent checkout regressions. Produce a target architecture and a two-stage migration plan. Identify which modules stay together because of invariants.

What the interviewer is testing

Boundary selection, incremental migration, data ownership, release safety, and avoidance of a distributed monolith.

Drill 2 Notification platform

Design a platform that accepts email, push, and SMS requests, supports scheduled delivery, per-tenant quotas, templates, retries, and delivery receipts. Decide which path is synchronous and where workflow state lives.

What the interviewer is testing

Sync/async decomposition, tenant isolation, idempotency, provider failure, control/data plane separation.

Drill 3 Regional degradation

A user-profile service is globally read-heavy but writes must remain ordered per user. Design regional serving and describe behavior when the home region is unreachable.

What the interviewer is testing

Data ownership, consistency scope, routing, failover semantics, read staleness, and operational recovery.

Common weak answers and how to improve them

“Microservices scale better.”

Show the stronger answer

Name the independent scaling or isolation need and include network, data, deployment, and on-call costs.

“Use events to decouple everything.”

Show the stronger answer

Specify schema ownership, ordering, retention, replay, idempotency, and which business workflow observes completion.

“The app servers are stateless.”

Show the stronger answer

Identify connection state, caches, sessions, local files, in-flight jobs, and how loss is handled.

“We will do a saga if the transaction fails.”

Show the stronger answer

Model workflow states and compensation before implementation; compensation is a fallible business action, not automatic rollback.

“Put a circuit breaker around it.”

Show the stronger answer

Define thresholds, open/half-open behavior, fallback, retry interaction, observability, and how to avoid synchronized recovery.

Primary sources and standards