Ports
A port declares, in YAML, a call to another system. The generator writes the interface; at run time a proxy factory implements it. There is no adapter class to write.
In hexagonal terms these are outbound ports: the application knows the interface, the technology is outside.
| Direction | What | Namespace |
|---|---|---|
| inbound | OrderUseCase - generated by hexagonal-springboot | inboundPort |
| outbound (this section) | port: - HTTP, DBMS, gRPC | port |
| outbound, persistence | OrderRepositoryPort - generated by the DDD bundles | domainRepository |
| Type | For | At run time |
|---|---|---|
http | REST APIs of other systems | HttpServiceProxyFactory - or a Spring @HttpExchange client with httpPort: spring |
dbms | SQL on a database | DbmsServiceProxyFactory |
proto | gRPC | a generated .proto service definition |
The shape
port:
ServiceName:
meta: <type> [options]
methods:
methodName:
meta: method [annotations]
params:
paramName: ParamType
return: ReturnType
Where the interface goes
namespace:
port: com.example.application.port.out
Not under domain: the contract is written with application DTOs and, when paged, Spring's
Page - it is the application's port. A domain service's uses: naming one is ignored with a
warning.
Registering the proxies: auto-configuration
From 3.0.261023 the runtime proxies (HTTP ports in their default mode, DBMS ports) become beans with
nothing on the application class. The runtime libraries (elasticore-springboot3 / -springboot4)
have a Spring Boot auto-configuration, io.elasticore.springboot.autoconfigure.ElastiCoreAutoConfiguration:
@SpringBootApplication // nothing else - ports are found in this package and below
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
It looks for ports (@ExternalService, @DbmsService) in the package of the @SpringBootApplication
class and below - where Spring Boot finds entities and repositories - so a @SpringBootTest starting
that class gets the port beans too. Only if it finds ports does it register:
- the ports;
DbTransactionGateway, which runs the DBMS ports' SQL, when there is oneEntityManagerFactoryor a@Primaryone;ApplicationContextProvider;- the banner.
An application without ports gets nothing.
@EnableElastiCorestill works. With it, each port and bean is registered once; there is no need to remove it.- Beans you already have win: a
@ComponentScan("io.elasticore.springboot3"), or a@Beanfor a port or forDbmsSqlExecutor. A@Primarybean of yours that wraps the proxy keeps the proxy (@Qualifier("CourierPort")). - Ports outside the application class's package are not found, including packages added with
@SpringBootApplication(scanBasePackages = ...). Use@AutoConfigurationPackage(basePackages = ...)(it also widens entity and repository scanning),@EnableElastiCoreon a class in that package, or a@Bean. An unregistered port cannot be injected:No qualifying bean of type 'ShopReportService'. - Ports that share a simple name (
shop.NotificationPort,billing.NotificationPort) are all registered under their full class names, with a warning. Inject them by type or with@Qualifier("<full class name>"). - Two or more
EntityManagerFactorybeans and none@Primary: no executor is created. Mark one@Primaryor declare aDbmsSqlExecutorbean. - Switching it off:
elasticore.autoconfigure.enabled=false; then use@EnableElastiCore.elasticore.banner.enabled=falsesilences the banner. - A plain Spring context without Spring Boot still needs
@EnableElastiCore(io.elasticore.springboot3.EnableElastiCore- the Boot 4 runtime has the same package).
The proxies look up their beans (the DBMS executor, HTTP authentication and converters) in their own bean factory, so tests that start several contexts do not see each other's beans.
Up to 3.0.261022-rc1, @EnableElastiCore is required: without it the ports are not beans, and
@ComponentScan("io.elasticore.springboot3") does not register them.
Ports with httpPort: spring are Spring's own clients and need neither.
gRPC
port:
UserGrpcService:
meta: proto @server @client
methods:
getUser:
meta: method -- read a user
params:
input: UserProtoInput
return: UserProtoOutput
@server generates the server side, @client the client; the .proto service definition is
generated.
Next: HTTP ports, DBMS ports.