A class should have only one reason to change.
A single OrderService that does everything. Any change to pricing, validation, or notification logic modifies the same class.
Extract responsibilities that change at different rates into separate classes, then compose them via orchestration.
// ─── EXAMPLE 1 ──────────────────────────────────────────────────────────────
// WHAT WE ARE IMPLEMENTING:
// An order processing workflow composed of highly cohesive, single-focus
// subservices.
//
// WHERE THE PRINCIPLE FITS IN:
// The principle separates OrderValidator (validation), PricingService
// (pricing), OrderRepository (DB persistence), and NotificationService
// (notifications) instead of keeping them in a single massive OrderService.
// ────────────────────────────────────────────────────────────────────────────
// --- Model ---
class Order {
private final String id;
private final String userId;
private final double amount;
private final String couponCode;
public Order(String id, String userId, double amount, String couponCode) {
this.id = id;
this.userId = userId;
this.amount = amount;
this.couponCode = couponCode;
}
public String getId() { return id; }
public String getUserId() { return userId; }
public double getAmount() { return amount; }
public String getCouponCode() { return couponCode; }
}
// --- Single Responsibility 1: Validation ---
class OrderValidator {
public void validate(Order order) {
if (order.getAmount() <= 0) throw new IllegalArgumentException("Amount must be positive");
if (order.getUserId() == null || order.getUserId().isBlank())
throw new IllegalArgumentException("User ID required");
System.out.println(" [Validator] Order " + order.getId() + " validated");
}
}
// --- Single Responsibility 2: Pricing ---
class PricingService {
public double applyDiscount(Order order) {
double discount = 0;
if (order.getCouponCode() != null && order.getCouponCode().equals("SAVE10")) {
discount = order.getAmount() * 0.10;
}
double finalAmount = order.getAmount() - discount;
System.out.println(" [Pricing] Discount: " + discount + ", Final: " + finalAmount);
return finalAmount;
}
}
// --- Single Responsibility 3: Persistence ---
class OrderRepository {
public void save(Order order) {
System.out.println(" [Repository] Order " + order.getId() + " saved to DB");
}
}
// --- Single Responsibility 4: Notification ---
class NotificationService {
public void sendConfirmation(String userId, String orderId) {
System.out.println(" [Notification] Confirmation sent to user " + userId + " for order " + orderId);
}
}
// --- Orchestrator (thin coordinator) ---
class OrderApplicationService {
private final OrderValidator validator;
private final PricingService pricingService;
private final OrderRepository repository;
private final NotificationService notificationService;
public OrderApplicationService(OrderValidator validator, PricingService pricingService,
OrderRepository repository, NotificationService notificationService) {
this.validator = validator;
this.pricingService = pricingService;
this.repository = repository;
this.notificationService = notificationService;
}
public void placeOrder(Order order) {
validator.validate(order);
pricingService.applyDiscount(order);
repository.save(order);
notificationService.sendConfirmation(order.getUserId(), order.getId());
}
}
// --- Client ---
public class Main {
public static void main(String[] args) {
OrderValidator validator = new OrderValidator();
PricingService pricing = new PricingService();
OrderRepository repo = new OrderRepository();
NotificationService notif = new NotificationService();
OrderApplicationService app = new OrderApplicationService(validator, pricing, repo, notif);
Order order = new Order("ORD-001", "user-42", 250.0, "SAVE10");
app.placeOrder(order);
}
}