- Chapter
- Chapter 1 — Design Pattern Fundamentals
- Prerequisites
- Topics 1.1 and 1.2
- You will be able to
- Distinguish patterns, libraries, frameworks and architectures as different kinds of reuse, explain inversion of control, recognise patterns inside frameworks, and decide what to build yourself versus take from a framework
- Covers
- Pattern · Library · Framework · Architecture
On this page
- 1. Concept / Definition
- 2. Why It Exists
- 3. Core Concepts
- 4. How It Works Internally — Frameworks Built from Patterns
- 4A. Deep Dive — Framework Coupling and Architecture
- 5. APIs / Features / Syntax — Extension Points in Frameworks
- 6. Practical Examples
- 7. Common Problems & Mistakes
- 8. Performance & Production Considerations
- 9. Architecture & Design Considerations
- 10. Interview Questions & Answers
1Concept / Definition
Software reuse comes in different forms. The TOC compares four:

| Form | What is reused | Who is in control | Example |
|---|---|---|---|
| Pattern | A design idea | You implement it | Strategy, Adapter |
| Library | Code you call | Your code calls the library | Jackson, Guava, java.time |
| Framework | Code + control flow — a skeleton you fill in | The framework calls your code | Spring, JUnit, Jakarta EE |
| Architecture | System structure and its rules | Organisation-wide decisions | Layered, hexagonal, microservices |
2Why It Exists
In the 1990s, object-oriented frameworks (MacApp, ET++, later Java EE) and design patterns grew together — several GoF authors worked on frameworks, and frameworks came to be described as partially complete applications that are often built from patterns. Confusion between the terms persists: teams say "we use the Spring pattern" or "we implemented a hexagonal framework". Precise distinctions matter for decisions: patterns are free to adapt, libraries are easy to swap, frameworks shape your whole codebase, and architectures shape your organisation.
3Core Concepts
3.1 Pattern
A pattern is abstract design knowledge: no code to download, no version to upgrade. You apply it by writing code shaped by the pattern.
| Property | Pattern |
|---|---|
| Artefact | Description (intent, structure, consequences) |
| Reuse | Of design experience |
| Coupling | None to a vendor or version |
| Adaptation | Unlimited — implement as your context requires |
3.2 Library
A library is reusable code with an API that your code calls when it needs it. Your code keeps control of the flow.
// Library: your code decides when to call Jackson
ObjectMapper mapper = new ObjectMapper();
Order order = mapper.readValue(json, Order.class);| Property | Library |
|---|---|
| Control flow | Owned by the application |
| Coupling | To the library API (can be wrapped behind an adapter) |
| Replaceability | Usually high — especially behind a port |
| Examples | Jackson, Apache Commons, Guava, Caffeine, java.time |
3.3 Framework
A framework is a reusable, semi-complete application: it defines the overall control flow and calls your code at defined extension points. This is inversion of control — the "Hollywood Principle": don't call us, we'll call you.

// Framework: Spring decides when your code runs
@RestController
class OrderController {
@GetMapping("/orders/{id}")
OrderView get(@PathVariable String id) { … } // called by DispatcherServlet
}
// JUnit is a framework too: it discovers and calls your test methods
class OrderTest { @Test void totalsLines() { … } }| Property | Framework |
|---|---|
| Control flow | Owned by the framework |
| Extension points | Interfaces, annotations, callbacks, configuration |
| Coupling | Deep — application structure follows the framework |
| Replaceability | Low — migrations are large projects |
| Examples | Spring, Jakarta EE, Quarkus, Micronaut, JUnit, Hibernate (partly) |

3.4 Architecture
An architecture is the fundamental organisation of a system — components, relationships, principles governing design and evolution. Architecture patterns (layers, hexagonal, microservices, event-driven, CQRS) are reusable architecture ideas; architecture is what you get when you apply them to a concrete system with its quality attributes and constraints.

| Property | Architecture |
|---|---|
| Scope | Whole system or subsystem |
| Decisions | Expensive to reverse (Book 4, Topic 10.7) |
| Influences | Quality attributes, team structure, deployment |
| Relation to frameworks | Frameworks should serve the architecture, not dictate it |
4How It Works Internally — Frameworks Built from Patterns
| Framework mechanism | Pattern |
|---|---|
DispatcherServlet delegating to handler mappings and adapters | Front Controller, Strategy, Adapter |
JdbcTemplate with callbacks | Template Method (callback variant), Facade |
@Transactional, @Cacheable | Proxy (AOP) |
BeanFactory, FactoryBean, @Bean | Factory Method / Abstract Factory |
| Bean scopes | Singleton, Prototype (container-managed) |
ApplicationEventPublisher | Observer / Mediator |
| Servlet filters, Spring Security filter chain | Chain of Responsibility / Decorator |
HandlerInterceptor | Interceptor (Decorator-like) |
Knowing these patterns explains framework behaviour: why @Transactional doesn't work on self-invocation (it's a proxy), why singleton beans must be thread-safe (one shared instance), and where to extend the framework (strategy interfaces, customizers).
4ADeep Dive — Framework Coupling and Architecture
4A.1 How deep should framework coupling go?
| Approach | Description | Trade-off |
|---|---|---|
| Framework everywhere | Domain classes use framework annotations and types | Fast to build; hard to migrate or test without framework |
| Framework at the edges | Domain/application code plain Java; framework in adapters and configuration (hexagonal) | More mapping/wiring; core independent |
| Pragmatic middle | Annotations like @Service/@Transactional allowed in application layer; domain framework-free | Common balance |
4A.2 Build vs take
| Question | If yes |
|---|---|
| Does a mature library/framework solve this generic problem? | Take it (don't reinvent logging, HTTP, JSON) |
| Is it core business logic? | Build it — patterns, not frameworks |
| Would the framework dictate your domain model? | Keep it at the edges |
| Is the library volatile or vendor-specific? | Wrap it behind a port (Adapter) |
5APIs / Features / Syntax — Extension Points in Frameworks
| Framework | Typical extension points |
|---|---|
| Spring | Beans, @Bean methods, *Customizer, BeanPostProcessor, HandlerInterceptor, event listeners |
| Spring Boot | Auto-configuration, @ConditionalOn…, starters |
| JUnit 5 | Extension API: BeforeEachCallback, ParameterResolver |
| Jakarta EE | CDI producers, interceptors, JAX-RS filters |
| Servlet API | Filter, ServletContainerInitializer |
6Practical Examples
6.1 Same need, four forms of reuse
Need: "Retry calls to the payment provider."
| Form | What you'd use |
|---|---|
| Pattern | Retry pattern with exponential backoff and jitter (design idea) |
| Library | Resilience4j Retry called explicitly in your adapter |
| Framework | Spring Framework 7 @Retryable applied by proxies |
| Architecture | Service mesh retries configured at platform level |
6.2 Wrapping a library behind a port
public interface JsonCodec { // port owned by the application
<T> T decode(String json, Class<T> type);
String encode(Object value);
}
final class JacksonJsonCodec implements JsonCodec { // adapter over the library
private final ObjectMapper mapper;
JacksonJsonCodec(ObjectMapper mapper) { this.mapper = mapper; }
public <T> T decode(String json, Class<T> type) {
try { return mapper.readValue(json, type); }
catch (JsonProcessingException e) { throw new InvalidJson(e); }
}
public String encode(Object v) {
try { return mapper.writeValueAsString(v); }
catch (JsonProcessingException e) { throw new InvalidJson(e); }
}
}Wrapping is worthwhile when the library is volatile or used widely in the core; for stable, ubiquitous libraries at the edges, direct use is fine.
6.3 Classification exercise
| Item | Kind |
|---|---|
| Repository | Pattern (PoEAA) |
| Spring Data JPA | Framework (generates repositories, calls your queries) |
| Hibernate ORM | Framework/library hybrid |
| HikariCP | Library |
| Hexagonal architecture | Architecture pattern |
| Resilience4j | Library (with Spring integration) |
| Circuit breaker | Pattern |
7Common Problems & Mistakes
- Calling a framework "a pattern" or a pattern "a framework".
- Letting the framework dictate the domain model without a conscious decision.
- Reinventing libraries for generic problems (custom JSON, HTTP, logging).
- Wrapping everything — adapters around stable, ubiquitous libraries add little.
- Ignoring the patterns inside frameworks, leading to proxy, scope and lifecycle surprises.
8Performance & Production Considerations
| Aspect | Consideration |
|---|---|
| Framework startup | Classpath scanning, proxies and auto-configuration cost startup time (AOT helps) |
| Framework upgrades | Major upgrades are projects; plan them in the roadmap |
| Library CVEs | Dependency scanning and timely upgrades |
| Architecture patterns | Drive infrastructure cost and operational complexity |
Framework choices dominate startup time, memory footprint and upgrade effort, so measure them rather than assume. Classpath scanning, proxy generation and auto-configuration typically add seconds to a Spring Boot start; Spring AOT processing, GraalVM native images, class data sharing or the Java 25 AOT cache reduce this where fast scaling or serverless deployment matters. Libraries bring operational risk through their transitive dependencies: generate an SBOM, scan it in CI with tools such as OWASP Dependency-Check, Dependabot or Renovate, and patch critical CVEs within an agreed time. Plan major framework upgrades, such as Spring Boot 3 to 4, as roadmap items and use automated migration recipes from OpenRewrite. Architecture patterns affect cost most of all: every additional microservice adds a pipeline, dashboards and on-call surface.
9Architecture & Design Considerations
- Frameworks should serve the architecture: keep domain logic framework-light if long-term independence matters.
- Technology radar: decide approved libraries/frameworks per concern.
- Patterns are portable across frameworks and languages — invest in pattern knowledge.
- Architecture decisions outlive frameworks; record them separately (ADRs).
10Interview Questions & Answers
What is the difference between a library and a framework?
The difference is who owns the control flow. Your code calls a library when it decides it needs it: you create an ObjectMapper and call readValue, and the library returns control to you. A framework inverts this: it owns the overall flow and calls your code at defined extension points, as Spring's DispatcherServlet calls your controller methods or JUnit discovers and runs your test methods. This is inversion of control. The consequence is coupling: a library sits behind a call site and can usually be wrapped and replaced, while a framework shapes your application's structure, lifecycle and configuration, so replacing it is a migration project rather than a local change.
Is Spring a pattern?
No. Spring is a framework, code you depend on that calls your code, while a pattern is a reusable design idea with no code to download. The confusion arises because Spring is built from many patterns. The IoC container uses Factory Method and Abstract Factory, beans are container-managed singletons or prototypes, @Transactional and @Cacheable work through proxies, JdbcTemplate is a Template Method with callbacks, DispatcherServlet is a Front Controller using Strategy and Adapter, and ApplicationEventPublisher is Observer. Dependency injection itself is a pattern that Spring implements. Saying we use the Spring pattern therefore mixes levels: you use the Spring framework, which applies these patterns on your behalf.
What is the Hollywood Principle?
The Hollywood Principle, don't call us, we'll call you, describes inversion of control. Instead of application code driving the flow and calling reusable code, the framework owns the main loop, the lifecycle and the call sequence, and calls application code at extension points such as interfaces, callbacks, annotations or configuration. Servlet containers call your filters and servlets, Spring calls your @Bean methods, listeners and controller methods, and JUnit calls @Test and @BeforeEach methods. The benefit is that concerns like request handling, transactions and lifecycle are implemented once in the framework. The cost is that your code must fit the framework's model and timing, which is why understanding its lifecycle matters.
How is a design pattern different from an architecture pattern?
They differ in scope and reversibility. A design pattern such as Strategy, Adapter or Observer structures a few classes and objects to solve a local problem; you can apply or remove it within one module, often in a single refactoring. An architecture pattern such as layered, hexagonal, microservices, event-driven or CQRS structures a whole system or subsystem, defining its components, their responsibilities and how they communicate. It drives quality attributes like scalability, deployability and team autonomy, and it is expensive to reverse because many components, teams and often infrastructure depend on it. Architecture patterns are realised with many design patterns; hexagonal architecture, for example, relies on Adapter at every port.
Why does knowing the patterns inside Spring matter?
Because most surprising Spring behaviour is a pattern's consequence. @Transactional, @Cacheable, @Async and method security are implemented with proxies, so a call from one method to another in the same bean bypasses the proxy and silently skips the transaction or cache; knowing it is a Proxy tells you to move the method to another bean or call through the proxy. Default beans are singletons shared by all threads, so they must be stateless or thread-safe. Template Method and callbacks explain JdbcTemplate, and Strategy explains why customisers, converters and handler adapters are the intended extension points. Pattern knowledge turns framework magic into predictable mechanics and makes debugging much faster.
When would you wrap a library behind an adapter?
I wrap a library behind an application-owned port when replacing or upgrading it later would otherwise ripple through the code. That is the case when it is volatile, with frequent breaking API changes; vendor-specific, like a payment provider or cloud SDK; or used widely in the domain core, where its types would leak into business logic. The adapter translates types and exceptions, such as JsonProcessingException into InvalidJson, so only one class changes on migration and tests can use simple fakes. I do not wrap stable, ubiquitous libraries used at the edges, such as java.time, SLF4J or Jackson inside a controller; the extra layer adds code without reducing real risk.
How deep should framework coupling go in a service?
Ideally the framework stays at the edges. Controllers, persistence adapters, messaging listeners and configuration use Spring freely, while the domain model is plain Java with records, value objects and domain services that can be unit-tested without a Spring context. This is the hexagonal approach: it costs some mapping and wiring, but the core survives framework upgrades and stays easy to test. In practice most teams take a pragmatic middle path, allowing annotations such as @Service and @Transactional in the application layer while keeping the domain framework-free. What matters is that any deeper coupling, such as JPA annotations on domain entities, is a conscious, recorded decision rather than accidental drift.
How do you evaluate adopting a new framework?
I start by asking what control it takes and where: does it own the request lifecycle, the domain model, data access or deployment? Then I assess fit with the target architecture and quality attributes, for example startup time and memory for serverless workloads, native-image support, observability and security. I check ecosystem maturity, release cadence, licence, community and vendor backing, CVE history, and the cost of upgrades and of leaving later. Team skills and hiring matter as much as features. I prove the critical assumptions with a time-boxed spike on a real use case, measure the results, and record the decision, alternatives and exit strategy in an ADR.