Crear un caso de uso
En el paso anterior evolucionaste el aggregate Task agregandole title, type y un factory method. Los paquetes usecase/ y service/ siguen vacios. En este paso vas a llenarlos con tu primer caso de uso: crear una tarea.
Que es un caso de uso en esta arquitectura
Un caso de uso representa una operacion que un actor externo puede ejecutar sobre el dominio. En el arquetipo, se modela como una interfaz en el paquete usecase/ con el sufijo UseCase:
public interface CreateTaskUseCase {
String create(CreateTaskCommand command);
}
La interfaz vive en task.usecase porque es un contrato publico — cualquier adaptador inbound (un controller, un listener, una tarea programada) puede depender de ella sin acoplarse a la implementacion.
El nombre del metodo no repite la accion completa — create es suficiente porque la interfaz ya dice CreateTaskUseCase. La convencion es usar verbos directos: create, update, remove, find.
Command, parametros directos, o ambos
No todo caso de uso necesita un Command, y no son mutuamente excluyentes. El criterio es la cohesion: si un grupo de parametros representa un concepto que tiene sentido nombrar como unidad, encapsulalo en un Command. Si son valores sueltos que se entienden por contexto, parametros directos son suficientes. Ambos enfoques pueden combinarse en una misma firma.
// Command: los datos de creacion forman un concepto cohesivo
CreateTaskUseCase.create(CreateTaskCommand command)
// Parametro directo: un solo valor obvio
CancelTaskUseCase.cancel(String taskId)
// Combinado: un identificador directo + un Command con los datos de la operacion
AddCartItemsUseCase.addInBulk(String cartId, Collection<AddItemCommand> items)
Para crear una tarea tenemos dos campos que forman un concepto cohesivo (titulo y tipo), asi que un Command tiene sentido. Crealo en el mismo paquete task.usecase:
@Data
public class CreateTaskCommand {
private String title;
private IssueType type;
}
Implementar el servicio
La interfaz define el que. Ahora necesitas el como. Los servicios de aplicacion viven en el paquete task.service e implementan las interfaces de los casos de uso:
@Service
@RequiredArgsConstructor
class TaskService implements CreateTaskUseCase {
private final TaskPersistencePort persistencePort;
@Override
public String create(CreateTaskCommand command) {
Task task = Task.create(command.getTitle(), command.getType());
persistencePort.save(task);
return task.getId();
}
}
Hay dos decisiones de diseno importantes aqui:
La clase es package-private (sin public). Nadie fuera del paquete service deberia conocer esta clase — los consumidores dependen de CreateTaskUseCase, no de TaskService. Spring la descubre igualmente gracias a @Service.
El servicio coordina las piezas: recibe el comando, interactua con el dominio y delega la persistencia al output port. La distribucion de logica de negocio entre el servicio y el dominio se explica en detalle en Arquitectura.
Verificar
El proyecto ya incluye la infraestructura de testing BDD con Cucumber. Ejecuta los tests para confirmar que todo conecta correctamente:
mvn test
Siguiente paso
Ahora tenes un caso de uso funcionando, pero solo es accesible desde codigo. En el siguiente paso vas a exponer un endpoint REST para que los clientes puedan crear tareas via HTTP.