Scenarios expose collaborators
A checkout flow reveals different collaborators than a refund or shipment exception flow.
Use-case-driven object modelling sits between raw requirements and class design. The idea is simple: do not invent objects in isolation. Let the scenarios reveal which objects must collaborate, which responsibilities are stable, and where policies or state transitions belong. This is one of the easiest ways to keep a Java design behavior-centered.
In e-commerce, the same Order may participate in checkout, payment retries, cancellation, refund, and shipment tracking. Use-case-driven modeling prevents teams from shaping the object only around persistence needs while ignoring the richer interactions the system must support.
Use-case-driven object modelling starts with behavior and then discovers structure. Instead of asking "which entities do we store?", it asks "what happens in the scenario, and which objects must collaborate to make it happen responsibly?"
That approach is especially useful in Java because framework defaults can otherwise push the design toward entity-first CRUD thinking. By centering the scenario, you give the model a better chance to carry domain behavior and orchestration in honest places.
The result is a design where classes are easier to justify because they answer to concrete flows, not just to generic technical categories.
A checkout flow reveals different collaborators than a refund or shipment exception flow.
An object is easier to model well when you see what work it must do across scenarios.
Alternate flows reveal which policies and abstractions are likely to vary in the future.
package org.javaomnibus.ecommerce.process;
public final class CancelOrderUseCase {
private final RefundPolicy refundPolicy;
private final InventoryReleaseService inventoryReleaseService;
public CancelOrderUseCase(RefundPolicy refundPolicy, InventoryReleaseService inventoryReleaseService) {
this.refundPolicy = refundPolicy;
this.inventoryReleaseService = inventoryReleaseService;
}
public void handle(Order order) {
order.cancel(refundPolicy);
inventoryReleaseService.release(order);
}
}
interface RefundPolicy {
RefundDecision evaluate(Order order);
}
interface InventoryReleaseService {
void release(Order order);
}
If you follow scenarios too literally, the design can become overly tailored to current flows. Balance scenario insight with stable domain concepts that can outlive one use case.