UseCaseBuilder.java
package com.guinetik.hexafun.hexa;
import com.guinetik.hexafun.HexaApp;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
/**
* Builder for creating HexaApp instances with use cases, ports, and adapters.
*
* <p>Supports fluent DSL with implicit closure - each useCase() call
* automatically closes the previous one.
*
* <p>Example:
* <pre class="language-java">{@code
* HexaFun.dsl()
* .withPort(TaskRepository.class, new InMemoryTaskRepository())
* .withAdapter(TO_DTO, task -> new TaskDTO(task.id(), task.title()))
* .useCase(Keys.CREATE)
* .validate(validator)
* .handle(handler)
* .useCase(Keys.DELETE)
* .handle(deleteHandler)
* .build();
* }</pre>
*/
public class UseCaseBuilder {
private final Map<String, UseCase<?, ?>> useCases = new HashMap<>();
private final Map<Class<?>, Object> ports = new HashMap<>();
private final Map<String, Function<?, ?>> adapters = new HashMap<>();
// Pending registration - committed on next useCase() or build()
private String pendingName;
private UseCase<?, ?> pendingUseCase;
/**
* Register a port (output adapter) by its type.
* Ports are registered when build() is called.
*
* <p>Example:
* <pre class="language-java">{@code
* HexaFun.dsl()
* .withPort(TaskRepository.class, new InMemoryTaskRepository())
* .withPort(EmailService.class, new SmtpEmailService())
* .useCase(...)
* .build();
* }</pre>
*
* @param type The interface/class type to register
* @param impl The implementation instance
* @param <T> The port type
* @return This builder for chaining
*/
public <T> UseCaseBuilder withPort(Class<T> type, T impl) {
ports.put(type, impl);
return this;
}
/**
* Register an adapter with a type-safe key.
* Adapters transform data from one type to another.
*
* <p>Example:
* <pre class="language-java">{@code
* HexaFun.dsl()
* .withAdapter(TO_INVENTORY, req -> new InventoryCheck(req.itemId()))
* .withAdapter(TO_PAYMENT, req -> new PaymentRequest(req.total()))
* .useCase(...)
* .build();
* }</pre>
*
* @param key The type-safe adapter key
* @param adapter The transformation function
* @param <From> The source type
* @param <To> The target type
* @return This builder for chaining
*/
public <From, To> UseCaseBuilder withAdapter(
AdapterKey<From, To> key,
Function<From, To> adapter
) {
adapters.put(key.name(), adapter);
return this;
}
/**
* Start defining a use case with a type-safe key.
* Implicitly closes any previous use case definition.
*
* @param key The type-safe key for this use case
* @param <I> The input type of the use case
* @param <O> The output type of the use case
* @return A new UseCaseInputStep for chaining
*/
public <I, O> UseCaseInputStep<I, O> useCase(UseCaseKey<I, O> key) {
commitPending();
return new UseCaseInputStep<>(key.name(), this);
}
/**
* Stage a use case for registration. Will be committed on next useCase() or build().
*/
<I, O> void stage(String name, UseCase<I, O> useCase) {
commitPending();
this.pendingName = name;
this.pendingUseCase = useCase;
}
/**
* Commit pending use case to the registry.
*/
private void commitPending() {
if (pendingName != null && pendingUseCase != null) {
useCases.put(pendingName, pendingUseCase);
pendingName = null;
pendingUseCase = null;
}
}
/**
* Build a HexaApp with all the registered use cases and ports.
* @return A new HexaApp instance
*/
public HexaApp build() {
commitPending();
HexaApp app = HexaApp.create();
// Register ports
for (Map.Entry<Class<?>, Object> entry : ports.entrySet()) {
registerPort(app, entry.getKey(), entry.getValue());
}
// Register adapters
for (Map.Entry<String, Function<?, ?>> entry : adapters.entrySet()) {
registerAdapter(app, entry.getKey(), entry.getValue());
}
// Register use cases
for (Map.Entry<String, UseCase<?, ?>> entry : useCases.entrySet()) {
registerUseCase(app, entry.getKey(), entry.getValue());
}
return app;
}
@SuppressWarnings({ "unchecked", "rawtypes" })
private void registerPort(HexaApp app, Class type, Object impl) {
app.port(type, impl);
}
@SuppressWarnings({ "unchecked", "rawtypes" })
private void registerAdapter(HexaApp app, String name, Function adapter) {
app.withAdapter(name, adapter);
}
@SuppressWarnings({ "unchecked", "rawtypes" })
private void registerUseCase(HexaApp app, String name, UseCase useCase) {
app.withUseCase(name, useCase);
}
}