Microservices Introduction
Welcome to our Microservices tutorial! Whether you are a beginner or an experienced developer, this guide will help you understand microservices architecture step by step, with simple explanations, diagrams and working Java examples.
You will learn what microservices are, why companies use them, the building blocks of a microservices system, how a real online shop is split into services, and when microservices are the right choice. Along the way, you will find links to detailed guides on each topic.
1. What Is Microservices Architecture?
Microservices architecture is a software design approach that breaks down a large application into smaller, independent services. Each service is designed to handle a specific business function and communicates with other services over a network using APIs or messaging systems.
Microservices are often contrasted with monolithic applications, which are built as a single unit. In a monolith, all the business logic, data access and user interface code is combined into one codebase, which is built and deployed together. Microservices break this model by creating multiple smaller services that can be developed, deployed and scaled independently.
2. Why Microservices? From Monolith to Services
Most applications start as a monolith: one codebase, one build and one database. That is simple and works well at first. As the application and the team grow, problems appear:
- A small change requires rebuilding and redeploying the whole application.
- Many teams work in the same codebase and get in each other’s way.
- To handle more traffic on one feature, you must scale the entire application.
- One bug, such as a memory leak, can crash everything.
Microservices address these problems by splitting the application along business boundaries, so each part can be built, deployed and scaled on its own. Companies such as Netflix, Amazon and Uber popularized this approach to let hundreds of teams work and release independently. For a detailed comparison, see Monolithic vs Microservices Architecture.
3. Key Characteristics of Microservices
| Characteristic | What it means |
|---|---|
| Single responsibility | Each service focuses on doing one business thing well, such as payments, and owns the data and logic for it |
| Independent development | Teams work on different services at the same time without stepping on each other’s toes |
| Independent deployment | You can update one service without redeploying the whole application |
| Technology freedom | Each service can use the programming language that suits it (polyglot programming) and the database that suits it (polyglot persistence) |
| Decentralized data | Each service has its own database, which avoids the tight coupling a shared database creates |
| Communication through APIs | Services talk over the network with lightweight protocols such as HTTP/REST, or through message brokers such as RabbitMQ and Kafka |
| Built for failure | Services can fail, so the system is designed to cope, using patterns such as circuit breakers and retries |
| Automation | Many services are only manageable with automated builds, tests, deployments and monitoring |
4. Building Blocks of a Microservices System
A real microservices system needs more than the services themselves. These supporting pieces appear in almost every architecture:
| Building block | Purpose | Common tools | Learn more |
|---|---|---|---|
| API gateway | Single entry point for clients; routing, security, rate limiting | Spring Cloud Gateway, Kong, AWS API Gateway | API Gateway |
| Service discovery | Lets services find each other by name instead of fixed addresses | Eureka, Consul, Kubernetes DNS | Service Discovery |
| Database per service | Each service owns and controls its data | PostgreSQL, MongoDB, Redis | Database per Service |
| Message broker | Asynchronous communication through events | Apache Kafka, RabbitMQ | Event-Driven Architecture |
| Resilience | Timeouts, retries and circuit breakers for unreliable networks | Resilience4j | Circuit Breaker |
| Observability | Logs, metrics and traces across all services | Micrometer, Prometheus, Grafana, Zipkin | Monitoring & Logging |
| Containers and orchestration | Package and run many services consistently | Docker, Kubernetes | — |
| Central configuration | Manage settings for all services in one place | Spring Cloud Config, Kubernetes ConfigMaps | — |
5. Example: An Online Shop as Microservices
Let us design a simple online shop. Each service owns one business capability and its own data:
| Service | Responsibility | Owns data about |
|---|---|---|
user-service | Registration, login and profiles | Users and addresses |
product-service | Product catalog and prices | Products |
order-service | Creating and tracking orders | Orders and order lines |
payment-service | Charging customers and refunds | Payments |
notification-service | Emails and text messages | Sent notifications |
The order service asks the product service for prices synchronously, because it needs the answer right away. After an order is placed, it publishes an event, and the payment and notification services react asynchronously.
6. Advantages of Microservices
- Agility and faster development: small, autonomous teams develop, test and deploy their services quickly and independently.
- Targeted scalability: scale only the services that need it, such as the payment service during a sale, which is more cost-effective.
- Resilience: the failure of one service does not have to crash the whole system; the rest can keep working, perhaps with reduced features.
- Technology freedom: teams can choose the best tools for their specific job.
- Easier to understand: a new developer only needs to learn one focused service to start contributing.
- Faster, safer releases: small deployments are less risky and easier to roll back.
7. Challenges and Disadvantages
- Complexity: managing many distributed services is much harder than one application. This is often called distributed system complexity.
- Network latency and failures: network calls are slower than method calls and can fail, so the system must be designed to handle this.
- Data consistency: keeping data consistent across services with separate databases is hard. It is often solved with the Saga pattern instead of traditional ACID transactions.
- Operational overhead: you need solid DevOps practices: containers (Docker), orchestration (Kubernetes), central logging, monitoring and tracing.
- Debugging: following a request through many services requires distributed tracing tools.
- Testing: you need contract tests and some end-to-end tests in addition to unit tests.
8. Essential Microservices Patterns
| Pattern | Problem it solves |
|---|---|
| API Gateway | One entry point for clients instead of calling every service directly |
| Service Discovery and Service Registry | Finding services whose addresses change |
| Database per Service | Keeping services independent at the data level |
| Aggregator | Combining data from several services into one response |
| Circuit Breaker | Stopping calls to a failing service |
| Retry and Timeout | Handling short failures and slow responses |
| Bulkhead | Stopping one slow dependency from using up all resources |
| Rate Limiting | Protecting services from too much traffic |
| Saga | Keeping data consistent across services without one transaction |
| Sidecar | Adding features such as logging or proxies next to a service |
| Domain-Driven Design | Finding good boundaries between services |
9. When to Use Microservices, and When Not To
| Microservices are a good fit when… | A monolith is usually better when… |
|---|---|
| The system is large and complex, with clear business areas | The application is small or simple |
| Many teams need to work and release independently | One small team builds the whole product |
| Different parts need very different scaling | Traffic is moderate and even |
| You have DevOps skills: automation, containers, monitoring | Infrastructure and operations experience is limited |
| The business domain is well understood | The product is new and still changing direction quickly |
Microservices are not a silver bullet. For a startup or a simple application, a well-structured monolith is often the more practical choice, and it can be split later when there is a clear need. See Use Cases of Microservices for real-world examples.
10. Summary
Microservices architecture builds an application from small, independent services, each owning one business capability and its own data, and communicating through APIs and events. It brings agility, targeted scaling, resilience and team independence, but also distributed complexity, harder data consistency and higher operational demands. Services talk synchronously over REST when they need an answer right away, and asynchronously through events, for example with Kafka, when others only need to react. Use microservices for large, complex systems with several teams, and start with a well-structured monolith when the application or team is small. Continue with Monolithic vs Microservices Architecture to take the next step.
Microservices Introduction: Interview Questions
Common interview questions on the basics of microservices architecture, from basic to scenario-based, with short answers.
Basic questions
Q. What is microservices architecture? Explain it as you would in an interview.
Answer: Microservices architecture builds one application as a set of small, independently deployable services. Each service owns one business capability, such as orders or payments, along with its own code and data, runs in its own process, and talks to other services over the network through APIs or events.
A strong answer adds the why: teams can change, release and scale their part of the system without coordinating a release of the whole application.
Q. What are the key characteristics of a microservice?
Answer:
- Single responsibility: one business capability per service.
- Independent deployment: a service can be released without redeploying others.
- Decentralized data: each service owns its database.
- Communication through APIs or messages: REST, gRPC, Kafka or RabbitMQ.
- Built for failure: timeouts, retries and circuit breakers are part of the design.
- Automation: builds, tests, deployments and monitoring are automated.
Q. How is a microservice different from a module in a monolith?
Answer: A module in a monolith runs in the same process, shares the same database and is released together with everything else; calls between modules are fast, in-memory method calls. A microservice runs in its own process, has its own data and release cycle, and is called over the network.
So microservices gain independent deployment and scaling, but pay for it with network latency, partial failures and distributed data. See Monolithic vs Microservices.
Q. Why should each microservice have its own database?
Answer: If two services share tables, a schema change by one team can break the other, and the services can no longer be deployed or scaled independently. Owning the data keeps the service's internals private: other services use its API or listen to its events.
Owning data does not always mean a separate server; a separate schema that no other service touches is enough. See Database per Service.
Q. What are the main building blocks of a microservices system besides the services?
Answer: An API gateway as the single entry point; service discovery so services find each other by name; a message broker such as Kafka for events; resilience tools such as Resilience4j; observability for logs, metrics and traces; containers and orchestration with Docker and Kubernetes; and central configuration.
Interviewers like it when you explain which problem each piece solves, not just name the tools.
Q. Why do clients call an API gateway instead of calling services directly?
Answer: Without a gateway, every web and mobile client must know the address of every service, and every service must handle authentication, rate limiting and CORS itself. The gateway gives clients one entry point, routes each request to the right service, and handles these cross-cutting concerns in one place. It also lets you split or move services without changing clients. See API Gateway Pattern.
Q. What is service discovery, and why not just hard-code service addresses?
Answer: Service instances start, stop and move all the time, especially when they scale or are replaced, so their IP addresses change. With service discovery, services register under a name, and callers look up a healthy instance by that name, through a registry such as Eureka or Consul, or through Kubernetes DNS. Hard-coded addresses break as soon as an instance moves. See Service Discovery.
Intermediate questions
Q. In the online shop example, why does the order service call the product service synchronously but publish an event for payment and notification?
Answer: The order service needs the price and stock right away to create a correct order, so it waits for the product service's answer over REST. After the order is placed, it doesn't need anything back from payment or notification, so it just announces the fact with an order-placed event.
The rule of thumb: call synchronously when you need the answer to continue; publish an event when others only need to react.
Q. Why should a client set both a connect timeout and a read timeout on remote calls?
Answer: Remote calls can hang. Without timeouts, a slow product service keeps the order service's threads waiting, they pile up, and the order service becomes slow or unavailable too: a cascading failure. A connect timeout, for example 2 seconds, limits how long to wait for a connection, and a read timeout, for example 3 seconds, limits how long to wait for the response.
Timeouts are the first layer of resilience; retries, circuit breakers and fallbacks build on them.
Q. Why should services not share domain classes, such as one common Product class in a shared JAR?
Answer: A shared business model couples the services: when the class changes, every service that uses it must be rebuilt and redeployed together, which brings back monolith-style releases. Each service should define its own small model with only the fields it needs, as the order service's product client does. Sharing purely technical libraries, such as logging or security helpers, is fine.
Q. How does the order service find the product service's URL on a laptop, in Docker and in production?
Answer: The URL is not in the code. It comes from configuration: services.product.url in application.yml, injected with @Value. Each environment overrides it. Thanks to Spring Boot's relaxed binding, the environment variable SERVICES_PRODUCT_URL overrides the property, which works the same in Docker and Kubernetes. The same build artifact then runs everywhere with different settings.
Q. Why are microservices usually packaged as Docker containers?
Answer: A container bundles the service with its runtime, such as the right Java version, so it runs the same on a laptop, in CI and in production. Each service can use its own runtime version without conflicts, and containers start quickly, which makes scaling and replacing instances easy. Docker builds and runs containers; an orchestrator such as Kubernetes runs many of them in production, restarting, scaling and load-balancing them.
Q. What are the main advantages and disadvantages of microservices?
Answer: Advantages: independent deployment, independent scaling of busy services, fault isolation, technology freedom per service, and teams that can work in parallel.
Disadvantages: network calls that can be slow or fail, data consistency across services, more infrastructure and monitoring, harder testing and debugging, and the need for mature automation. See Advantages & Disadvantages.
Tricky and scenario questions
Q. What happens to orders if the notification service is down for ten minutes?
Answer: Nothing breaks for customers. The order service publishes the event to Kafka and continues. Kafka keeps the events, and when the notification service comes back, its consumer group continues from its last committed position and processes the backlog.
A good follow-up point: delivery is at-least-once, so after a crash some events may be delivered twice. The consumer should be idempotent, for example by remembering the IDs of events it has already handled, so customers don't get duplicate emails.
Q. When would you not use microservices?
Answer: For a small team, a new product whose domain is still changing, or an organisation without automated deployment and monitoring. In those cases a well-structured modular monolith is faster to build and easier to run. Services can be extracted later along clear business boundaries, when there is a real reason such as independent scaling or several teams blocking each other.
Q. What is a distributed monolith, and how do you avoid building one?
Answer: It is a system split into services that still have to be changed and deployed together: services that share a database, share business classes, call each other in long synchronous chains, or must be released in a fixed order. It has the costs of microservices without the benefits.
Avoid it by splitting along business capabilities, giving each service its own data, preferring events where no immediate answer is needed, and keeping APIs backward compatible.
Q. How would you find the cause of a failed request that passed through several services?
Answer: Use the trace ID. With distributed tracing, such as Micrometer Tracing in Spring Boot, every request gets a trace ID that is passed from service to service and added to every log line. Search the central log system for that ID to see the request's whole path, and open the trace in Zipkin, Jaeger or Grafana Tempo to see which call failed or was slow. See Distributed Tracing.
Q. Scenario: the product service becomes very slow. What happens to the order service, and how do you protect it?
Answer: Without protection, order requests wait for the product service, threads and connections fill up, and the order service slows down or fails as well. Protect it in layers:
- Timeouts so no call waits forever.
- A circuit breaker that stops calling the product service for a while once it is clearly failing.
- A fallback, such as a cached price with a clear message, or failing fast with a helpful error.
- A bulkhead that limits how many calls to the product service can run at once, so other work keeps going.