UseCaseHandler.java

package com.guinetik.hexafun.hexa;

import com.guinetik.hexafun.HexaApp;

/**
 * Abstract base class for use case handlers that need access to ports.
 *
 * <p>This class provides a convenient way to implement use cases that require
 * infrastructure dependencies (ports) while maintaining the functional interface
 * contract of {@link UseCase}.</p>
 *
 * <h2>When to use UseCaseHandler</h2>
 * <ul>
 *   <li>When your use case needs to access repositories, external services, or other ports</li>
 *   <li>When you want to externalize handler logic into dedicated classes</li>
 *   <li>When handlers need to be tested with different port implementations</li>
 * </ul>
 *
 * <h2>When to use lambdas instead</h2>
 * <ul>
 *   <li>For simple, pure transformations with no port dependencies</li>
 *   <li>For one-liner handlers that don't need testing in isolation</li>
 * </ul>
 *
 * <h2>Example</h2>
 * <pre class="language-java">{@code
 * public class CreateTaskHandler extends UseCaseHandler<CreateTask, Result<Task>> {
 *     public CreateTaskHandler(HexaApp app) {
 *         super(app);
 *     }
 *
 *     @Override
 *     public Result<Task> apply(CreateTask input) {
 *         TaskRepository repo = port(TaskRepository.class);
 *         Task task = Task.create(input.title());
 *         return Result.ok(repo.save(task));
 *     }
 * }
 *
 * // Registration
 * HexaApp app = HexaFun.dsl()
 *     .port(TaskRepository.class, new InMemoryTaskRepo())
 *     .useCase(CREATE_TASK)
 *         .handle(new CreateTaskHandler(app))
 *     .build();
 * }</pre>
 *
 * @param <I> The input type for this use case
 * @param <O> The output type for this use case
 * @see UseCase
 * @see HexaApp#port(Class)
 */
public abstract class UseCaseHandler<I, O> implements UseCase<I, O> {

    /**
     * Reference to the HexaApp for port access.
     */
    protected final HexaApp app;

    /**
     * Creates a new handler with access to the given HexaApp.
     *
     * @param app The HexaApp instance for port access
     */
    protected UseCaseHandler(HexaApp app) {
        this.app = app;
    }

    /**
     * Convenience method to retrieve a port by its type.
     *
     * <p>This is a shorthand for {@code app.port(type)}.</p>
     *
     * @param type The interface/class type to retrieve
     * @param <T> The port type
     * @return The registered implementation
     * @throws IllegalArgumentException if no port is registered for the given type
     */
    protected <T> T port(Class<T> type) {
        return app.port(type);
    }

    /**
     * Check if a port is registered for the given type.
     *
     * @param type The interface/class type to check
     * @return true if a port is registered, false otherwise
     */
    protected boolean hasPort(Class<?> type) {
        return app.hasPort(type);
    }
}