Illustrated technology atlas · Java fundamentals and Spring Framework / major Spring portfolio projects
Java & Spring — Illustrated Engineering Atlas
Best practices
Depth 2: fundamentals and best practices for a working software engineer. 32 topics cover Java contracts and runtime, Framework core, Boot, web/data/security, messaging/integration/batch, Cloud, testing/observability and optional Modulith/AI/native/specialist capabilities. This is a broad mechanism atlas, not exhaustive documentation of every Spring API or cloud subproject. Java examples use non-preview constructs from modern Java (records require 16+; virtual threads 21+). Research anchors Java SE 25 and live Spring documentation retrieved 2026-10-09. The retrieved Boot system-requirements page reports Boot 4.1.1, Framework 7.0.9+, Java 17–26. Use the matching BOM and supported project generations; do not combine displayed individual project versions arbitrarily. Code snippets are illustrative and were not compiled as a complete application. Choices and improvements marked synthesis are reasoned recommendations, not measured performance results.
Research date: 2026-10-09
Part 1 · Fundamentals
Java, Framework, Boot: find the layers
Prerequisites: Start here / basic technical literacy
Which layer owns the behavior?
Layer arrows mean builds on or integrates with; they are not the path of an HTTP request. [S01][S02][S43]
Goal & mental model verified
Learn which layer owns which concern. Java supplies the language and runtime. Framework supplies the container and application infrastructure. Boot assembles and configures applications. Portfolio projects add specialized capabilities.
For a quote API, start with Java, Boot, MVC, Data and Security. Add Batch for overnight reconciliation; add messaging when asynchronous delivery is required. A portfolio is a toolbox, not a dependency shopping list.
Choose a compatible Boot generation first; let its dependency management align libraries. This reduces manual coordination but means an upgrade can change several components together.
“All Spring” is too broad for exhaustive API coverage. This atlas teaches major mechanisms and maps specialist projects; it does not document every method or cloud provider integration.
Keep this: Know the owner of a behavior before debugging its annotations.
Check yourself: Does Boot replace Framework?
No. Boot builds on Framework and automates application setup.
Part 2 · Fundamentals
Types, objects, records and domain invariants
Prerequisites: 01-ecosystem
Why can a final reference still change?
Reference identity and object state are different; defensive copies break unwanted sharing. [S03][S04][S05]
Goal & mental model verified
Separate primitive values from object references. An interface states a contract; a class implements behavior. Records express data aggregates with final component fields; they do not make referenced collections deeply immutable.
Use composition for replaceable pricing behavior and small value types for identifiers. This clarifies invariants at the cost of more explicit conversions.
A final reference can point to a mutable object. Value equality also needs a compatible hashCode; identity equality with == answers a different question.
Move invalid-state checks into construction boundaries. Test equality, null handling and defensive copies where the business invariant depends on them.
Keep this: A type should make the wrong state harder to represent.
Check yourself: Is a record containing ArrayList deeply immutable?
No. Its field reference is final, but the referenced list can remain mutable.
Part 3 · Fundamentals
Collections, generics and equality
Prerequisites: 01-ecosystem
Which collection matches the invariant?
These are contracts, not performance rankings. Implementation and workload determine cost. [S06][S07][S05]
Goal & mental model verified
Select the collection by its contract: List preserves a sequence, Set models uniqueness, Map associates keys and values. Hash-based containers rely on equality and hashing; HashMap does not promise iteration order.
A broker list preserves display order; a Set tracks unique coverage codes; a Map indexes quotes by stable QuoteId. Use a sorted map when sorted traversal is part of the requirement.
Prefer interface-typed variables and explicit generic parameters. Choose ArrayList for ordinary indexed sequences; choose a deque for queue/stack operations instead of treating one structure as universal.
Changing fields used by a key’s equality/hash after insertion can make lookup fail. Concurrent access needs a suitable concurrency strategy, not merely a different collection name.
Write down ordering, duplicate and mutation requirements before selection. Measure realistic access patterns before swapping structures for supposed performance gains.
Keep this: Collection contracts are part of your domain contract.
Check yourself: Why are mutable map keys dangerous?
Their hash/equality behavior may change after storage, so later lookup searches the wrong location.
Part 4 · Fundamentals
Lambdas, streams and honest parallelism
Prerequisites: 01-ecosystem
When does a stream actually execute?
This is a sequential pipeline. Parallel execution requires separate assumptions about side effects and workload. [S08][S11]
Goal & mental model verified
A lambda implements a functional interface. A Stream describes a computation over a source: intermediate transformations are lazy, and a terminal operation consumes the pipeline. A stream is not a reusable collection.
Use streams for clear transformations and loops for stateful control flow or early branching. Parallel execution adds coordination overhead and is useful only with appropriate work and isolation.
Side effects inside map/filter make ordering and parallel behavior difficult to reason about. A parallel stream is not a safe way to issue unbounded database calls.
Check yourself: Why does filter alone produce no result?
It is intermediate and lazy; a terminal operation such as toList triggers traversal.
Part 5 · Fundamentals
Resource ownership and decimal money
Prerequisites: 01-ecosystem
What must be explicit at a boundary?
Top: deterministic resource lifetime. Bottom: decimal policy is a separate domain decision. [S09][S10]
Goal & mental model verified
Garbage collection does not replace deterministic resource closure. AutoCloseable integrates with try-with-resources. BigDecimal models decimal arithmetic, with explicit precision/rounding decisions and scale-sensitive equals.
Worked example · illustrative, not executed synthesis
var premium = new java.math.BigDecimal("1250000.00");
var tax = premium.multiply(new java.math.BigDecimal("0.10"));
// Pair amounts with a currency and an explicit rounding policy.
try (var reader = java.nio.file.Files.newBufferedReader(path)) {
return reader.readLine();
}
Use decimal values or an explicitly specified minor-unit model for premiums. State currency, scale and rounding at business boundaries; neither approach chooses the policy for you.
BigDecimal constructed from a binary double can inherit representation artifacts. Leaking file/database resources can exhaust capacity even when heap usage appears healthy.
Define resource ownership and close scope. Exercise rounding edges and failure paths; compare 1.0 and 1.00 with the intended numeric or representation semantics.
Keep this: Memory, resources and money each need their own ownership rules.
Check yourself: Does BigDecimal.equals ignore scale?
No. compareTo can test numeric equality; equals also distinguishes scale.
Part 6 · Fundamentals
Concurrency, visibility and virtual threads
Prerequisites: 01-ecosystem
Where should concurrency be limited?
Limit the scarce downstream resource. The figure illustrates admission control, not virtual-thread scheduling internals. [S11][S12][S13]
Goal & mental model verified
Concurrency coordinates overlapping tasks. Visibility and atomicity are different: volatile can publish writes, but does not make read-modify-write increments atomic. Virtual threads make blocking I/O concurrency cheaper; they do not create CPU or database capacity.
Two workers read count=0 and both write 1: one increment is lost. AtomicInteger or a lock can protect this invariant. For quote calls, a semaphore can bound access to a scarce insurer connection.
Use virtual threads for many blocking I/O tasks when dependencies fit. Bound downstream concurrency; use workload-appropriate executors for CPU work and decide timeout/cancellation ownership.
A shared mutable singleton remains shared under virtual threads. Avoid pooling virtual threads. Pinning guidance is JDK-dependent; older synchronized pinning advice should not be repeated universally for newer JDKs.
Load-test with slow dependencies. Record pool wait, in-flight calls, CPU and tail latency; stop increasing concurrency when the constrained resource saturates.
Keep this: Cheap waiting is not unlimited throughput.
Check yourself: Does volatile count++ become atomic?
No. Reading, adding and writing remain a compound operation.
Part 7 · Fundamentals
JVM, JIT, garbage collection and diagnosis
Prerequisites: 01-ecosystem
How do execution and evidence connect?
Simplified HotSpot view. JFR includes additional event categories; GC and compilation may run concurrently. [S14][S15]
Goal & mental model verified
HotSpot starts with interpretation and adaptively compiles hot code. Garbage collection manages object memory. Flight Recorder captures runtime events for diagnosis; request latency can include CPU, allocation, locks and external waiting.
A quote endpoint’s latency rises while CPU stays modest. A recording and dependency timings may reveal contention or connection waits rather than expensive Java computation.
Choose a runtime and collector against workload and deployment limits. Compare warm steady-state behavior and startup separately; a microbenchmark rarely predicts full service latency.
Treating every pause as a GC problem can hide a slow database. Increasing heap can reduce some collection pressure while increasing footprint and changing pause behavior.
Form a hypothesis, capture JFR plus service metrics, change one factor, then repeat representative load. Keep the measurement environment and warm-up conditions explicit.
Keep this: Observe the bottleneck before tuning the JVM.
Check yourself: Can low CPU coexist with high latency?
Yes. A request may be waiting for a lock, I/O or a scarce connection.
Part 8 · Fundamentals
IoC, constructor injection and bean definitions
Prerequisites: 01-ecosystem
Who creates and connects collaborators?
Arrows distinguish construction from injecting an existing collaborator. Business calls are omitted. [S16][S20]
Goal & mental model verified
Inversion of control means the container constructs and connects collaborators. Dependency injection exposes what an object needs. Component scanning and @Bean methods are two ways to register bean definitions.
Worked example · illustrative, not executed synthesis
class QuoteService {
private final PricingPort pricing;
QuoteService(PricingPort pricing) { this.pricing = pricing; }
}
// Register QuoteService and a PricingPort implementation as beans.
Use constructor injection for required collaborators. Use @Bean for third-party objects or explicit assembly; use qualifiers when multiple candidates represent meaningful alternatives.
Field injection hides required dependencies and complicates plain construction. Circular dependencies often signal tangled responsibilities; adding lazy references can conceal the design problem.
Keep this: The container should assemble your design, not become your design.
Check yourself: Why inject an interface?
It names a replaceable contract; the container supplies the selected implementation.
Part 9 · Fundamentals
Scopes, lifecycle and thread-safe singleton design
Prerequisites: 01-ecosystem
When is a bean ready for advised calls?
Typical simplified lifecycle. Prototype destruction and manually created objects require separate ownership. [S17][S18]
Goal & mental model verified
A singleton is one instance per bean definition per container. Request scope follows a web request. The container manages initialization/destruction for managed beans, with important exceptions such as prototype destruction.
QuoteService may safely be a singleton when it stores only stable collaborators. A mutable currentTenant field would mix simultaneous requests; carry tenant context through an explicit request-level mechanism instead.
Keep shared service state immutable or deliberately synchronized. Choose scope by ownership and lifetime; request-scoped dependencies in singletons need appropriate proxies or lookup.
@PostConstruct runs during initialization, before normal proxy-mediated use. Depending on transactional advice there is unsafe. Prototype cleanup is not automatically managed like singleton destruction.
Move external startup work into a suitable lifecycle stage. Test concurrent requests and shutdown; name who owns cleanup for prototype or manually created objects.
Keep this: Scope controls lifetime; it does not grant thread safety.
Check yourself: Does singleton mean one instance across a cluster?
No. Each application container has its own singleton instances.
Part 10 · Fundamentals
AOP proxies: why annotations sometimes vanish
Prerequisites: 01-ecosystem
Which call crosses the proxy boundary?
Default Spring AOP proxy mode. A self-call does not return to the proxy; weaving can behave differently. [S19][S30]
How can an advised audit boundary become explicit?
Proposed refactoring: call another managed collaborator. Select transaction propagation intentionally; separate beans alone do not select it. [S19][S30]
Goal & mental model verified
Spring AOP commonly intercepts calls through a proxy before reaching the target. JDK interface proxies and subclass proxies have different constraints. A target calling itself does not re-enter its Spring AOP proxy.
A controller invokes quoteService.issue(): transaction advice runs. Inside the target, this.audit() bypasses proxy interception, even if audit has an advice-triggering annotation.
Place independently advised operations on separate collaborators or use an explicit programmatic boundary. This adds a visible interface but avoids magical assumptions about annotation placement.
Subclass proxies cannot advise final/private methods as overridable methods. Calling a manually constructed object also bypasses container advice. AspectJ weaving is a different mechanism.
Test behavior through the managed bean and deliberately exercise self-invocation. Inspect actual proxy type and advice ordering when multiple concerns wrap one method.
Keep this: An annotation is metadata; the invocation path makes it operational.
Check yourself: Will this.audit() start a proxy-driven new transaction?
Not in default proxy mode. The call stays within the target.
Part 11 · Fundamentals
Spring Boot: conditional assembly, not sorcery
Prerequisites: 01-ecosystem
Why was an infrastructure bean created?
Conditions vary by auto-configuration. This figure shows the main inputs, not every condition type. [S21][S02][S20]
Goal & mental model verified
Boot auto-configuration uses available classes, properties and existing beans to configure infrastructure. User definitions can cause defaults to back off. Starters assemble related dependencies; dependency management aligns versions.
Add a database driver and the relevant starter, supply connection properties, and Boot can create infrastructure beans. Define your own DataSource and the matching default may no longer apply.
A starter can activate unexpected configuration. Mixing individual Spring versions may produce linkage errors. A bean declared too broadly can accidentally replace a useful default.
Use the condition evaluation report to understand why configuration matched or failed. Record custom overrides and test upgrades against startup and representative paths.
Keep this: Auto-configuration is conditional code you can inspect.
Check yourself: Does adding a starter guarantee a working database?
No. Driver, credentials, reachable infrastructure and matching configuration still matter.
Part 12 · Applications
Configuration, profiles and secret boundaries
Prerequisites: 01-ecosystem
Which configuration value actually wins?
The complete precedence order is documented in Boot. This diagram illustrates merging, not the whole ranked list. [S22]
Goal & mental model verified
Boot combines ordered property sources into an Environment and binds structured configuration. Later/higher-precedence sources can override earlier values. Profiles select configurations; they are not a security boundary.
For pricing.timeout, define a default and an environment-specific override. Bind settings into a typed ConfigurationProperties object and validate required values instead of scattering string lookups.
Treat operational settings as external inputs and make startup fail clearly on invalid required configuration. This improves repeatability but demands disciplined deployment configuration.
An unexpected environment variable can override a checked-in value. Logging a full configuration dump can reveal secrets; a prod profile does not protect management endpoints.
Document the effective source and precedence for critical settings. Review secrets access separately and test startup with missing, malformed and overridden values.
Keep this: Debug the effective configuration, not just the file you edited.
Check yourself: Why might changing application.yml have no effect?
A higher-precedence source can supply the same property.
Part 13 · Applications
MVC request flow, DTO validation and errors
Prerequisites: 01-ecosystem
Where do request responsibilities belong?
Request-side responsibilities; responses return through MVC. Authentication filters normally run before this view. [S23][S24][S25]
Goal & mental model verified
DispatcherServlet delegates request mapping and invocation to MVC components. Validation checks request constraints. Error handling can produce ProblemDetail responses rather than leaking internal exception representations.
POST /quotes accepts a validated request DTO. The service enforces business eligibility; the repository persists. A validation failure returns a stable client error, while an internal failure gets a traceable sanitized response.
Keep controllers focused on transport and services on use-case policy. DTOs decouple API shapes from persistence; the additional mapping is an intentional boundary cost.
Bean Validation cannot prove business eligibility or authorization. Returning persistence entities can expose fields and trigger unexpected serialization-time loads.
Exercise malformed input, missing access and failure responses with HTTP tests. Keep stable problem types and correlate internal diagnostics without returning stack traces.
Keep this: Validate syntax at transport boundaries and invariants in the domain.
Check yourself: Can @Valid prove an agent owns a policy?
No. Ownership is an authorization/domain rule, not just a field constraint.
Part 14 · Applications
MVC, WebFlux and virtual threads: choose by constraints
Prerequisites: 01-ecosystem
Which I/O contract fits the execution model?
Two valid styles; virtual-thread configuration is separate from choosing MVC. No universal performance ranking is implied. [S26][S13][S44]
Goal & mental model verified
MVC supports an imperative request model; WebFlux uses non-blocking composition and reactive backpressure. Mono represents zero-or-one values and Flux a sequence. Virtual threads provide a separate way to scale blocking waits.
A JPA quote API fits MVC naturally. A streaming aggregator with non-blocking downstream clients may fit WebFlux. Wrapping a blocking JPA call in Mono does not make its I/O non-blocking.
Choose based on dependency APIs, concurrency pattern and team expertise. MVC with virtual threads can retain readable blocking code; WebFlux adds composition and scheduling discipline.
Blocking an event loop can stall unrelated requests. A reactive return type alone does not identify the server stack, and virtual threads do not remove database limits.
Prototype the same realistic workload with bounded dependencies. Compare memory, tail latency and failure handling; adopt complexity only for a measured benefit.
Keep this: Choose an execution model for your dependencies, not your fashion sense.
Check yourself: Does Mono.just(blockingCall()) defer that call?
No. The argument is evaluated immediately before Mono.just receives it.
Part 15 · Applications
Spring Data: repositories, aggregates and fetch plans
Prerequisites: 01-ecosystem
Where does repository abstraction stop?
JPA and JDBC are alternatives shown together for comparison. They do not share a persistence context. [S27][S28][S29]
Goal & mental model verified
Spring Data builds repository abstractions around store-specific behavior. JPA has persistence-context and relationship semantics; Data JDBC uses a simpler aggregate model. Derived queries reduce boilerplate without replacing query design.
A broker dashboard pages quotes by tenant and status. Use a response projection and a deliberate fetch plan instead of traversing every policy relationship during JSON serialization.
Choose JPA when its entity model helps; choose JDBC-style access when explicit aggregate operations fit. R2DBC is a non-blocking access model, not reactive JPA.
A compact repository method can still produce expensive SQL. N+1 access and loading oversized graphs should be diagnosed through executed queries and row counts, not annotation counts.
Capture SQL and query plans for representative tenant sizes. Validate pagination and fetch plans together; compare query count and end-to-end latency after changes.
Keep this: Repository convenience does not cancel database physics.
Check yourself: Is R2DBC just JPA returning Mono?
No. It is a different non-blocking relational access model.
Part 16 · Applications
Transactions, rollback and propagation boundaries
Prerequisites: 01-ecosystem
What does the transaction actually protect?
One database transaction. Reactive transaction context, distributed coordination and propagation need separate treatment. [S30][S31][S19]
Goal & mental model verified
@Transactional defines an advised transaction boundary. Default rollback typically covers unchecked exceptions and Error; checked-exception behavior can be customized, including global defaults. Read-only is generally a hint, not a universal write prohibition.
An issuePolicy service method updates the quote and inserts the policy in one database transaction. An email call is not rolled back just because the database transaction fails.
Keep boundaries around coherent database use cases and avoid long remote waits while holding connections. Choose explicit rollback policy and account for propagation and pool capacity.
Self-invocation can bypass advice. Catching and swallowing a failure can prevent expected rollback. REQUIRES_NEW can require an additional connection and therefore worsen pool pressure.
Verify rollback through the proxied bean with real persistence. Exercise checked exceptions, concurrent conflicts and external side-effect failure separately.
Keep this: A local transaction is a precise boundary, not a universal undo button.
Check yourself: Will a database rollback unsend an email?
No. External effects need separate coordination such as durable events and idempotent handling.
Part 17 · Applications
Security filters, JWT and domain authorization
Prerequisites: 01-ecosystem
Where does identity become resource access?
Servlet resource-server example. Token verification and per-resource authorization are separate decisions. [S32][S33]
Goal & mental model verified
SecurityFilterChain applies authentication and authorization to matching servlet requests. A resource server verifies bearer tokens and derives authorities; accepting a decoded token is not equivalent to verifying it.
With Keycloak as issuer, validate the JWT signature, issuer and applicable claims. Convert claims deliberately, then check that the authenticated agent may access the requested tenant’s policy.
Keep identity-provider responsibilities separate from application resource rules. Token authorities can express permissions, but row/tenant ownership still belongs in enforceable application logic.
A valid token can target another tenant’s resource. Incorrect matcher order can leave a route under the wrong filter chain. Configure audience requirements rather than assuming they are always enforced automatically.
Test anonymous, expired, wrong-issuer, wrong-audience and cross-tenant requests. Inspect granted authorities and deny access consistently at the relevant resource boundary.
Keep this: Authentication names the caller; authorization constrains the action.
Check yourself: Does a valid JWT authorize every policy ID?
No. The application must enforce permission and ownership for that resource.
Part 18 · Applications
Sessions, browser credentials and CSRF
Prerequisites: 01-ecosystem
How do sessions survive replica changes?
A shared session store is an option, not mandatory for every app. CSRF still depends on credential transport. [S34][S35]
Goal & mental model verified
Spring Session externalizes session handling through shared implementations. CSRF is about a browser automatically attaching credentials to a forged request; it is not solved by calling the API “REST.”
A browser broker portal uses a session cookie and CSRF token on state-changing requests. Two app replicas can access shared session state instead of relying solely on one process’s memory.
Choose session or bearer-token flows from client and threat requirements. Shared sessions improve replica mobility but introduce storage availability, expiration and serialization concerns.
Stateless does not automatically mean CSRF-safe if browser credentials are still sent automatically. CORS policy is not a replacement for CSRF protection.
Exercise expiration, logout, replica changes and state-changing cross-origin requests. Confirm credential behavior and token checks for the actual browser flow.
Keep this: Understand how credentials travel before disabling protections.
Check yourself: Why can a cookie API need CSRF protection?
Because a browser can automatically attach the cookie to a forged request.
Part 19 · Applications
Caching: keys, freshness and invalidation
Prerequisites: 01-ecosystem
What happens on a cache hit and miss?
Cache-aside conceptual flow. TTL, stampede control and update consistency are provider/application choices. [S36][S37]
Goal & mental model verified
Spring’s cache abstraction delegates storage to a backing implementation. @Cacheable can reuse results; eviction and updates define freshness behavior. An annotation alone does not define TTL or make a distributed cache consistent.
For a policy summary, use a key containing tenant and policy ID. Evict or update affected summaries after successful policy changes under an explicitly chosen consistency policy.
Cache expensive, repeatedly read data with an acceptable staleness budget. Choose local versus shared storage by consistency, footprint and latency requirements.
Missing tenant identity in a key can reuse another tenant’s data. Default proxy-based caching also has self-invocation limits. Cache invalidation and database commit ordering need care.
Keep this: Every cache needs an owner, a key contract and a freshness promise.
Check yourself: Does @Cacheable itself choose a TTL?
No. Expiration is generally a backing-cache/provider concern.
Part 20 · Applications
Kafka, AMQP and Pulsar: delivery boundaries
Prerequisites: 01-ecosystem
Where can a duplicate become a second effect?
Application-level idempotency sketch; broker protocols and acknowledgement semantics differ. [S38][S39][S59]
Goal & mental model verified
Spring messaging integrations connect templates and listeners to broker-specific behavior. Kafka transactions can cover a read/process/write Kafka sequence; this does not universally make external database or HTTP side effects exactly once.
A PolicyIssued event may be redelivered. A consumer records a stable event ID and performs its database change atomically where feasible, avoiding a second business effect on retry.
Use messaging to decouple timing and absorb work under explicit delivery rules. Choose the broker by ordering, routing, retention and operational requirements rather than a universal winner.
Acknowledging before durable processing can lose work; acknowledging afterward can allow redelivery. “Exactly once” must name the protected boundary, and poison-message retries need a finite outcome.
Test a crash after the effect but before acknowledgement. Track lag, retries and dead-letter handling; verify deduplication with the actual transaction and broker configuration.
An inbound insurer file adapter emits messages. A transformer normalizes records; a router sends valid records to underwriting and malformed ones to a review destination.
Use integration flows when multiple protocols and routing rules need explicit composition. Prefer a direct service call for a simple local interaction; each extra channel adds behavior to understand.
Assuming every channel is asynchronous misreads the execution model. A slow endpoint can block a synchronous flow; unbounded asynchronous handoff can merely move overload into memory.
Choose and document channel capacity and error handling. Simulate slow consumers and malformed messages; inspect queue depth, rejection behavior and recovery ownership.
Keep this: A diagrammed integration flow still needs a capacity model.
Check yourself: Is every Spring Integration channel async?
No. Channel implementation determines handoff and threading behavior.
Part 22 · Applications
Spring Batch: chunk commits and safe restarts
Prerequisites: 01-ecosystem
What survives a batch failure?
Conceptual chunk and metadata responsibilities; transaction participation depends on actual repository/resource configuration. [S41][S42]
Which work remains after a failed chunk?
Conceptual restart boundary. Stable input, restartable components and durable metadata are required; external effects need idempotency. [S41][S42]
Goal & mental model verified
A Job contains Steps; chunk-oriented processing reads items, optionally processes them, and writes a chunk at a commit interval. Job/Step execution metadata distinguishes logical instances, attempts and restart state.
An overnight reconciliation job handles rows in chunks. If a later chunk fails, previously committed chunks remain; restart behavior depends on metadata and the components’ restart contracts.
Choose chunk size against transaction duration, memory and write cost. Use stable job parameters and restartable input ordering; external side effects still need deliberate idempotency.
A restarted job is not equivalent to replaying every row from zero. Changing input between attempts or losing execution metadata can break assumptions and duplicate effects.
Fail a run deliberately halfway through. Verify which chunks commit and which rows resume; measure processing rate, skip/retry counts and resource contention.
A gateway routes broker requests; a service calls an insurer using an HTTP client. On Kubernetes, evaluate whether platform discovery/configuration already supplies the needed capability before adding another layer.
Use RestClient for blocking calls, WebClient for reactive composition, or HTTP Service Clients for declarative interfaces. OpenFeign remains feature-complete; maintainers recommend HTTP Service Clients for new evolution.
Cloud abstractions do not remove network failures or distributed consistency problems. The old docs/current Cloud URL can expose an old train; match documentation to the actual version.
Map each component to one operational need. Verify Boot/Cloud compatibility and timeout behavior, then remove duplicate discovery, routing or configuration responsibilities.
Keep this: Distribute deliberately; pay the network bill consciously.
Check yourself: Should every Kubernetes service add Eureka?
No. First determine whether native discovery already meets the requirement.
Part 24 · Applications
REST, GraphQL, gRPC and hypermedia
Prerequisites: 01-ecosystem
How does GraphQL avoid repeated owner loads?
One GraphQL mechanism. REST and gRPC are contract alternatives, not stages of this flow. [S46][S47][S62]
Goal & mental model verified
API styles expose different contracts. REST centers resources and HTTP semantics; GraphQL executes a schema-selected field graph; gRPC defines RPC services/messages. Spring HATEOAS helps express links and affordances in representations.
A broker dashboard asks GraphQL for policies and owners. Request-scoped DataLoader batching can collect owner IDs, reducing repeated lookups without turning the API into one unrestricted database query.
GraphQL can hide N+1 fetching and expensive query shapes. Changing transport does not supply per-field/resource permissions. Hypermedia links should reflect permitted actions rather than grant authority themselves.
Measure query cost and downstream calls. Enforce workload limits and compatible contract evolution; keep authentication, authorization and deadline behavior explicit for each transport.
Keep this: Transport changes the contract shape; business rules still need enforcement.
Check yourself: Does DataLoader guarantee one query for the entire request?
No. It batches compatible loads; actual queries depend on keys, resolvers and backing implementation.
Part 25 · Best practices
Tests that catch wiring, persistence and HTTP failures
Prerequisites: 01-ecosystem
Which test can prove the behavior?
These are complementary test scopes, not a required execution chain. Full-context does not automatically mean real external services. [S48][S25]
Goal & mental model verified
Boot supports focused test slices and full-context tests. A slice loads a constrained subset; a full application test exercises broader wiring. Different scopes answer different questions.
Test pricing rules with ordinary objects; validate controller errors with an MVC slice; exercise repository mappings against a representative database; reserve full application tests for critical integration flows.
Pick the cheapest test that can detect the targeted failure. Real infrastructure tests cost time but can expose SQL, serialization and configuration problems that mocks cannot.
A mocked repository cannot establish real transaction rollback. Test-managed transactions can also conceal commit-time behavior; choose the test boundary to match the production failure.
Build a few high-value scenarios: authorization denial, conflict/rollback, malformed requests and startup misconfiguration. Track failure relevance rather than chasing coverage percentage alone.
Observe quote latency by route template and outcome. Correlate a slow request through a trace; inspect database wait and JVM events instead of placing every policy ID in a metric label.
Design signals around an operational question: errors, latency, saturation and dependency behavior. Expose only required management capabilities and restrict sensitive endpoints.
Health is not one universal status. Putting shared external outages into liveness can trigger needless restarts; readiness dependencies should reflect whether this instance can serve useful traffic.
Simulate slow insurer calls and database outages. Confirm traces propagate and alerts identify the constrained boundary; measure signal cardinality and retention costs.
Keep this: A dashboard earns its place by answering a failure question.
Check yourself: Should policyId be a metric label?
Usually no. Its high cardinality can create excessive time series; use logs/traces appropriately.
Part 27 · Best practices
Timeouts, retries, bulkheads and scheduled work
Prerequisites: 01-ecosystem
When does retry improve reliability?
Policy sketch, not annotation execution order. Retry and transaction nesting must be chosen deliberately. [S51][S52][S53]
Goal & mental model verified
Framework 7 has core retry and concurrency-limit facilities with explicit enabling. Circuit breakers address repeated dependency failure. Executors run tasks; schedulers decide when to trigger them. @Async commonly relies on proxy invocation.
An insurer times out after receiving a quote request. Retry only under an idempotency contract; otherwise a successful but lost response can lead to duplicate work.
Set an overall deadline, per-attempt budgets and bounded concurrency. Retry selected transient failures with backoff/jitter; a fallback must preserve honest business semantics.
Retries amplify overload. The new Framework retry API and older Spring Retry have different packages/settings. Scheduled work can run on every replica unless coordination is explicitly designed.
Inject latency, partial failure and response loss. Measure total attempts and deadline breaches; verify limits protect dependencies and that failed work has an observable final outcome.
Keep this: Resilience protects a budget; it does not promise eventual success.
Check yourself: Is retrying every failure always safer?
No. Permanent failures waste capacity, and non-idempotent operations may repeat side effects.
Part 28 · Further improvements
Modulith: enforce domain boundaries before distribution
Prerequisites: 01-ecosystem
What is a legitimate module dependency?
Logical domain boundaries inside one process. The figure does not imply separate services or databases. [S54]
Goal & mental model verified
Spring Modulith derives application modules from a Boot application structure and supports boundary verification. Modules expose APIs/named interfaces while keeping implementation details internal.
Separate quotes, policies and billing into modules. Quotes calls the published policy API or emits a domain event; it should not reach directly into billing’s internal persistence package.
Use a modular monolith when one deployment can satisfy operational needs. This preserves local interactions while clarifying boundaries, but independent scaling/deployment may later justify extraction.
Package names alone cannot prevent coupling if public internals are widely imported. Replacing calls with events changes consistency and failure semantics; it is not automatically an improvement.
Run module dependency verification and focused module tests. Track coupling and change ownership before extracting a service; choose extraction based on an actual deployment constraint.
Keep this: Make boundaries real before making them remote.
Check yourself: Does Modulith require microservices?
No. It is designed to structure modules within a Spring Boot application.
Part 29 · Further improvements
Spring AI: retrieval and tools with controlled authority
Prerequisites: 01-ecosystem
Who owns authority when the model calls a tool?
Proposed guarded design grounded in Spring AI mechanisms; application authorization is not supplied by retrieval itself. [S55][S56][S57]
Goal & mental model verified
Spring AI offers model, embedding, vector-store and tool abstractions. RAG retrieves context for generation; tools let the model request application capabilities. The application executes those capabilities and owns the resulting authority.
An assistant retrieves permitted policy wording and drafts an explanation. A requested issuePolicy tool must still validate the agent, tenant, eligibility and required approval in ordinary application code.
Use the framework to integrate models, not to outsource business authorization. Retrieval improves grounding under suitable data and chunking, but cannot guarantee factual correctness.
Retrieved text and model output can be untrusted. A tool exposed to a model may have real side effects; prompt wording alone cannot enforce access rules or prevent repeated operations.
Build a small evaluation set covering wrong-tenant retrieval, unsupported answers and unsafe tool requests. Trace sources and measure correctness, latency and cost before broadening capabilities.
Keep this: The model can suggest an action; the application must authorize it.
Check yourself: Does retrieved context make every answer true?
No. Retrieval can be incomplete or wrong, and generation can still misinterpret it.
Part 30 · Further improvements
AOT and native images: optimize the deployment case
Prerequisites: 01-ecosystem
What moves from runtime into the build?
Simplified native-image pipeline; ahead-of-time processing and compilation are distinct steps. [S58][S14]
Goal & mental model verified
Native images compile under a closed-world assumption. Spring AOT analyzes application assembly ahead of runtime and generates supporting assets. Dynamic bean-graph changes and reflection/resource access can require different handling.
A rarely invoked quote utility may benefit from reduced startup overhead. A long-running service may favor JVM JIT behavior; measure both against the actual deployment workload.
Evaluate native deployment when startup and footprint constrain the product. Accept build complexity and compatibility work only when the measured operational benefit matters.
A successful JVM run does not prove native compatibility. Runtime configuration that changes which beans exist can conflict with build-time assembly assumptions.
Test the native artifact itself, including serialization, resources and integrations. Compare cold start, steady-state throughput, memory and build time under equivalent conditions.
Keep this: Optimize the lifecycle that dominates your real cost.
Check yourself: Can native mode freely rebuild its bean graph from runtime properties?
No. AOT/closed-world constraints limit runtime changes to application assembly.
Part 31 · Applications
Specialist Spring projects: choose the protocol
Prerequisites: 01-ecosystem
Which specialist fits the requirement?
These are alternatives for distinct needs. REST Docs, Shell and Web Flow add other focused capabilities. [S01][S60][S61][S62]
Goal & mental model verified
The portfolio includes specialist tools: Web Services for contract-first SOAP, LDAP for directory access, HATEOAS for links, REST Docs for tested documentation, Shell for CLIs and Web Flow for controlled navigation.
Use SOAP integration for an insurer’s fixed XML contract; LDAP when identity data lives in a directory. Use REST Docs to connect documented API examples to tests rather than maintaining unrelated examples manually.
Match the external contract first, then select the adapter/framework. Specialized projects reduce plumbing but add protocol and lifecycle knowledge; they are not mandatory for every Boot application.
An older tutorial may reference an Attic project as if it were a current default. Attic status requires checking migration/replacement guidance; it is not a blanket statement about every existing deployment.
Inventory specialty protocols and historical dependencies. Read each selected project’s current support and migration notes before adoption or replacement.
Keep this: Use the smallest set of projects that honors your actual contracts.
Check yourself: Should a REST-only service add Spring Web Services?
Usually no. Its purpose is SOAP/XML-oriented service integration.
Part 32 · Further improvements
A production learning loop and migration compass
Prerequisites: 01-ecosystem
How does a better service become a proven service?
This is an engineering synthesis, not a claim of measured performance gains for the example service. [S02][S43][S48][S49][S01]
Goal & mental model verified
A compatible runtime and framework baseline makes behavior reproducible. Verification spans business rules, framework wiring and operating signals. Portfolio status and compatibility documentation should guide version-sensitive choices.
Baseline a quote service, add one change such as a fetch plan or concurrency limit, and repeat representative load and failure scenarios. Keep the result only when it improves the target without breaking invariants.
Start with one well-observed modular service. Add distributed components, caching, reactive execution or native compilation when a specific constraint justifies the complexity.
Installing every project is not ecosystem mastery. A change that lowers average latency while worsening failure recovery or tenant isolation may be a regression.
Study in order: Java contracts → container/proxies → HTTP/data/security → failure handling → optional capabilities. Record expected benefit, measurement and rollback criteria for every production change.
Capability map; follow the topic sheets for mechanisms and trade-offs. [S43][S45]
Capability / project
Responsibility
Study next
Config / Bus
Configuration / change distribution
12 / 23
Gateway / LoadBalancer
Routing / instance selection
23
Netflix Eureka / Consul / Zookeeper
Discovery integrations
Avoid duplicate platform discovery
Kubernetes
Platform integration
23
Circuit Breaker
Failure isolation abstraction
27
Stream
Broker-bound application messaging
20
Function / Task
Function model / finite workloads
22–23
Vault
Secrets integration
12
OpenFeign
Declarative HTTP integration; feature-complete
HTTP Service Clients guidance
Specialist and adjacent capabilities
Capability map; follow the topic sheets for mechanisms and trade-offs. [S01][S60][S61][S62]
Capability / project
Responsibility
Study next
Security / Session
Identity, access rules and session handling
17–18
Batch / Integration
Bulk jobs / message routing
21–22
Kafka / AMQP / Pulsar
Broker-specific templates and listeners
20
GraphQL / gRPC / HATEOAS
Schema graphs / RPC / resource links
24
Modulith / AI
Module boundaries / model integration
28–29
REST Docs / Shell / Web Flow
Tested API docs / CLIs / navigation flows
31
Web Services / LDAP
SOAP / directory integration
31
Micrometer / Reactor
Observability / reactive foundation
26 / 14
Attic examples
Sleuth, Retry, Cloud Contract, Data Flow, Statemachine, Authorization Server appear in retrieved Attic list
Check project-specific migration and support guidance
This compass maps responsibilities, not interchangeable APIs or a promise that every listed project shares the same support lifecycle. Core mechanisms receive dedicated lessons; niche protocols and provider-specific integrations remain further-reading branches.