Override a write use case
Goal: add city-specific behaviour to a write — here, reject creating an event whose capacity exceeds a city limit, then fall back to the platform logic — reusing the platform implementation instead of re-writing it.
- Seam:
CreateEventUseCase<T extends EventDto, R extends EventRequestDto>. - Door:
extends DefaultCreateEventUseCase(reuse the platform body). - City files: one
@Service. - Applies to: both topologies.
Recipe
Extend the platform default, add your rule, then delegate to super. Because the platform
default carries @Transactional and @CacheEvict on execute, and those annotations act
on the called method, you must re-declare them on your override — a super.execute(…)
call is a self-invocation and would not re-trigger them.
// com.modelcity.leisure.events.usecase.MyCreateEventUseCase
@Service
public class MyCreateEventUseCase extends DefaultCreateEventUseCase {
private static final int CITY_CAPACITY_LIMIT = 5000;
public MyCreateEventUseCase(EventStore<? extends EventView, EventRequestDto> eventStore,
EventWriteValidator validator,
StripeFacade stripeFacade,
SystemTrailGenerator trails) {
super(eventStore, validator, stripeFacade, trails);
}
@Override
@Transactional
@CacheEvict(cacheNames = CacheNames.EVENTS, allEntries = true)
public EventDto execute(String sub, EventRequestDto request, String locale) {
if (request.getCapacity() != null && request.getCapacity() > CITY_CAPACITY_LIMIT) {
throw new ResponseStatusException(HttpStatus.UNPROCESSABLE_ENTITY,
"Event capacity exceeds the city limit of " + CITY_CAPACITY_LIMIT);
}
return super.execute(sub, request, locale); // platform validation, Stripe, persist, trail
}
}
DefaultCreateEventUseCase is @ModelCityDisabledIfInherited; your subclass covers the
CreateEventUseCase seam, so the default's bean definition is removed at startup and there is
no duplicate.
Notes
- Two doors, pick per need.
extends DefaultCreateEventUseCasereuses the whole body (best for add a guard / a side effect).implements CreateEventUseCase<…>from scratch is better when you replace the logic entirely or bind subtype DTOs (see Expose a new field). - Always re-declare the annotations you want. Dropping
@CacheEvicthere would leave theeventslist cache stale after a create; dropping@Transactionalwould lose atomicity. - Reuse the platform
EventWriteValidator,StripeFacadeandSystemTrailGenerator— they are ordinary beans injected through the inherited constructor. - A thrown
ResponseStatusExceptionis turned into the right HTTP status by the platform'sGlobalExceptionHandler(in commons), so you get a clean422without extra wiring. For a domain error with a code, throwModelCityExceptioninstead.
Verify
POST /events with capacity > 5000 returns the validation error; a normal create still
persists, evicts the list cache and writes the audit trail exactly as before.