View Javadoc
1   package com.guinetik.hexafun.hexa;
2   
3   import com.guinetik.hexafun.HexaApp;
4   
5   /**
6    * Abstract base class for use case handlers that need access to ports.
7    *
8    * <p>This class provides a convenient way to implement use cases that require
9    * infrastructure dependencies (ports) while maintaining the functional interface
10   * contract of {@link UseCase}.</p>
11   *
12   * <h2>When to use UseCaseHandler</h2>
13   * <ul>
14   *   <li>When your use case needs to access repositories, external services, or other ports</li>
15   *   <li>When you want to externalize handler logic into dedicated classes</li>
16   *   <li>When handlers need to be tested with different port implementations</li>
17   * </ul>
18   *
19   * <h2>When to use lambdas instead</h2>
20   * <ul>
21   *   <li>For simple, pure transformations with no port dependencies</li>
22   *   <li>For one-liner handlers that don't need testing in isolation</li>
23   * </ul>
24   *
25   * <h2>Example</h2>
26   * <pre class="language-java">{@code
27   * public class CreateTaskHandler extends UseCaseHandler<CreateTask, Result<Task>> {
28   *     public CreateTaskHandler(HexaApp app) {
29   *         super(app);
30   *     }
31   *
32   *     @Override
33   *     public Result<Task> apply(CreateTask input) {
34   *         TaskRepository repo = port(TaskRepository.class);
35   *         Task task = Task.create(input.title());
36   *         return Result.ok(repo.save(task));
37   *     }
38   * }
39   *
40   * // Registration
41   * HexaApp app = HexaFun.dsl()
42   *     .port(TaskRepository.class, new InMemoryTaskRepo())
43   *     .useCase(CREATE_TASK)
44   *         .handle(new CreateTaskHandler(app))
45   *     .build();
46   * }</pre>
47   *
48   * @param <I> The input type for this use case
49   * @param <O> The output type for this use case
50   * @see UseCase
51   * @see HexaApp#port(Class)
52   */
53  public abstract class UseCaseHandler<I, O> implements UseCase<I, O> {
54  
55      /**
56       * Reference to the HexaApp for port access.
57       */
58      protected final HexaApp app;
59  
60      /**
61       * Creates a new handler with access to the given HexaApp.
62       *
63       * @param app The HexaApp instance for port access
64       */
65      protected UseCaseHandler(HexaApp app) {
66          this.app = app;
67      }
68  
69      /**
70       * Convenience method to retrieve a port by its type.
71       *
72       * <p>This is a shorthand for {@code app.port(type)}.</p>
73       *
74       * @param type The interface/class type to retrieve
75       * @param <T> The port type
76       * @return The registered implementation
77       * @throws IllegalArgumentException if no port is registered for the given type
78       */
79      protected <T> T port(Class<T> type) {
80          return app.port(type);
81      }
82  
83      /**
84       * Check if a port is registered for the given type.
85       *
86       * @param type The interface/class type to check
87       * @return true if a port is registered, false otherwise
88       */
89      protected boolean hasPort(Class<?> type) {
90          return app.hasPort(type);
91      }
92  }