Skip to main content
Spring Boot & Microservices Foundations
Microservices Foundations
Before writing code, we must understand why we are building microservices.
1. Monolith vs Microservices
The Monolith
A single deployment unit containing all business logic, UI, and data access.
Pros:
Simple to develop, test, and deploy (initially).
No network latency between calls.
Easy to debug (single stack trace).
Cons:
Scaling: Can only scale the whole app (X-axis scaling), not specific bottlenecks.
Technology Lock-in: Hard to switch languages/frameworks.
Complexity: Over time, “Big Ball of Mud” anti-pattern emerges.
Reliability: If one module causes an OOM error, the whole system crashes.
Microservices
Architectural style where an application is a collection of loosely coupled services.
Pros:
Technological Freedom: Each service can use the best tool for the job.
Independent Deployment: Deploy User Service without redeploying Order Service.
Fault Isolation: If Payment Service fails, users can still browse products.
Scaling: Scale only the popular services independently.
Cons:
Distributed Complexity: Network failures, latency, consistency issues (CAP Theorem).
Operational Overhead: Need sophisticated monitoring, logging, and deployment pipelines.
2. Domain Driven Design (DDD) Basics
DDD is crucial for identifying service boundaries. A microservice should correspond to a Bounded Context .
Ubiquitous Language: Common language shared by developers and domain experts.
Bounded Context: A boundary within which a particular domain model is defined and applicable.
Example: “User” in the Sales Context might refer to a customer with credit limits. “User” in the Support Context might refer to a ticket opener. These should be different Microservices.
3. Communication Patterns
Services need to talk to each other.
Synchronous (Request/Response)
The client waits for a response.
HTTP/REST: Standard, easy to debug over JSON.
gRPC: High performance, strictly typed (Protobuf).
Drawback: Coupling. If Service B is down, Service A might fail (Cascading Failure).
Asynchronous (Event-Driven)
The client sends a message and forgets.
Message Queues: RabbitMQ, ActiveMQ.
Event Streaming: Apache Kafka.
Advantage: Decoupling. If Service B is down, the message stays in the queue until B recovers.
4. The 12-Factor App
A methodology for building cloud-native apps:
Codebase: One codebase tracked in revision control, many deploys.
Dependencies: Explicitly declare and isolate dependencies.
Config: Store config in the environment.
Backing services: Treat backing services as attached resources.
Build, release, run: Strictly separate build and run stages.
Processes: Execute the app as one or more stateless processes.
Port binding: Export services via port binding.
Concurrency: Scale out via the process model.
Disposability: Maximize robustness with fast startup and graceful shutdown.
Dev/prod parity: Keep development, staging, and production as similar as possible.
Logs: Treat logs as event streams.
Admin processes: Run admin/management tasks as one-off processes.
5. CAP Theorem (The Trade-offs)
In any distributed data store, you can only provide two of the following three guarantees:
Consistency (C): Every read receives the most recent write or an error.
Availability (A): Every request receives a (non-error) response, without the guarantee that it contains the most recent write.
Partition Tolerance (P): The system continues to operate despite an arbitrary number of messages being dropped/delayed by the network.
Reality: Network Partitions (P) will happen. So you have to choose between CP (Consistency) and AP (Availability).
CP (Banking): If the network breaks, stop accepting updates until it’s fixed. Don’t show wrong balance.
AP (Social Media): If the network breaks, show the old feed. It’s better than showing an error.