Mikrotjänster (Microservices)
Introduktion
Mikrotjänstarkitektur är ett sätt att strukturera en applikation som en samling av små, självständiga tjänster — var och en körandes i sin egen process och kommunicerandes via väldefinierade API:er. Varje tjänst ansvarar för ett specifikt affärsdomän och kan driftsättas, skalas och uppdateras oberoende av de andra.
Monolith vs. Mikrotjänster
Monolitisk arkitektur
I en monolit körs hela applikationen som en enda enhet. All kod — användarhantering, betalning, produktkatalog — är sammanflätad och driftsätts tillsammans.
┌─────────────────────────────────────┐
│ Monolit │
│ ┌──────────┐ ┌──────────────┐ │
│ │ Users │ │ Products │ │
│ └──────────┘ └──────────────┘ │
│ ┌──────────┐ ┌──────────────┐ │
│ │ Payments │ │ Orders │ │
│ └──────────┘ └──────────────┘ │
│ En databas │
└─────────────────────────────────────┘
Problem med monoliter vid skalning:
- En bugg i betalningsmodulen kan krascha hela applikationen.
- Hela systemet måste driftsättas om vid minsta ändring.
- Svårt att skala enskilda delar (t.ex. bara produktsökning).
Mikrotjänstarkitektur
[Klient]
│
[API Gateway]
/ | \ \
Users Orders Products Payments
│ │ │ │
DB1 DB2 DB3 DB4
Varje tjänst:
- Har sin egen databas (Database per Service)
- Exponerar ett REST- eller gRPC-API
- Kan skalas oberoende
- Kan skrivas i olika språk
Nyckelkomponenter
API Gateway
Ingångspunkten för alla klientförfrågningar. Den ansvarar för:
- Routing till rätt tjänst
- Autentisering och auktorisation
- Rate limiting
- Load balancing
Klient → API Gateway → /users → User Service
→ /orders → Order Service
→ /products → Product Service
Service Discovery
Tjänsterna måste kunna hitta varandra dynamiskt — IP-adresser och portar ändras när containrar startar och stoppar.
Verktyg: Consul, Kubernetes DNS, AWS Cloud Map
Message Broker
Asynkron kommunikation via events istället för direkta HTTP-anrop ökar robustheten.
Order Service ──publishes──▶ "order.created" ──▶ Message Broker
│
┌───────────────┤
▼ ▼
Payment Service Email Service
Verktyg: Apache Kafka, RabbitMQ, AWS SQS
Exempel — Order Service (Java / Spring Boot)
// Order Service — en självständig mikrotjänst
@RestController
@RequestMapping("/api/orders")
public class OrderController {
private final OrderService orderService;
private final KafkaProducer eventBus;
public OrderController(OrderService orderService, KafkaProducer eventBus) {
this.orderService = orderService;
this.eventBus = eventBus;
}
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public OrderResponse createOrder(@RequestBody @Valid CreateOrderRequest request) {
// 1. Skapa ordern i vår egen databas
Order order = orderService.create(request);
// 2. Publicera event — Payment Service lyssnar och hanterar betalningen
eventBus.publish("order.created", new OrderCreatedEvent(
order.getId(),
order.getCustomerId(),
order.getTotalAmount()
));
return new OrderResponse(order.getId(), order.getStatus());
}
@GetMapping("/{id}")
public OrderResponse getOrder(@PathVariable Long id) {
return orderService.findById(id)
.map(OrderResponse::from)
.orElseThrow(() -> new OrderNotFoundException(id));
}
}
// Payment Service lyssnar på events från Order Service
@Component
public class OrderEventListener {
private final PaymentService paymentService;
@KafkaListener(topics = "order.created")
public void onOrderCreated(OrderCreatedEvent event) {
// Hanterar betalning oberoende av Order Service
paymentService.processPayment(
event.getOrderId(),
event.getCustomerId(),
event.getAmount()
);
}
}
Designmönster för Mikrotjänster
| Mönster | Problem det löser |
|---|---|
| Circuit Breaker | Förhindrar kaskadfel när en tjänst är nere |
| Saga | Hanterar distribuerade transaktioner |
| API Gateway | Ger en enda ingångspunkt för klienter |
| Database per Service | Löst koppling — varje tjänst äger sin data |
| Event Sourcing | Auditlog och tidresning via events |
Circuit Breaker — exempel
// Om Payment Service är nere, bryt kretsen istället för att hänga
@CircuitBreaker(name = "paymentService", fallbackMethod = "fallbackPayment")
public PaymentResult processPayment(PaymentRequest request) {
return paymentClient.charge(request);
}
public PaymentResult fallbackPayment(PaymentRequest request, Exception ex) {
// Köa betalningen för senare försök
paymentQueue.enqueue(request);
return PaymentResult.queued("Betalningen köas och behandlas snart.");
}
Fördelar
- Oberoende driftsättning — Uppdatera en tjänst utan att röra de andra.
- Teknikfrihet — Välj bästa verktyget för varje tjänst.
- Skalbarhet — Skala bara de tjänster som är under hög last.
- Felatolerabilitet — En kraschad tjänst påverkar inte hela systemet.
Nackdelar
- Komplexitet — Distribuerade system är genuint svårare att debugga.
- Nätverksfördröjning — Tjänstkommunikation är långsammare än inprocess-anrop.
- Datakonsistens — Transaktioner som spänner flera tjänster är svåra.
- Operationell overhead — Kräver container-orchestrering (Kubernetes), monitoring, tracing.
Tumregel: Börja med en välstrukturerad monolit. Bryt ut mikrotjänster när du har tydliga domängränser och verklig skalningsproblematik.
Av Victor Hernandez från Bytebase.se