Microservices
Microservices is an architectural style that structures an application as a collection of loosely coupled, independently deployable services. Each service typically focuses on a specific business capability, communicates through well-defined APIs, and can be developed and scaled independently. This contrasts with monolithic applications where all functionality is bundled into a single process.
The core principle of microservices is to break down a large, complex application into smaller, manageable parts. This approach offers benefits like increased agility, improved scalability, technology diversity, and fault isolation. However, it also introduces complexities related to distributed system management, inter-service communication, and data consistency.
Usage
Microservices are commonly used in:
- Large-scale web applications: Where independent scaling of different features is crucial (e.g., user authentication, product catalog, shopping cart).
- E-commerce platforms: To manage separate services for ordering, payments, shipping, and inventory.
- Streaming services: Handling video encoding, content delivery, user accounts, and recommendation engines as independent services.
- Cloud-native applications: Leveraging the scalability and resilience of cloud platforms.
- Continuous Delivery/Deployment (CI/CD) pipelines: Enabling faster and more frequent releases of individual services.
Examples
- Netflix: A prime example of microservices architecture. They migrated from a monolithic application to an architecture composed of hundreds of microservices, each responsible for a specific function like user profiling, video streaming, or recommendation algorithms. This allowed them to scale efficiently and handle massive traffic.
- Spotify: Uses microservices to manage different aspects of its music streaming service. Services handle user authentication, music catalog, search, payment processing, and social features. This allows for independent updates and scaling of each component without impacting the entire platform.
- Amazon: Amazon’s retail platform is built on microservices. Each service handles a specific part of the shopping experience, such as product listings, order management, or customer reviews. This allows Amazon to rapidly innovate and deploy new features.
- Uber: Utilizes microservices for core functionalities like rider matching, fare calculation, payment processing, and driver management. This architecture supports their global scale and real-time demands.
Specimens
15 implementationsThe Microservices pattern structures an application as a collection of loosely coupled, independently deployable services, modeled around a business domain. Each service owns its data and communicates via lightweight mechanisms, often a RESTful API. This example demonstrates a simplified implementation with two microservices: product_service and order_service. They communicate via HTTP requests. Dart’s asynchronous capabilities (async/await) and its support for HTTP clients make it well-suited for this pattern. The use of separate functions to represent service endpoints and a simple data model keeps the code clean and focused, aligning with Dart’s emphasis on readability and maintainability.
// product_service.dart
import 'dart:convert';
import 'package:http/http.dart' as http;
class Product {
final String id;
final String name;
final double price;
Product({required this.id, required this.name, required this.price});
factory Product.fromJson(Map<String, dynamic> json) {
return Product(
id: json['id'],
name: json['name'],
price: json['price'].toDouble(),
);
}
}
Future<Product> getProduct(String id) async {
final response = await http.get(Uri.parse('http://localhost:8081/products/$id'));
if (response.statusCode == 200) {
return Product.fromJson(jsonDecode(response.body));
} else {
throw Exception('Failed to load product');
}
}
// order_service.dart
import 'dart:convert';
import 'package:http/http.dart' as http;
class Order {
final String orderId;
final String productId;
final int quantity;
Order({required this.orderId, required this.productId, required this.quantity});
factory Order.fromJson(Map<String, dynamic> json) {
return Order(
orderId: json['orderId'],
productId: json['productId'],
quantity: json['quantity'],
);
}
}
Future<void> createOrder(Order order) async {
final response = await http.post(
Uri.parse('http://localhost:8082/orders'),
headers: {'Content-Type': 'application/json'},
body: jsonEncode(order),
);
if (response.statusCode != 201) {
throw Exception('Failed to create order');
}
}
// main.dart
import 'package:microservices_dart/product_service.dart';
import 'package:microservices_dart/order_service.dart';
void main() async {
try {
final product = await getProduct('123');
print('Product: ${product.name}, Price: ${product.price}');
final order = Order(orderId: '456', productId: product.id, quantity: 2);
await createOrder(order);
print('Order created successfully!');
} catch (e) {
print('Error: $e');
}
}
The Microservices pattern structures an application as a collection of loosely coupled, independently deployable services, modeled around a business domain. Each service owns its data and communicates via lightweight mechanisms, often a REST API. This example demonstrates a simplified scenario with two microservices: UserService and OrderService. They communicate using HTTP requests. Scala’s functional nature and Akka toolkit (though not fully utilized here for brevity) make it well-suited for building resilient and concurrent microservices. The use of case classes for data transfer and simple HTTP routes with http4s aligns with Scala’s concise and expressive style.
// UserService.scala
import http4s._
import http4s.dsl.http4s._
import http4s.server.blaze._
import scala.concurrent.ExecutionContext
case class User(id: Int, name: String)
object UserService extends HttpApp(ExecutionContext.global) {
val users = Map(1 -> User(1, "Alice"), 2 -> User(2, "Bob"))
val httpRoutes: HttpRoutes[IO] = HttpRoutes.of[IO] {
case GET -> Root / "users" / idStr =>
users.get(idStr.toInt) match {
case Some(user) => Ok(user)
case None => NotFound()
}
}
override def server(args: RequestParser): Server[IO] = {
BlazeServerBuilder[IO](ExecutionContext.global)
.bindHttp(8081)
.mountService(httpRoutes)
.resource
}
}
// OrderService.scala
import http4s._
import http4s.dsl.http4s._
import http4s.server.blaze._
import scala.concurrent.ExecutionContext
import scala.io.Source
case class Order(userId: Int, productId: String)
object OrderService extends HttpApp(ExecutionContext.global) {
val httpRoutes: HttpRoutes[IO] = HttpRoutes.of[IO] {
case GET -> Root / "orders" / userIdStr =>
// Simulate fetching orders. In a real system, this would query a database.
val orders = List(Order(userIdStr.toInt, "ProductA"), Order(userIdStr.toInt, "ProductB"))
IO.pure(Ok(orders))
}
override def server(args: RequestParser): Server[IO] = {
BlazeServerBuilder[IO](ExecutionContext.global)
.bindHttp(8082)
.mountService(httpRoutes)
.resource
}
}
The Microservices pattern structures an application as a collection of loosely coupled, independently deployable services, built around specific business capabilities. Each service owns its data and communicates with others typically via lightweight mechanisms like HTTP. This example simulates a simple e-commerce system with OrderService and ProductService. Each service has its own basic logic. Communication is achieved through simple function calls (in a real-world scenario, this would be API calls). This approach promotes modularity, scalability, and independent development/deployment, fitting PHP’s capability for creating independent scripts and utilizing frameworks for API development.
<?php
// ProductService.php
class ProductService {
private $products = [
1 => ['id' => 1, 'name' => 'Shirt', 'price' => 20],
2 => ['id' => 2, 'name' => 'Pants', 'price' => 30],
];
public function getProduct(int $id): ?array {
return $this->products[$id] ?? null;
}
}
// OrderService.php
class OrderService {
private $productService;
public function __construct(ProductService $productService) {
$this->productService = $productService;
}
public function createOrder(int $productId, int $quantity): array {
$product = $this->productService->getProduct($productId);
if (!$product) {
return ['error' => 'Product not found'];
}
$totalPrice = $product['price'] * $quantity;
return ['order_id' => uniqid(), 'product_id' => $productId, 'quantity' => $quantity, 'total_price' => $totalPrice];
}
}
// Example Usage (could be a separate script or entry point)
require_once 'ProductService.php';
require_once 'OrderService.php';
$productService = new ProductService();
$orderService = new OrderService($productService);
$order = $orderService->createOrder(1, 2);
if (isset($order['error'])) {
echo $order['error'];
} else {
echo "Order created: " . json_encode($order);
}
?>
The Microservices pattern structures an application as a collection of loosely coupled, independently deployable services, modeled around a business domain. Each service owns its data and communicates via lightweight mechanisms, often an HTTP resource API. This example demonstrates a simplified order and payment service interaction. We use Ruby’s class-based structure to define each service. The services communicate via HTTP requests using the net/http library, a standard Ruby approach for making web requests. This avoids tight coupling and allows for independent scaling and development. The use of simple classes and methods keeps the code concise and readable, aligning with Ruby’s emphasis on developer happiness.
require 'net/http'
require 'json'
# Order Service
class OrderService
def self.place_order(item, quantity)
order_data = { item: item, quantity: quantity, status: 'pending' }
# In a real app, this would persist to a database
order_id = rand(1000)
puts "Order placed: #{order_id} for #{quantity} #{item}"
order_id
end
end
# Payment Service
class PaymentService
def self.process_payment(order_id, amount)
# Simulate payment processing
payment_successful = rand(1..2) == 1
if payment_successful
puts "Payment processed successfully for order #{order_id}, amount #{amount}"
return true
else
puts "Payment failed for order #{order_id}"
return false
end
end
end
# Orchestrator (Simple example - could be a separate service)
def fulfill_order(item, quantity)
order_id = OrderService.place_order(item, quantity)
amount = quantity * 10 # Example price
if PaymentService.process_payment(order_id, amount)
puts "Order #{order_id} fulfilled successfully!"
else
puts "Order #{order_id} fulfillment failed due to payment issues."
end
end
# Example Usage
fulfill_order("Widget", 2)
fulfill_order("Gadget", 5)
The Microservices pattern structures an application as a collection of loosely coupled, independently deployable services, modeled around a business domain. Each service owns its data and communicates via lightweight mechanisms, often a RESTful API. This example demonstrates a simplified implementation with two services: UserService and OrderService. They communicate through a shared User model and a basic API client. Swift’s protocol-oriented programming and strong typing lend themselves well to defining clear service interfaces. Using Codable for data transfer is idiomatic for handling JSON in Swift. The structure favors composition over inheritance, aligning with microservice principles.
// Shared Model
struct User: Codable {
let id: Int
let name: String
}
// UserService
protocol UserService {
func getUser(id: Int) async throws -> User
}
class UserServiceImpl: UserService {
func getUser(id: Int) async throws -> User {
// Simulate database call
if id == 1 {
return User(id: 1, name: "Alice")
} else {
throw NSError(domain: "UserService", code: 404, userInfo: [NSLocalizedDescriptionKey: "User not found"])
}
}
}
// OrderService
protocol OrderService {
func createOrder(userId: Int) async throws -> String
}
class OrderServiceImpl: OrderService {
private let userService: UserService
init(userService: UserService) {
self.userService = userService
}
func createOrder(userId: Int) async throws -> String {
let user = try await userService.getUser(id: userId)
// Simulate order creation
let orderId = UUID().uuidString
return "Order \(orderId) created for \(user.name)"
}
}
// API Client (Simplified)
actor APIClient {
private let userService: UserService
private let orderService: OrderService
init(userService: UserService, orderService: OrderService) {
self.userService = userService
self.orderService = orderService
}
func getUserName(userId: Int) async throws -> String {
let user = try await userService.getUser(id: userId)
return user.name
}
func placeOrder(userId: Int) async throws -> String {
return try await orderService.createOrder(userId: userId)
}
}
// Example Usage
func main() async {
let userService = UserServiceImpl()
let orderService = OrderServiceImpl(userService: userService)
let apiClient = APIClient(userService: userService, orderService: orderService)
do {
let userName = try await apiClient.getUserName(userId: 1)
print("User name: \(userName)")
let orderConfirmation = try await apiClient.placeOrder(userId: 1)
print(orderConfirmation)
} catch {
print("Error: \(error)")
}
}
Task {
await main()
}
The Microservices pattern structures an application as a collection of loosely coupled, independently deployable services, modeled around a business domain. Each service owns its data and communicates via lightweight mechanisms, often an HTTP REST API. This example demonstrates a simplified scenario with two microservices: customer-service and product-service. Kotlin’s concise syntax and support for both OOP and functional programming make it well-suited for building these services. We use Ktor, a Kotlin-native framework, for creating lightweight REST APIs. Data classes and immutability are favored for data transfer objects, aligning with functional principles and simplifying communication.
// customer-service/src/main/kotlin/CustomerService.kt
package customer
import io.ktor.application.*
import io.ktor.response.*
import io.ktor.routing.*
import kotlinx.serialization.Serializable
data class Customer(val id: Int, val name: String)
fun main(args: Array<String>): Unit {
val customers = listOf(Customer(1, "Alice"), Customer(2, "Bob"))
io.ktor.server.netty.EngineMain.main(args)
}
fun Application.module() {
install(io.ktor.serialization.Serialization)
routing {
get("/customers/{id}") {
val id = call.parameters["id"]?.toIntOrNull() ?: return@get call.respond(
io.ktor.http.HttpStatusCode.BadRequest,
"Invalid customer ID"
)
val customer = customers.find { it.id == id }
if (customer != null) {
call.respond(customer)
} else {
call.respond(io.ktor.http.HttpStatusCode.NotFound, "Customer not found")
}
}
}
}
// product-service/src/main/kotlin/ProductService.kt
package product
import io.ktor.application.*
import io.ktor.response.*
import io.ktor.routing.*
import kotlinx.serialization.Serializable
data class Product(val id: Int, val name: String, val price: Double)
fun main(args: Array<String>): Unit {
val products = listOf(Product(1, "Laptop", 1200.0), Product(2, "Mouse", 25.0))
io.ktor.server.netty.EngineMain.main(args)
}
fun Application.module() {
install(io.ktor.serialization.Serialization)
routing {
get("/products/{id}") {
val id = call.parameters["id"]?.toIntOrNull() ?: return@get call.respond(
io.ktor.http.HttpStatusCode.BadRequest,
"Invalid product ID"
)
val product = products.find { it.id == id }
if (product != null) {
call.respond(product)
} else {
call.respond(io.ktor.http.HttpStatusCode.NotFound, "Product not found")
}
}
}
}
The Microservices pattern structures an application as a collection of loosely coupled, independently deployable services, modeled around a business domain. Each service owns its data and communicates via lightweight mechanisms, often a REST API. This example demonstrates a simplified order and payment service interaction. We use Rust’s strong typing and ownership system to ensure data integrity between services. tokio handles asynchronous networking, crucial for microservice communication. The serde crate facilitates serialization/deserialization for API requests and responses, a common practice in Rust web services. Error handling uses Result for clarity and robustness.
// order_service/src/main.rs
use actix_web::{get, post, web, App, HttpResponse, HttpServer, Responder};
use serde::{Deserialize, Serialize};
use uuid::Uuid;
#[derive(Debug, Serialize, Deserialize)]
struct Order {
id: Uuid,
items: Vec<String>,
total: f64,
}
#[get("/orders/{id}")]
async fn get_order(id: web::Path<Uuid>) -> impl Responder {
// In a real app, this would fetch from a database.
let order = Order {
id: id.into_inner(),
items: vec!["item1".to_string(), "item2".to_string()],
total: 100.0,
};
HttpResponse::Ok().json(order)
}
#[post("/orders")]
async fn create_order(order: web::Json<Order>) -> impl Responder {
// In a real app, this would save to a database and potentially
// trigger events for other services (e.g., payment).
HttpResponse::Created().json(order.into_inner())
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| {
App::new()
.service(get_order)
.service(create_order)
})
.bind("127.0.0.1:8080")?
.run()
.await
}
// payment_service/src/main.rs
use actix_web::{post, web, App, HttpResponse, HttpServer, Responder};
use serde::{Deserialize, Serialize};
use uuid::Uuid;
#[derive(Debug, Serialize, Deserialize)]
struct PaymentRequest {
order_id: Uuid,
amount: f64,
}
#[derive(Debug, Serialize, Deserialize)]
struct PaymentResponse {
status: String,
}
#[post("/payments")]
async fn process_payment(req: web::Json<PaymentRequest>) -> impl Responder {
// Simulate payment processing.
let status = if req.amount > 0.0 {
"success".to_string()
} else {
"failed".to_string()
};
HttpResponse::Ok().json(PaymentResponse { status })
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| {
App::new()
.service(process_payment)
})
.bind("127.0.0.1:8081")?
.run()
.await
}
The Microservices pattern structures an application as a collection of loosely coupled, independently deployable services, modeled around a business domain. Each service owns its data and communicates via lightweight mechanisms, often a RESTful API. This example demonstrates a simplified order and payment service. The order service handles order creation and the payment service processes payments. They communicate via HTTP requests. This fits idiomatic Go through its emphasis on simplicity, concurrency (though not explicitly shown here for brevity), and clear interfaces. The use of HTTP handlers and structs for data transfer aligns with Go’s standard library and common web development practices.
// main.go
package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
"strconv"
)
// Order Service
type Order struct {
ID int `json:"id"`
ItemID int `json:"item_id"`
Amount int `json:"amount"`
}
var orders = []Order{}
func createOrderHandler(w http.ResponseWriter, r *http.Request) {
var newOrder Order
if err := json.NewDecoder(r.Body).Decode(&newOrder); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
newOrder.ID = len(orders) + 1
orders = append(orders, newOrder)
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(newOrder)
}
// Payment Service
func processPaymentHandler(w http.ResponseWriter, r *http.Request) {
orderIDStr := r.URL.Query().Get("order_id")
amountStr := r.URL.Query().Get("amount")
orderID, err := strconv.Atoi(orderIDStr)
if err != nil {
http.Error(w, "Invalid order ID", http.StatusBadRequest)
return
}
amount, err := strconv.Atoi(amountStr)
if err != nil {
http.Error(w, "Invalid amount", http.StatusBadRequest)
return
}
// Simulate payment processing
paymentSuccessful := true // In a real app, this would depend on external factors
if paymentSuccessful {
w.WriteHeader(http.StatusOK)
fmt.Fprintln(w, "Payment processed successfully for order ID:", orderID, "amount:", amount)
} else {
http.Error(w, "Payment failed", http.StatusInternalServerError)
}
}
func main() {
// Order Service Routes
http.HandleFunc("/orders", createOrderHandler)
// Payment Service Routes
http.HandleFunc("/payments/process", processPaymentHandler)
fmt.Println("Order service listening on :8080")
log.Fatal(http.ListenAndServe(":8080", nil))
}
The Microservices pattern structures an application as a collection of loosely coupled, independently deployable services, modeled around a business domain. Each service handles a specific function and communicates with others, often via HTTP. This example simulates a simplified e-commerce system with ‘product’ and ‘order’ services. It uses basic C structures and functions to represent the services and their interactions. While C isn’t a typical choice for microservices due to its lack of built-in concurrency and networking features, this demonstrates the architectural concept. The use of function pointers allows for a degree of decoupling, simulating service calls.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// Product Service
typedef struct {
int id;
char name[50];
float price;
} Product;
typedef int (*getProductFunc)(int, Product*);
int getProductDetails(int id, Product* product) {
// Simulate database lookup
if (id == 1) {
strcpy(product->name, "Laptop");
product->price = 1200.00;
return 0; // Success
} else {
return -1; // Product not found
}
}
// Order Service
typedef struct {
int orderId;
int productId;
int quantity;
} Order;
typedef int (*processOrderFunc)(Order*);
int processOrder(Order* order) {
// Simulate order processing logic
if (order->quantity > 0) {
printf("Order processed: Order ID: %d, Product ID: %d, Quantity: %d\n",
order->orderId, order->productId, order->quantity);
return 0; // Success
} else {
return -1; // Invalid quantity
}
}
int main() {
Product product;
Order order;
// Product Service Call
getProductFunc getProduct = &getProductDetails;
if (getProduct(1, &product) == 0) {
printf("Product found: ID: %d, Name: %s, Price: %.2f\n",
product.id, product.name, product.price);
} else {
printf("Product not found.\n");
}
// Order Service Call
processOrderFunc process = &processOrder;
order.orderId = 101;
order.productId = 1;
order.quantity = 2;
if (process(&order) == 0) {
printf("Order processing successful.\n");
} else {
printf("Order processing failed.\n");
}
return 0;
}
The Microservices pattern structures an application as a collection of loosely coupled, independently deployable services, modeled around a business domain. Each service owns its data and communicates via lightweight mechanisms, often a RESTful API. This example simulates two microservices: UserService and OrderService, communicating over a simple HTTP interface using a basic in-memory data store for demonstration. A ServiceClient class abstracts the HTTP communication. This approach leverages C++’s ability to create well-defined classes and interfaces, and the use of standard libraries like iostream and a simplified HTTP client demonstrates a practical, albeit basic, implementation suitable for resource-constrained environments or when full-fledged frameworks are undesirable.
#include <iostream>
#include <string>
#include <vector>
#include <sstream>
#include <map>
// Simplified HTTP Client (for demonstration)
class ServiceClient {
public:
std::string get(const std::string& url) {
// In a real implementation, this would make an HTTP GET request.
// For simplicity, we simulate a response.
if (url == "http://user-service/users/123") {
return "{\"user_id\": \"123\", \"username\": \"john.doe\"}";
} else if (url == "http://order-service/orders/user/123") {
return "[{\"order_id\": \"456\", \"item\": \"Book\"}, {\"order_id\": \"789\", \"item\": \"Pen\"}]";
}
return "{}"; // Default empty response
}
};
// UserService Microservice
class UserService {
public:
std::string getUser(int userId) {
// In a real implementation, this would fetch from a database.
std::stringstream ss;
ss << "{\"user_id\": \"" << userId << "\", \"username\": \"user_" << userId << "\" }";
return ss.str();
}
};
// OrderService Microservice
class OrderService {
public:
std::vector<std::string> getOrders(int userId) {
// In a real implementation, this would fetch from a database.
if (userId == 123) {
return {"{\"order_id\": \"456\", \"item\": \"Book\"}",
"{\"order_id\": \"789\", \"item\": \"Pen\"}"};
}
return {};
}
};
int main() {
UserService userService;
OrderService orderService;
ServiceClient client;
// Simulate a client application using the microservices
int userId = 123;
// Get user information
std::string userJson = client.get("http://user-service/users/" + std::to_string(userId));
std::cout << "User: " << userJson << std::endl;
// Get orders for the user
std::string ordersJson = client.get("http://order-service/orders/user/" + std::to_string(userId));
std::cout << "Orders: " << ordersJson << std::endl;
return 0;
}
The Microservices pattern structures an application as a collection of loosely coupled, independently deployable services, modeled around a business domain. Each service owns its data and communicates via lightweight mechanisms, often a RESTful API. This example demonstrates a simplified order and customer service interaction. It uses ASP.NET Core Web APIs for each service, showcasing C#’s strong typing and asynchronous capabilities. Dependency Injection is used for loose coupling. The services are intentionally minimal to focus on the pattern’s core concept of independent deployment and communication. Real-world implementations would involve more robust error handling, security, and service discovery.
// OrderService/OrderController.cs
using Microsoft.AspNetCore.Mvc;
namespace OrderService
{
[ApiController]
[Route("[controller]")]
public class OrderController : ControllerBase
{
private readonly ICustomerService _customerService;
public OrderController(ICustomerService customerService)
{
_customerService = customerService;
}
[HttpGet("{orderId}")]
public async Task<IActionResult> GetOrder(int orderId)
{
// Simulate order retrieval
var order = new Order { Id = orderId, CustomerId = 1 };
// Call Customer Service to get customer details
var customer = await _customerService.GetCustomer(order.CustomerId);
if (customer == null)
{
return NotFound($"Customer with ID {order.CustomerId} not found.");
}
return Ok(new { Order = order, Customer = customer });
}
}
public class Order { public int Id { get; set; } public int CustomerId { get; set; } }
}
// CustomerService/CustomerController.cs
using Microsoft.AspNetCore.Mvc;
namespace CustomerService
{
[ApiController]
[Route("[controller]")]
public class CustomerController : ControllerBase
{
[HttpGet("{customerId}")]
public Customer GetCustomer(int customerId)
{
// Simulate customer retrieval
return new Customer { Id = customerId, Name = "Alice" };
}
}
public class Customer { public int Id { get; set; } public string Name { get; set; } }
}
// Shared Interface (for inter-service communication)
public interface ICustomerService
{
Task<Customer?> GetCustomer(int customerId);
}
//OrderService/Program.cs (Example of dependency injection)
using OrderService;
using CustomerService;
using Microsoft.Extensions.DependencyInjection;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
builder.Services.AddScoped<ICustomerService, CustomerServiceProxy>(); // Use a proxy for external service calls
var app = builder.Build();
// Configure the HTTP request pipeline.
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
//CustomerService/Program.cs
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
var app = builder.Build();
// Configure the HTTP request pipeline.
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
//OrderService/CustomerServiceProxy.cs (Proxy to call external service)
using System.Net.Http;
using System.Threading.Tasks;
using CustomerService;
using Newtonsoft.Json;
namespace OrderService
{
public class CustomerServiceProxy : ICustomerService
{
private readonly HttpClient _httpClient;
public CustomerServiceProxy(IConfiguration configuration)
{
_httpClient = new HttpClient();
_httpClient.BaseAddress = new Uri(configuration["CustomerServiceUrl"]);
}
public async Task<Customer?> GetCustomer(int customerId)
{
var response = await _httpClient.GetAsync($"Customer/{customerId}");
response.EnsureSuccessStatusCode();
var customerJson = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<Customer>(customerJson);
}
}
}
The Microservices pattern structures an application as a collection of loosely coupled, independently deployable services, modeled around a business domain. Each service owns its data and communicates via lightweight mechanisms, often an HTTP REST API. This example demonstrates a simplified implementation with two services: UserService and ProfileService. They communicate via HTTP requests. TypeScript’s modularity and strong typing are well-suited for defining clear service boundaries and interfaces. The use of Express.js provides a straightforward way to create RESTful APIs, and asynchronous functions (using async/await) handle the communication between services cleanly.
// userService.ts
import express from 'express';
import { v4 as uuidv4 } from 'uuid';
const app = express();
const port = 3001;
interface User {
id: string;
name: string;
email: string;
}
const users: User[] = [];
app.get('/users', (req, res) => {
res.json(users);
});
app.post('/users', async (req, res) => {
const { name, email } = req.body;
const newUser: User = { id: uuidv4(), name, email };
users.push(newUser);
// Simulate calling ProfileService to create a profile
await createProfile(newUser.id, name);
res.status(201).json(newUser);
});
async function createProfile(userId: string, userName: string): Promise<void> {
const profileResponse = await fetch('http://localhost:3002/profiles', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ userId, userName }),
});
if (!profileResponse.ok) {
throw new Error(`Failed to create profile: ${profileResponse.status}`);
}
}
app.listen(port, () => {
console.log(`UserService listening on port ${port}`);
});
// profileService.ts
import express from 'express';
const app = express();
const port = 3002;
interface Profile {
id: string;
userId: string;
userName: string;
bio: string;
}
const profiles: Profile[] = [];
app.post('/profiles', (req, res) => {
const { userId, userName } = req.body;
const newProfile: Profile = { id: userId, userId, userName, bio: 'Default Bio' };
profiles.push(newProfile);
res.status(201).json(newProfile);
});
app.get('/profiles', (req, res) => {
res.json(profiles);
});
app.listen(port, () => {
console.log(`ProfileService listening on port ${port}`);
});
The Microservices pattern structures an application as a collection of loosely coupled, independently deployable services, modeled around a business domain. Each service owns its data and communicates via lightweight mechanisms, often an HTTP resource API. This example simulates three microservices: users, products, and orders. Each service exposes a simple API endpoint. A basic api-gateway orchestrates requests. This implementation uses Node.js with Express to create simple HTTP servers for each service, demonstrating the independent deployability and communication aspects. The use of separate files for each service and a gateway aligns with common JavaScript project structures and modularity principles.
// api-gateway.js
const express = require('express');
const app = express();
const port = 3000;
app.get('/', (req, res) => {
res.send('API Gateway');
});
app.get('/users', async (req, res) => {
const userResponse = await fetch('http://localhost:3001/users');
const users = await userResponse.json();
res.json(users);
});
app.get('/products', async (req, res) => {
const productResponse = await fetch('http://localhost:3002/products');
const products = await productResponse.json();
res.json(products);
});
app.get('/orders', async (req, res) => {
const orderResponse = await fetch('http://localhost:3003/orders');
const orders = await orderResponse.json();
res.json(orders);
});
app.listen(port, () => {
console.log(`API Gateway listening at http://localhost:${port}`);
});
// users.js
const express = require('express');
const app = express();
const port = 3001;
app.get('/users', (req, res) => {
res.json([
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' }
]);
});
app.listen(port, () => {
console.log(`Users service listening at http://localhost:${port}`);
});
// products.js
const express = require('express');
const app = express();
const port = 3002;
app.get('/products', (req, res) => {
res.json([
{ id: 101, name: 'Laptop' },
{ id: 102, name: 'Mouse' }
]);
});
app.listen(port, () => {
console.log(`Products service listening at http://localhost:${port}`);
});
// orders.js
const express = require('express');
const app = express();
const port = 3003;
app.get('/orders', (req, res) => {
res.json([
{ id: 201, userId: 1, productId: 101 },
{ id: 202, userId: 2, productId: 102 }
]);
});
app.listen(port, () => {
console.log(`Orders service listening at http://localhost:${port}`);
});
The Microservices pattern structures an application as a collection of loosely coupled, independently deployable services, modeled around a business domain. Each service owns its data and communicates via lightweight mechanisms, often an HTTP resource API. This example demonstrates a simplified order and customer service. The OrderService handles order creation, while CustomerService manages customer details. They communicate via HTTP requests using the requests library. This approach aligns with Python’s flexibility and ease of use for building APIs, leveraging its web frameworks (like Flask, used here) for service implementation and standard libraries for communication.
# order_service.py
from flask import Flask, request, jsonify
app = Flask(__name__)
orders = {}
order_id_counter = 1
@app.route('/orders', methods=['POST'])
def create_order():
global order_id_counter
customer_id = request.json.get('customer_id')
if not customer_id:
return jsonify({'error': 'Customer ID is required'}), 400
order_id = order_id_counter
order_id_counter += 1
orders[order_id] = {'customer_id': customer_id, 'items': request.json.get('items', [])}
return jsonify({'order_id': order_id}), 201
@app.route('/orders/<int:order_id>', methods=['GET'])
def get_order(order_id):
if order_id in orders:
return jsonify(orders[order_id])
return jsonify({'error': 'Order not found'}), 404
if __name__ == '__main__':
app.run(port=5000)
# customer_service.py
from flask import Flask, jsonify
app = Flask(__name__)
customers = {
1: {'name': 'Alice', 'email': 'alice@example.com'},
2: {'name': 'Bob', 'email': 'bob@example.com'}
}
@app.route('/customers/<int:customer_id>')
def get_customer(customer_id):
if customer_id in customers:
return jsonify(customers[customer_id])
return jsonify({'error': 'Customer not found'}), 404
if __name__ == '__main__':
app.run(port=5001)
The Microservices pattern structures an application as a collection of loosely coupled, independently deployable services, modeled around a business domain. Each service owns its data and communicates via lightweight mechanisms, often an HTTP resource API. This example demonstrates a simplified scenario with two microservices: a ProductService and an ReviewService. They communicate via simple method calls (in a real-world scenario, this would likely be REST or message queues). The implementation uses standard Java classes and interfaces, favoring composition over inheritance. Dependency Injection (though not explicitly shown with a framework like Spring for brevity) would be a common addition in a production environment to further decouple services.
// ProductService.java
import java.util.List;
import java.util.ArrayList;
interface ReviewService {
List<String> getReviewsForProduct(String productId);
}
public class ProductService {
private final ReviewService reviewService;
public ProductService(ReviewService reviewService) {
this.reviewService = reviewService;
}
public String getProductDetails(String productId) {
return "Product ID: " + productId + ", Details: ... " +
(reviewService.getReviewsForProduct(productId).isEmpty() ? "No reviews yet." : " Reviews: " + reviewService.getReviewsForProduct(productId));
}
}
// ReviewService.java
import java.util.List;
import java.util.Arrays;
public class ReviewServiceImplementation implements ReviewService {
@Override
public List<String> getReviewsForProduct(String productId) {
// Simulate fetching reviews from a database
if (productId.equals("123")) {
return Arrays.asList("Great product!", "Highly recommended.");
} else {
return new ArrayList<>();
}
}
}
// Main.java (Example Usage)
public class Main {
public static void main(String[] args) {
ReviewService reviewService = new ReviewServiceImplementation();
ProductService productService = new ProductService(reviewService);
System.out.println(productService.getProductDetails("123"));
System.out.println(productService.getProductDetails("456"));
}
}