Java Microservices Complete Learning Notes Alacriti-1
Java Microservices Complete Learning Notes Alacriti-1
Goal: Make you interview-ready for Senior Software Engineer roles requiring Core Java, REST API development,
microservices, distributed systems thinking, payment-domain flows, performance, reliability, and security.
Interview reality: You will be evaluated on (1) correctness, (2) clarity, (3) trade-offs, and (4) production mindset
(failures, retries, idempotency, monitoring, data consistency). These notes keep that mindset throughout.
Core Java OOP, static/final, immutability, exceptions, collections internals, generics, streams
REST APIs resource modeling, status codes, validation, pagination, versioning, idempotency
Spring Boot DI, configuration, validation, transactions, error handling, layered architecture
Data consistency ACID vs BASE, isolation levels, outbox, dedup, exactly-once illusions
15 15. Security for APIs + Payment Systems (JWT, encryption concepts, PCI mindset)
Java is pass-by-value for everything. For primitives, the value is the primitive. For objects, the value is the
reference (a pointer-like value). This is why you can mutate the same object inside a method, but you cannot
make the caller’s variable point to a new object.
class PassByValueDemo {
static void rebind(StringBuilder sb) {
sb = new StringBuilder("NEW"); // only local variable changes
}
static void mutate(StringBuilder sb) {
[Link]("!"); // mutates shared object on heap
}
public static void main(String[] args) {
StringBuilder a = new StringBuilder("Hi");
rebind(a);
mutate(a);
[Link](a); // Hi!
}
}
Checkpoint: Explain in one sentence why rebind() does not change the caller’s reference but mutate() changes
the caller’s object state.
static means the member belongs to the class, not to a particular object instance. There is one copy per
classloader.
• Static fields are shared across all instances: useful for constants and shared caches (but be careful with
memory leaks).
• Static methods are not polymorphic: they are resolved by the reference type at compile time (method
hiding).
• Static initialization runs when class is first loaded (lazy, unless forced).
Interview line: “Static methods are resolved at compile-time; instance methods are dynamically dispatched.”
To make an object truly immutable, you must design it so that state cannot change, and you do not expose
internal mutable references.
Immutable objects are inherently thread-safe: no thread can observe partial mutations because there are no
mutations.
import [Link];
• Use immutable types for: identifiers, money amounts, timestamps, domain events, request/response
DTOs.
• Common mistake: returning internal List/Map directly. Return unmodifiable copies.
• Use [Link] (Instant, LocalDate) instead of [Link].
Collections such as HashMap/HashSet depend on correct equality and hashing. If you break these, you get
“impossible” bugs (lost entries, duplicates).
• equals must be: reflexive, symmetric, transitive, consistent; handle null.
• hashCode: if [Link](b) then [Link] == [Link].
• Never use mutable fields in hashCode if object is used as HashMap key.
Encapsulation is not “private fields + getters”. It is: hide state and expose behavior that keeps state valid.
class Account {
private long balanceCents;
Notice: we did not give a setter for balance; we force all updates through debit/credit so rules are enforced.
Abstraction is about depending on what you need, not on details. In services, you want core domain logic
to depend on interfaces, not on framework classes.
interface PaymentGateway {
GatewayResponse authorize(Payment payment);
GatewayResponse capture(Payment payment);
}
Senior hint: In interviews, when you introduce an interface, also say what it enables: testability, swapping
vendors, failure simulation, and clean separation.
Inheritance creates tight coupling and fragile base classes. Composition models variability safely.
interface FeePolicy { long fee(long amountCents); }
class PricingService {
private final FeePolicy policy;
PricingService(FeePolicy policy){ [Link] = policy; }
Polymorphism lets you add new behavior without rewriting existing code (Open/Closed). In payment
domain: different payment methods, different fraud checks, different routing rules.
• Prefer polymorphism when behavior branches would otherwise become huge if/else ladders.
• Keep implementations small and cohesive; avoid “God strategy”.
• Make selection logic explicit (router/factory).
3. SOLID + Clean Code Applied to Services
SOLID is a set of heuristics to keep code change-friendly. The interviewer wants to see that you can build
systems that survive constant requirement changes.
In microservices, a common anti-pattern is mixing HTTP logic + DB + payment gateway calls inside one
controller method. Split layers: Controller → Application service → Domain → Repositories/Gateways.
// Controller: HTTP boundary only
@RestController
class PaymentController {
private final PaymentAppService app;
PaymentController(PaymentAppService app){ [Link] = app; }
@PostMapping("/payments")
PaymentResult create(@RequestHeader("Idempotency-Key") String key,
@Valid @RequestBody CreatePaymentRequest req) {
return [Link](key, req);
}
}
Open/Closed: you add a new payment method without changing the orchestration code.
• LSP: If a method accepts PaymentGateway, any implementation must obey the contract (timeouts,
idempotency assumptions, error types).
• ISP: Prefer small interfaces (PaymentAuthorizer, PaymentCapturer) if clients do not need full gateway
surface area.
4. Exceptions and Error Modeling
In payment microservices, error handling is not a side topic. It defines correctness and user trust.
Inside Java code, throw exceptions to signal failure. At HTTP boundaries, convert failures into stable error
responses with consistent codes.
• Separate domain errors (invalid state, insufficient funds) from technical errors (DB down, gateway
timeout).
• Domain errors usually map to 4xx/409; technical errors map to 5xx (or 503).
• Never leak stack traces to clients.
@RestControllerAdvice
class Errors {
@ExceptionHandler([Link])
ResponseEntity<ErrorResponse> domain(DomainException ex, HttpServletRequest req){
return [Link](409).body(new ErrorResponse([Link](), [Link](), [Link]("X-C
}
}
Checkpoint: If the gateway times out, do you return “FAILED”? Why is “UNKNOWN/PENDING” often better?
5. Collections: From Usage to Internals
Interviews love collections because it reveals whether you understand performance and correctness beyond
writing code that “works”.
High-level flow for get(key): compute hash → find bucket index → scan chain or tree → equals() match →
return value.
• Capacity is power-of-two; index computed via (n-1) & hash (fast).
• Collisions store entries in bucket chain; may treeify to red-black tree after threshold.
• Resize re-bins entries; can be expensive; use initial capacity when you can estimate size.
• Hash flooding can degrade performance; modern JDK mitigates using tree bins.
Generics are invariant: List is not List. Use wildcards. PECS: Producer Extends, Consumer Super.
static void readNumbers([Link]<? extends Number> nums) { // Producer
for (Number n : nums) [Link](n);
}
static void addIntegers([Link]<? super Integer> dst) { // Consumer
[Link](1); [Link](2);
}
• Generic type info is erased at runtime: you cannot do new T(), instanceof List.
• Prefer explicit DTOs over Map for APIs (safer, clearer).
Streams are great for transformations but can reduce clarity if overused. Do not hide business rules behind
long stream pipelines.
var counts =
[Link]("A","B","A","C","A").stream()
.collect([Link](x -> x,
[Link]()));
Java prevents dangling pointers, but you can still leak by keeping references reachable.
• Static caches that grow without eviction.
• Listener lists not cleaned.
• ThreadLocal usage in thread pools without remove().
• Large maps/lists holding old data due to not clearing references.
class LeakyCache {
private static final [Link]<String, byte[]> CACHE = new [Link]<>();
static void add(String k) { [Link](k, new byte[10_000_000]); } // unbounded growth
}
• Start with symptoms: latency percentiles, throughput, error rate, CPU, memory, GC pause time.
• Form hypotheses: allocation rate too high? DB pool saturated? thread pool exhausted?
• Use tools: thread dump, heap dump, JFR/Flight Recorder, metrics dashboards.
• Fix root causes: reduce allocations in hot paths, bound caches, add timeouts, optimize DB queries.
8. Concurrency Masterclass
Concurrency is a top filter for senior roles. You must explain not just APIs, but the Java Memory Model:
visibility, ordering, and atomicity.
• Interruption is cooperative: you signal a thread to stop; it checks the interrupt flag.
• Best practice: when catching InterruptedException, restore flag and stop work.
try {
[Link](1000);
} catch (InterruptedException e) {
[Link]().interrupt(); // restore
return;
}
Without synchronization, one thread’s writes may not be visible to another. JMM defines happens-before
rules that guarantee visibility and ordering.
• volatile write happens-before subsequent volatile read of same variable.
• synchronized unlock happens-before subsequent lock on same monitor.
• [Link] happens-before actions in started thread; join happens-before after join returns.
class SafePublishExample {
private volatile boolean ready = false;
private int data;
• volatile gives visibility/order but not atomic compound operations like ++.
• synchronized gives mutual exclusion + visibility, but can reduce concurrency if overused.
• AtomicInteger uses CAS for atomic updates; good for counters and simple state.
class Counter {
private final [Link] c = new [Link]
int inc(){ return [Link](); }
}
8.4 Executors: correct concurrency in services
In production services, creating raw threads is uncommon. Use executors to bound concurrency and
manage resources.
var pool = [Link](16);
try {
var f = [Link](() -> "ok");
String r = [Link](300, [Link]);
} finally {
[Link]();
}
Checkpoint: Explain why deadlocks are more likely when you call external services inside synchronized blocks.
9. REST API Design (Interview Grade)
REST design is not about annotations. It is about resource modeling, correctness, reliability, and client
safety (idempotency).
Reject invalid requests early using bean validation. Keep domain invariants strict even if validation exists at
controller level.
If the client retries a POST due to timeout, your system must not create duplicates or double-charge. Use
Idempotency-Key.
class IdempotencyStore {
[Link]<CachedResponse> find(String key) { return [Link](); }
void save(String key, String requestHash, CachedResponse resp) { /* persist */ }
}
• Store key + request hash + response; if same key different hash → 409 conflict.
• Return the same response for repeated calls with same key.
• Apply idempotency for command endpoints: create payment, capture, refund.
10. Spring Boot Production Basics
Spring Boot helps you build services quickly, but interviews test whether you understand architecture:
layering, DI, configuration, transactions, and error handling.
@Transactional gives atomicity inside one database. It does not make remote calls atomic.
@Service
class PaymentRepoService {
private final PaymentRepository repo;
PaymentRepoService(PaymentRepository repo){ [Link] = repo; }
@Transactional
public PaymentEntity create(PaymentEntity e){ return [Link](e); }
}
Checkpoint: Explain why @Transactional does not solve distributed consistency between Payment and Order
services.
11. Microservices Architecture
Define services around business capabilities. Payment domain naturally separates: Payments, Orders,
Ledger, Reconciliation, Fraud.
• Avoid shared databases across services; integrate via APIs/events.
• Each service owns its data and publishes events when state changes.
• Keep APIs backward compatible; treat them as contracts.
• Sync (HTTP): easier but can cause cascading failures if dependencies slow down.
• Async (events): decouples; supports retries; introduces eventual consistency.
• Hybrid: commands sync, events async.
Without timeouts, your service can hang. Without bounded retries, your service can amplify failures (retry
storms).
• Set connect + read timeouts on every outbound call.
• Retry only transient failures (timeouts, 503).
• Use exponential backoff + jitter; cap retries.
• Use circuit breaker to fail fast when dependency is unhealthy.
class Backoff {
static long ms(int attempt){
long base = (long) [Link](2, attempt) * 100L;
long jitter = (long)([Link]() * 50L);
return [Link](base + jitter, 2000L);
}
}
• Atomicity: payment + ledger entry commit together (in one service DB).
• Consistency: invariants hold (no double refund).
• Isolation: concurrent updates don’t corrupt balances.
• Durability: once committed, data persists.
In microservices, you usually keep local ACID but accept global BASE (eventual consistency). You
compensate instead of doing distributed 2PC.
To reliably publish events when DB transaction commits, store events in an outbox table in the same
transaction and publish asynchronously.
-- In the same DB transaction:
INSERT INTO payments(...);
INSERT INTO outbox(event_id, event_type, payload_json, created_at) VALUES (...);
• Choreography: services react to events; no central controller; can become hard to reason about.
• Orchestration: a coordinator commands steps and triggers compensations; clearer for complex flows like
payments.
If you timed out calling the gateway, you do not know whether it authorized or not. Treat the state as
PENDING, store gateway correlation data, and reconcile later by querying gateway or processing settlement
files.
Checkpoint: Explain why “fail fast” is not always correct for payments; sometimes “pending + reconcile” is safer.
15. Security for APIs + Payment Systems
• Measure p95/p99 latency; track GC pauses; monitor thread pools and DB pools.
• Test failure modes: slow gateway, gateway down, DB latency spikes.
17. System Design Walkthrough: Payment Service
End-to-End
class Payment {
private PaymentState state = [Link];
void authorizeApproved() {
if (state != [Link]) throw new IllegalStateException("bad transition");
state = [Link];
}
void capture() {
if (state != [Link]) throw new IllegalStateException("must authorize first");
state = [Link];
}
void refund() {
if (state != [Link] && state != [Link]) throw new IllegalStateException("n
state = [Link];
}
}
• 1. Explain HashMap internals including collisions and resize. (give a concrete example and 2 trade-offs).
• 2. Explain ConcurrentHashMap and why it forbids nulls. (give a concrete example and 2 trade-offs).
• 3. Explain volatile vs synchronized with a bug example. (give a concrete example and 2 trade-offs).
• 4. Explain happens-before and why visibility matters. (give a concrete example and 2 trade-offs).
• 5. Design an idempotent POST endpoint for payments. (give a concrete example and 2 trade-offs).
• 6. Explain ACID isolation levels using refund/double-spend examples. (give a concrete example and 2
trade-offs).
• 7. Explain Outbox pattern and why it exists. (give a concrete example and 2 trade-offs).
• 8. Design saga orchestration with compensations. (give a concrete example and 2 trade-offs).
• 9. Explain unknown outcomes with payment gateways and reconciliation. (give a concrete example and 2
trade-offs).
• 10. Explain GC pauses and how they affect latency. (give a concrete example and 2 trade-offs).
• 11. Explain Java memory leak patterns and how to diagnose them. (give a concrete example and 2
trade-offs).
• 12. Explain why constructor injection improves testability. (give a concrete example and 2 trade-offs).
• 13. Explain @Transactional limitations in microservices. (give a concrete example and 2 trade-offs).
• 14. Explain retries with backoff+jitter and retry storms. (give a concrete example and 2 trade-offs).
• 15. Explain circuit breaker behavior and tuning. (give a concrete example and 2 trade-offs).
• 16. Explain API error modeling and consistent error codes. (give a concrete example and 2 trade-offs).
• 17. Explain secure API design for payments. (give a concrete example and 2 trade-offs).
• 18. Explain the payment lifecycle: auth, capture, settlement, reconciliation, chargebacks. (give a concrete
example and 2 trade-offs).
• 19. Explain HashMap internals including collisions and resize. (give a concrete example and 2
trade-offs).
• 20. Explain ConcurrentHashMap and why it forbids nulls. (give a concrete example and 2 trade-offs).
• 21. Explain volatile vs synchronized with a bug example. (give a concrete example and 2 trade-offs).
• 22. Explain happens-before and why visibility matters. (give a concrete example and 2 trade-offs).
• 23. Design an idempotent POST endpoint for payments. (give a concrete example and 2 trade-offs).
• 24. Explain ACID isolation levels using refund/double-spend examples. (give a concrete example and 2
trade-offs).
• 25. Explain Outbox pattern and why it exists. (give a concrete example and 2 trade-offs).
• 26. Design saga orchestration with compensations. (give a concrete example and 2 trade-offs).
• 27. Explain unknown outcomes with payment gateways and reconciliation. (give a concrete example and
2 trade-offs).
• 28. Explain GC pauses and how they affect latency. (give a concrete example and 2 trade-offs).
• 29. Explain Java memory leak patterns and how to diagnose them. (give a concrete example and 2
trade-offs).
• 30. Explain why constructor injection improves testability. (give a concrete example and 2 trade-offs).
• 31. Explain @Transactional limitations in microservices. (give a concrete example and 2 trade-offs).
• 32. Explain retries with backoff+jitter and retry storms. (give a concrete example and 2 trade-offs).
• 33. Explain circuit breaker behavior and tuning. (give a concrete example and 2 trade-offs).
• 34. Explain API error modeling and consistent error codes. (give a concrete example and 2 trade-offs).
• 35. Explain secure API design for payments. (give a concrete example and 2 trade-offs).
• 36. Explain the payment lifecycle: auth, capture, settlement, reconciliation, chargebacks. (give a concrete
example and 2 trade-offs).
• 37. Explain HashMap internals including collisions and resize. (give a concrete example and 2
trade-offs).
• 38. Explain ConcurrentHashMap and why it forbids nulls. (give a concrete example and 2 trade-offs).
• 39. Explain volatile vs synchronized with a bug example. (give a concrete example and 2 trade-offs).
• 40. Explain happens-before and why visibility matters. (give a concrete example and 2 trade-offs).
• 41. Design an idempotent POST endpoint for payments. (give a concrete example and 2 trade-offs).
• 42. Explain ACID isolation levels using refund/double-spend examples. (give a concrete example and 2
trade-offs).
• 43. Explain Outbox pattern and why it exists. (give a concrete example and 2 trade-offs).
• 44. Design saga orchestration with compensations. (give a concrete example and 2 trade-offs).
• 45. Explain unknown outcomes with payment gateways and reconciliation. (give a concrete example and
2 trade-offs).
• 46. Explain GC pauses and how they affect latency. (give a concrete example and 2 trade-offs).
• 47. Explain Java memory leak patterns and how to diagnose them. (give a concrete example and 2
trade-offs).
• 48. Explain why constructor injection improves testability. (give a concrete example and 2 trade-offs).
• 49. Explain @Transactional limitations in microservices. (give a concrete example and 2 trade-offs).
• 50. Explain retries with backoff+jitter and retry storms. (give a concrete example and 2 trade-offs).
• 51. Explain circuit breaker behavior and tuning. (give a concrete example and 2 trade-offs).
• 52. Explain API error modeling and consistent error codes. (give a concrete example and 2 trade-offs).
• 53. Explain secure API design for payments. (give a concrete example and 2 trade-offs).
• 54. Explain the payment lifecycle: auth, capture, settlement, reconciliation, chargebacks. (give a concrete
example and 2 trade-offs).
• 55. Explain HashMap internals including collisions and resize. (give a concrete example and 2
trade-offs).
• 56. Explain ConcurrentHashMap and why it forbids nulls. (give a concrete example and 2 trade-offs).
• 57. Explain volatile vs synchronized with a bug example. (give a concrete example and 2 trade-offs).
• 58. Explain happens-before and why visibility matters. (give a concrete example and 2 trade-offs).
• 59. Design an idempotent POST endpoint for payments. (give a concrete example and 2 trade-offs).
• 60. Explain ACID isolation levels using refund/double-spend examples. (give a concrete example and 2
trade-offs).
• 61. Explain Outbox pattern and why it exists. (give a concrete example and 2 trade-offs).
• 62. Design saga orchestration with compensations. (give a concrete example and 2 trade-offs).
• 63. Explain unknown outcomes with payment gateways and reconciliation. (give a concrete example and
2 trade-offs).
• 64. Explain GC pauses and how they affect latency. (give a concrete example and 2 trade-offs).
• 65. Explain Java memory leak patterns and how to diagnose them. (give a concrete example and 2
trade-offs).
• 66. Explain why constructor injection improves testability. (give a concrete example and 2 trade-offs).
• 67. Explain @Transactional limitations in microservices. (give a concrete example and 2 trade-offs).
• 68. Explain retries with backoff+jitter and retry storms. (give a concrete example and 2 trade-offs).
• 69. Explain circuit breaker behavior and tuning. (give a concrete example and 2 trade-offs).
• 70. Explain API error modeling and consistent error codes. (give a concrete example and 2 trade-offs).
• 71. Explain secure API design for payments. (give a concrete example and 2 trade-offs).
• 72. Explain the payment lifecycle: auth, capture, settlement, reconciliation, chargebacks. (give a concrete
example and 2 trade-offs).
• 73. Explain HashMap internals including collisions and resize. (give a concrete example and 2
trade-offs).
• 74. Explain ConcurrentHashMap and why it forbids nulls. (give a concrete example and 2 trade-offs).
• 75. Explain volatile vs synchronized with a bug example. (give a concrete example and 2 trade-offs).
• 76. Explain happens-before and why visibility matters. (give a concrete example and 2 trade-offs).
• 77. Design an idempotent POST endpoint for payments. (give a concrete example and 2 trade-offs).
• 78. Explain ACID isolation levels using refund/double-spend examples. (give a concrete example and 2
trade-offs).
• 79. Explain Outbox pattern and why it exists. (give a concrete example and 2 trade-offs).
• 80. Design saga orchestration with compensations. (give a concrete example and 2 trade-offs).