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 }