Adding a feature¶
The order is the same idea in both architectures: start in the domain, end at the edges. Pick the checklist for the service's architecture.
Hexagonal services (job, crawler, application)¶
- Domain first — add the model, port interface(s), and a domain exception if needed.
- Service — implement the use-case interface(s) in
domain/service/. Inject ports only. - Persistence — entity → mapper → Panache repository implementing the port.
- REST — resource method → DTO(s) → an
ExceptionMapperif you added a new domain exception. - Config — add
@ConfigPropertykeys toapplication.propertieswith defaults; document dev-only keys inapplication-dev.properties. - DB migration — add a numbered SQL file under
db/init/for schema changes. Never rely ondrop-and-createin prod. - Tests — unit tests for service + mapper/DTO; component tests for the endpoint. See Testing.
Clean service (auth)¶
- Entity / domain service — add or extend a Layer 1 entity (invariants via guard methods) or a pure domain service.
- Use case — create
application/usecase/<UseCase>/withCommand,Handler,Response. The handler injects Layer 2 ports (repositories, domain services) — never adapters. - Ports — if the handler needs something from the outside, add an interface in
application/port/out/. - Adapters — implement the port in
adapter/out/persistence/(JPA entity + mapper + repository), and wire REST inadapter/in/rest/(DTO ↔ Command mapper, resource method). - Config / DB / Tests — same as steps 5–7 above. Handlers are unit-testable with mocked ports.
Definition of done¶
- Domain has no framework annotations.
- New exceptions have an
ExceptionMapper. - Schema change shipped as a numbered
db/init/file. - Unit + component tests added and green (
mvn -pl <module> test). - If the change touches a public endpoint, the OpenAPI contract in
api-contracts/src/main/resources/openapi/<service>.yamlis updated — the API reference renders from it automatically.