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
Early bird: €100 off with code EARLYBIRD100
🔒 Hello, this is Valentina with a premium issue of the Optivem Journal. I help Engineering Leaders & Senior Software Developers apply TDD in Legacy Code.
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

