A microservices application looks intimidating on an architecture diagram — boxes for services, gateways, registries, databases and brokers, with arrows everywhere. But every one of those boxes exists to solve a specific problem created by splitting an app into independent pieces. Once you know which problem each component solves, the whole diagram reads like a sentence. This tutorial walks the components in the order a request actually flows through them.
If the very idea of microservices is still new, read what are microservices first. Here we go one level deeper into how the parts connect into a working system.
The shape of the system
At the highest level, a request travels like this: a client hits the API gateway, which authenticates it and routes it to the right service; that service finds any other services it needs through service discovery; each service reads and writes its own database; and services that do not need an immediate reply talk through a message broker. Every component below maps to one of those steps.
| Component | Problem it solves |
|---|---|
| Services | Split business capabilities into independent, deployable units |
| API gateway | Give clients one entry point; centralise auth and routing |
| Service discovery | Let services find each other when addresses keep changing |
| Per-service database | Keep services independent by isolating their data |
| Message broker | Decouple services that communicate asynchronously |
| Config server | Manage configuration across many services centrally |
Services: one capability each
The building block is the service itself. Each one owns a single business capability — orders, inventory, payments, notifications — and nothing more. This focus is what lets a small team fully own a service and deploy it on its own schedule.
Here is a minimal order service endpoint in Spring Boot. It exposes one capability over REST and knows nothing about how inventory or payments are implemented internally:
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/orders")
public class OrderController {
private final OrderService orderService;
public OrderController(OrderService orderService) {
this.orderService = orderService;
}
@PostMapping
public OrderResponse placeOrder(@RequestBody OrderRequest request) {
Order saved = orderService.placeOrder(request); // owns only order logic
return OrderResponse.from(saved);
}
}
The key idea is the boundary: this service exposes what it does (place an order) and hides how. If you already know Spring Boot REST controllers, a service is just a small, focused Spring Boot app with a clear responsibility.
API gateway: the single front door
If clients had to know the address of every service, adding or moving a service would break every client. The API gateway solves this by being the one address clients talk to. It receives every external request and routes it to the correct internal service.
Because all traffic flows through it, the gateway is also the natural home for cross-cutting concerns: authentication, rate limiting, and request logging happen once, at the edge, instead of being duplicated in every service. A Spring Cloud Gateway route is as simple as declaring which path goes to which service:
spring:
cloud:
gateway:
routes:
- id: order-service
uri: lb://order-service # lb = look up via service discovery
predicates:
- Path=/orders/**
- id: inventory-service
uri: lb://inventory-service
predicates:
- Path=/inventory/**
Notice lb://order-service — the gateway does not hardcode an IP. It asks service discovery for the
current address, which brings us to the next component. The
API gateway deep dive covers this
piece in full.
Pro tip: Do not put business logic in the gateway. Its job is routing and cross-cutting concerns only. The moment the gateway starts making order-specific decisions, it becomes a bottleneck that every team must coordinate through — the exact coupling microservices are meant to avoid.
Service discovery: finding moving targets
In the cloud, service instances are created and destroyed constantly as the system scales up and down, and their IP addresses change every time. Hardcoding addresses is hopeless. Service discovery solves this: each service registers itself with a registry (such as Eureka or Consul) when it starts, and callers look services up by name.
So when the gateway wants order-service, it asks the registry "where is order-service right now?" and
gets back the address of a healthy instance. If three instances of the order service are running, the
registry helps distribute calls across them. This is what makes independent scaling actually work — you
add instances and the rest of the system finds them automatically.
Databases: one per service
Each service owns its own database, and no other service touches it directly. This is a deliberate constraint, and it is the heart of what makes services independent. The order service can change its schema, switch from MySQL to PostgreSQL, or restructure its tables, and no other service notices, because none of them read its tables.
The cost is that you can no longer wrap a single transaction around changes in two services. Keeping data consistent across services requires patterns like the saga, and understanding that trade-off is central to the whole style — the microservices vs monolith comparison digs into why teams accept it.
Messaging: decoupling in time
Synchronous REST calls are simple but couple services in time — if the inventory service is down when the order service calls it, the order fails. A message broker breaks that coupling. The order service publishes an "order placed" event and moves on; the inventory service consumes the event whenever it is ready, even if it was briefly down.
This asynchronous style improves resilience and lets one event fan out to many consumers — inventory, analytics and notifications can all react to the same "order placed" event independently. The trade-off is eventual consistency: the inventory is not updated the instant the order is placed, but a moment later. The Apache Kafka basics page shows how a broker actually delivers those events.
Common mistake: Making every call asynchronous "for decoupling." Some operations genuinely need an immediate answer — checking stock before confirming an order, for instance. Use synchronous calls when the caller must wait for the result, and messaging when it can react later. Mixing them up leads to either fragile chains or needless complexity.
Supporting components
A production system adds a few more pieces around this core. A configuration server centralises settings so you do not redeploy every service to change a value. Observability tooling — metrics, logs and distributed traces — lets you understand a request that crosses many services, which is why teams weigh observability against plain monitoring. And security is applied at the edge and re-verified in each service. None of these are optional at scale; they are what turn a pile of services into an operable system.
Putting it together
Trace one request end to end to lock it in. A user submits an order. The API gateway authenticates the
request and routes /orders to the order service, whose address it got from service discovery. The
order service writes to its own database, then publishes an "order placed" event to the broker. The
inventory service, discovered and running independently, consumes that event and updates its own
database. Every component you met did exactly one job in that flow.
That mental model — request in through the gateway, routed via discovery, data in per-service databases, events over the broker — is the whole architecture. Everything else is refinement.
The fastest way to make this concrete is to build a two-service version yourself: an order service and an inventory service, a gateway in front, talking over both REST and a broker. That is precisely the project you build in the Java Full Stack with AI program at CodeBegun, and it turns this diagram into something you have actually run. From here, continue through the microservices learning path to go deeper on each component, and check your understanding against the microservices interview questions.
Frequently Asked Questions
What are the main components of a microservices architecture?
What does an API gateway do in microservices?
What is service discovery and why is it needed?
Why does each microservice have its own database?
How do services communicate in this architecture?
Is Spring Boot good for building microservices?
Want to Build Your Career in Java Full Stack with AI?
Join CodeBegun and train with working industry engineers — Explore the Program

