Layered Architecture
Layered architecture organizes code into horizontal tiers — presentation, application, domain, infrastructure — with strict dependency direction downward.
Introduction
Layered architecture organizes code into horizontal tiers — presentation, application, domain, infrastructure — with strict dependency direction downward. Netflix's early streaming platform used layered monoliths before microservices; understanding layers remains essential for structuring modules inside any deployable unit.
Real production story
Netflix's 2010 streaming API was a classic three-tier Java monolith: JSP controllers called service classes that called DAOs. A engineer bypassed the service layer to "just query the DAO" from a controller for a hackathon feature. The shortcut shipped, then propagated — within a year, 40% of controllers hit DAOs directly, business rules duplicated across tiers, and a single schema change required touching 200 files.
Glenn Vanderburg's refactor introduced strict layering with package-private DAOs and Checkstyle rules: presentation → application → domain → infrastructure. Test coverage on domain logic jumped from 34% to 89% because rules were testable without servlet containers. The layered monolith survived until microservice extraction began — layers became service boundaries.
Business problem
Business pressure: Netflix needed to iterate on recommendation UI weekly while keeping billing and playback rules consistent. Without layer discipline, UI teams embed SQL in controllers and billing logic leaks into JSPs — every UI tweak becomes a schema migration.
- Revenue at risk: Duplicate discount logic in controller and service tiers caused inconsistent pricing for 2% of streams — millions in entitlement disputes.
- Engineering velocity: Onboarding engineers could not locate business rules; "where does cancellation logic live?" had four answers.
- Compliance / trust: SOC1 auditors require traceable business rule layer separate from presentation — layer skipping breaks audit narrative.
Architecture overview
Layered architecture stacks responsibilities vertically. Upper layers orchestrate; lower layers implement technical details. The domain layer must not depend on presentation or persistence frameworks.
- Definition: Presentation → Application → Domain → Infrastructure, dependencies point inward/down.
- When to adopt: CRUD-heavy enterprise apps, teams new to DDD, monoliths needing immediate structure.
- When to defer: Event-heavy systems where layers cut across event flows — consider hexagonal instead.
- Operability: Layer violations surface as arch tests; runtime metrics still per-endpoint, not per-layer.
Architecture motivation
Why architects care: Layers separate concerns that change at different rates — UI changes daily, domain rules monthly, infrastructure when vendors shift. Dependency inversion keeps domain logic independent of frameworks.
- Force: Multiple teams modify same codebase with different change frequencies.
- Constraint: Cannot rewrite to microservices while subscriber growth demands feature velocity.
- Outcome: Predictable location for every class of change; testable domain core.
Internal architecture
Netflix streaming monolith layers — classic n-tier with inverted domain:
- Application layer owns transaction boundaries (@Transactional at use-case level).
- Domain layer defines Repository interfaces; infrastructure implements them.
┌──────────────────────────────────────────────────────────┐│ PRESENTATION (Controllers, DTOs, View models) ││ │ calls application services only │├───────▼──────────────────────────────────────────────────┤│ APPLICATION (Use cases, orchestration, transactions) ││ │ coordinates domain; no business rules here │├───────▼──────────────────────────────────────────────────┤│ DOMAIN (Entities, value objects, domain services) ││ │ pure Java — zero Spring, zero JDBC imports │├───────▼──────────────────────────────────────────────────┤│ INFRASTRUCTURE (JPA repos, REST clients, message adapters)││ implements interfaces defined in DOMAIN │└──────────────────────────────────────────────────────────┘Dependency rule: each layer may call ONLY the layer below
Data flow
Primary path: REST controller receives CancelSubscriptionRequest, maps to application command, use case loads aggregate via repository port, domain applies cancel() rule, infrastructure persists and emits integration event.
- Write path: Controller → UseCase → Domain aggregate → Repository impl → DB.
- Read path: Query use cases may bypass full domain for read models (CQRS-lite) but still enter through application layer.
- Async path: Infrastructure publishes to Kafka after use case commits; domain never imports Kafka SDK.
System design diagram
Two diagrams show the Layered Architecture topology and the primary request/event path used in production at scale.
Production code example
Spring Boot layered package structure with ArchUnit — Netflix-style enforcement:
// Package layoutcom.netflix.streaming.presentation.api // @RestController — DTOs in/out only.application.command // CancelSubscriptionUseCase.domain.model // Subscription aggregate, CancelPolicy.domain.port // SubscriptionRepository interface.infrastructure.jpa // SubscriptionJpaRepository implements port.infrastructure.kafka // SubscriptionEventPublisher@Service@RequiredArgsConstructorclass CancelSubscriptionUseCase {private final SubscriptionRepository repo;private final EventPublisher events;@Transactionalpublic void execute(CancelCommand cmd) {Subscription sub = repo.findById(cmd.subscriberId()).orElseThrow(() -> new NotFoundException(cmd.subscriberId()));sub.cancel(cmd.reason()); // domain rule — no IF in use caserepo.save(sub);events.publish(sub.domainEvents());}}// ArchUnit: domain must not depend on infrastructurenoClasses().that().resideInAPackage("..domain..").should().dependOnClassesThat().resideInAPackage("..infrastructure..");
Enterprise case study
Netflix streaming API (2009–2012): Layered monolith supported 20M subscribers before microservice migration; layer discipline made domain logic portable to extracted services.
- Before: Controllers with embedded SQL; untestable cancellation logic.
- Decision: Enforce four layers with Checkstyle + package visibility + domain-only unit tests.
- After: Domain tests ran in <30s CI; billing service extraction reused 80% of domain package.
Trade-offs
- Clarity vs rigidity: Layers make navigation predictable but encourage anemic domain models if application layer hoards logic.
- Performance vs purity: Strict layering adds mapping hops; pragmatic teams allow read-path shortcuts with ADRs.
- Monolith layers vs microservices: Layers are vertical slices inside one deployable; do not confuse with service boundaries.
Security considerations
Security is architectural: Authorization belongs in application layer (use-case guards), not sprinkled in controllers or repositories.
- Identity: Presentation validates JWT; application layer enforces role-per-use-case.
- Data: Infrastructure encrypts at rest; domain never sees connection strings.
- Supply chain: Domain layer's zero-dependency rule reduces attack surface in business-critical code.
Scalability analysis
Scale dimensions: Netflix scaled the layered monolith horizontally until playback and billing layers needed independent fleets — layers informed but did not dictate service cuts.
- Horizontal scale: Stateless presentation tier behind Eureka/ELB; session in Cassandra.
- Hot spots: Application layer becomes god-service — split use cases by subdomain before extracting microservices.
- Cost: Layer mapping (DTO explosion) increases CPU on high-QPS paths — profile before adding mappers.
Failure scenarios
What breaks: Layer skipping erodes over time; infrastructure exceptions leak through domain into API responses.
- Anemic domain: All logic in application services — domain entities are data bags; changes require touching every layer.
- Leaky abstraction: JPA annotations on domain entities couple domain to Hibernate — schema change ripples to API.
- God service: Single application service class grows to 4000 lines — sign to split by use case or extract module.
Staff engineer insights
- Layers are a organizing tool, not an architecture — if your domain layer imports Spring, you have three tiers and a pretense.
- Netflix's migration succeeded because domain logic was already isolated — layers paid for themselves at extraction time.
- Watch for the "smart controller" anti-pattern: if PRs add business rules above the application layer, reject them.
Interview questions
Interview Prep
Practice concise answers, then expand each card for the explanation.
1AdvancedQuestionWhat is the difference between layered architecture and hexagonal architecture?+
Answer
Follow-up
2AdvancedQuestionHow do you prevent layer violation decay over time?+
Answer
Follow-up
3AdvancedQuestionCan layered architecture coexist with microservices?+
Answer
Follow-up
Architecture review questions
- Is dependency direction enforced (presentation → application → domain ← infrastructure)?
- Does domain layer have zero framework imports (Spring, JPA, HTTP)?
- Are transactions owned at application/use-case boundary?
- Are repository and messaging ports defined in domain, implemented in infrastructure?
- Do ArchUnit or equivalent tests fail CI on layer violations?
- Is authorization enforced at use-case level, not duplicated in controllers?
Summary
Layered architecture at Netflix scale organized a generation of monoliths before microservices — presentation, application, domain, infrastructure with strict dependency direction. Staff engineers use it today inside modules and services, always guarding against anemic domains and layer-skipping shortcuts that erode the structure.