ATDD in Legacy Code: catch regression bugs before production.
Hands-on work with your teams. Limited spaces for 2027.
📅 Join our next live course: ATDD, Clean Architecture, Pipelines
Ealy bird: €100 off with code EARLYBIRD100
Your SubscriptionService has 800 lines.
It activates subscriptions, handles cancellations, validates plan changes, checks expiration...
Meanwhile Subscription is just getters and setters.
That’s the problem.
❌Your Domain Is Just Data (Anemic Domain)
public class Subscription {
private String customerId;
private SubscriptionPlan plan;
private SubscriptionStatus status;
private LocalDate startedAt;
private LocalDate expiresAt;
private BigDecimal price;
public String getCustomerId() {
return customerId;
}
public void setCustomerId(String customerId) {
this.customerId = customerId;
}
public SubscriptionPlan getPlan() { ... }
public void setPlan(SubscriptionPlan plan) { ... }
public SubscriptionStatus getStatus() { ... }
public void setStatus(SubscriptionStatus status) { ... }
public LocalDate getStartedAt() { ... }
public void setStartedAt(LocalDate startedAt) { ... }
public LocalDate getExpiresAt() { ... }
public void setExpiresAt(LocalDate expiresAt) { ... }
public BigDecimal getPrice() { ... }
public void setPrice(BigDecimal price) { ... }
}There isn’t much here.
The Subscription holds data.
The business rules live somewhere else:
public class SubscriptionService {
public void cancel(Subscription subscription) {
if (subscription.getStartedAt().plusDays(7).isBefore(LocalDate.now())) {
throw new IllegalStateException(
"Free cancellation is only available during the 7-day trial"
);
}
subscription.setStatus(SubscriptionStatus.CANCELLED);
}
public void changePlan(
Subscription subscription,
SubscriptionPlan newPlan
) {
if (newPlan == subscription.getPlan()) {
throw new IllegalArgumentException(
"Subscription is already on this plan"
);
}
subscription.setPlan(newPlan);
subscription.setPrice(newPlan.getPrice());
}
public void renew(Subscription subscription) {
if (subscription.getStatus() != SubscriptionStatus.ACTIVE) {
throw new IllegalStateException(
"Only active subscriptions can be renewed"
);
}
subscription.setExpiresAt(
subscription.getExpiresAt().plusMonths(1)
);
}
}At first, this doesn’t look terrible.
But keep adding business rules.
Soon you have:
SubscriptionService
├── startTrial()
├── cancel()
├── changePlan()
├── renew()
├── pause()
├── resume()
├── extendTrial()
├── changeBillingCycle()
├── updatePaymentMethod()
└── ...And your Subscription is still just data.
That’s where the real problems appear:
Rules get scattered across services
The same rules get duplicated in different places
Rules can be bypassed by calling setters directly
Changing a business rule means hunting through services to find everywhere it is implemented
The Subscription object can’t protect its own state

