From c3d37fa06684940bcfd34e5314d0897ba1ab5db6 Mon Sep 17 00:00:00 2001 From: jaguililla Date: Wed, 14 Aug 2024 18:37:47 +0200 Subject: [PATCH 1/2] Organize imports --- .../appointments/ApplicationConfiguration.java | 8 +++++--- .../domain/AppointmentsService.java | 1 - .../jaguililla/appointments/domain/Checks.java | 4 ++-- .../controllers/AppointmentsController.java | 5 ++--- .../input/controllers/AppointmentsMapper.java | 1 - .../input/controllers/BaseController.java | 4 ++-- .../input/controllers/UsersController.java | 7 ++++--- .../JdbcTemplateAppointmentsRepository.java | 8 ++++---- .../JdbcTemplateUsersRepository.java | 17 ++++++++++------- .../jaguililla/appointments/ApplicationIT.java | 12 +++++------- .../github/jaguililla/appointments/Asserts.java | 4 ++-- .../jaguililla/appointments/TestTemplate.java | 4 ++-- .../domain/model/AppointmentTest.java | 11 +++++------ .../appointments/domain/model/UserTest.java | 7 +++---- 14 files changed, 46 insertions(+), 47 deletions(-) diff --git a/src/main/java/com/github/jaguililla/appointments/ApplicationConfiguration.java b/src/main/java/com/github/jaguililla/appointments/ApplicationConfiguration.java index 65be8db..25a02b4 100644 --- a/src/main/java/com/github/jaguililla/appointments/ApplicationConfiguration.java +++ b/src/main/java/com/github/jaguililla/appointments/ApplicationConfiguration.java @@ -1,22 +1,24 @@ package com.github.jaguililla.appointments; +import com.github.jaguililla.appointments.domain.AppointmentsNotifier; +import com.github.jaguililla.appointments.domain.AppointmentsRepository; +import com.github.jaguililla.appointments.domain.AppointmentsService; +import com.github.jaguililla.appointments.domain.UsersRepository; import com.github.jaguililla.appointments.output.notifiers.KafkaTemplateAppointmentsNotifier; import com.github.jaguililla.appointments.output.repositories.JdbcTemplateAppointmentsRepository; import com.github.jaguililla.appointments.output.repositories.JdbcTemplateUsersRepository; -import com.github.jaguililla.appointments.domain.*; import org.apache.kafka.clients.admin.AdminClientConfig; import org.apache.kafka.clients.admin.NewTopic; import org.apache.kafka.clients.consumer.ConsumerConfig; import org.apache.kafka.clients.producer.ProducerConfig; -import org.apache.kafka.common.serialization.StringSerializer; import org.apache.kafka.common.serialization.StringDeserializer; +import org.apache.kafka.common.serialization.StringSerializer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.kafka.core.*; - import javax.sql.DataSource; import java.util.Map; diff --git a/src/main/java/com/github/jaguililla/appointments/domain/AppointmentsService.java b/src/main/java/com/github/jaguililla/appointments/domain/AppointmentsService.java index d20a5e8..c1046d5 100644 --- a/src/main/java/com/github/jaguililla/appointments/domain/AppointmentsService.java +++ b/src/main/java/com/github/jaguililla/appointments/domain/AppointmentsService.java @@ -7,7 +7,6 @@ import com.github.jaguililla.appointments.domain.model.Appointment; import com.github.jaguililla.appointments.domain.model.User; - import java.util.HashSet; import java.util.List; import java.util.Set; diff --git a/src/main/java/com/github/jaguililla/appointments/domain/Checks.java b/src/main/java/com/github/jaguililla/appointments/domain/Checks.java index fe5a9fa..c09097e 100644 --- a/src/main/java/com/github/jaguililla/appointments/domain/Checks.java +++ b/src/main/java/com/github/jaguililla/appointments/domain/Checks.java @@ -1,11 +1,11 @@ package com.github.jaguililla.appointments.domain; -import java.time.LocalDateTime; - import static java.lang.String.format; import static java.util.Locale.US; import static java.util.Objects.requireNonNull; +import java.time.LocalDateTime; + public interface Checks { static String requireNonBlank(String value, String field) { diff --git a/src/main/java/com/github/jaguililla/appointments/input/controllers/AppointmentsController.java b/src/main/java/com/github/jaguililla/appointments/input/controllers/AppointmentsController.java index 3175300..96fcdb3 100644 --- a/src/main/java/com/github/jaguililla/appointments/input/controllers/AppointmentsController.java +++ b/src/main/java/com/github/jaguililla/appointments/input/controllers/AppointmentsController.java @@ -7,12 +7,11 @@ import com.github.jaguililla.appointments.http.controllers.messages.AppointmentRequest; import com.github.jaguililla.appointments.http.controllers.messages.AppointmentResponse; import com.github.jaguililla.appointments.http.controllers.messages.IdResponse; - -import java.util.List; -import java.util.UUID; import org.slf4j.Logger; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; +import java.util.List; +import java.util.UUID; @Controller final class AppointmentsController extends BaseController implements AppointmentsApi { diff --git a/src/main/java/com/github/jaguililla/appointments/input/controllers/AppointmentsMapper.java b/src/main/java/com/github/jaguililla/appointments/input/controllers/AppointmentsMapper.java index aaceeec..061df83 100644 --- a/src/main/java/com/github/jaguililla/appointments/input/controllers/AppointmentsMapper.java +++ b/src/main/java/com/github/jaguililla/appointments/input/controllers/AppointmentsMapper.java @@ -7,7 +7,6 @@ import com.github.jaguililla.appointments.http.controllers.messages.AppointmentRequest; import com.github.jaguililla.appointments.http.controllers.messages.AppointmentResponse; import com.github.jaguililla.appointments.http.controllers.messages.UserResponse; - import java.util.List; import java.util.Set; diff --git a/src/main/java/com/github/jaguililla/appointments/input/controllers/BaseController.java b/src/main/java/com/github/jaguililla/appointments/input/controllers/BaseController.java index e6b5893..8fd3569 100644 --- a/src/main/java/com/github/jaguililla/appointments/input/controllers/BaseController.java +++ b/src/main/java/com/github/jaguililla/appointments/input/controllers/BaseController.java @@ -1,13 +1,13 @@ package com.github.jaguililla.appointments.input.controllers; +import static org.slf4j.LoggerFactory.getLogger; + import jakarta.servlet.http.HttpServletRequest; import org.slf4j.Logger; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.ExceptionHandler; -import static org.slf4j.LoggerFactory.getLogger; - @Controller class BaseController { diff --git a/src/main/java/com/github/jaguililla/appointments/input/controllers/UsersController.java b/src/main/java/com/github/jaguililla/appointments/input/controllers/UsersController.java index a3b7be8..7b5704b 100644 --- a/src/main/java/com/github/jaguililla/appointments/input/controllers/UsersController.java +++ b/src/main/java/com/github/jaguililla/appointments/input/controllers/UsersController.java @@ -1,14 +1,15 @@ package com.github.jaguililla.appointments.input.controllers; +import static org.slf4j.LoggerFactory.getLogger; + import com.github.jaguililla.appointments.domain.AppointmentsService; import com.github.jaguililla.appointments.http.controllers.UsersApi; -import com.github.jaguililla.appointments.http.controllers.messages.*; +import com.github.jaguililla.appointments.http.controllers.messages.UserRequest; +import com.github.jaguililla.appointments.http.controllers.messages.UserResponse; import org.slf4j.Logger; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; -import static org.slf4j.LoggerFactory.getLogger; - @Controller final class UsersController extends BaseController implements UsersApi { diff --git a/src/main/java/com/github/jaguililla/appointments/output/repositories/JdbcTemplateAppointmentsRepository.java b/src/main/java/com/github/jaguililla/appointments/output/repositories/JdbcTemplateAppointmentsRepository.java index 526a065..c598477 100644 --- a/src/main/java/com/github/jaguililla/appointments/output/repositories/JdbcTemplateAppointmentsRepository.java +++ b/src/main/java/com/github/jaguililla/appointments/output/repositories/JdbcTemplateAppointmentsRepository.java @@ -6,14 +6,14 @@ import com.github.jaguililla.appointments.domain.AppointmentsRepository; import com.github.jaguililla.appointments.domain.model.Appointment; import com.github.jaguililla.appointments.domain.model.User; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; +import javax.sql.DataSource; import java.sql.ResultSet; import java.sql.SQLException; import java.util.*; import java.util.stream.Stream; -import javax.sql.DataSource; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; public class JdbcTemplateAppointmentsRepository implements AppointmentsRepository { diff --git a/src/main/java/com/github/jaguililla/appointments/output/repositories/JdbcTemplateUsersRepository.java b/src/main/java/com/github/jaguililla/appointments/output/repositories/JdbcTemplateUsersRepository.java index 6c49afd..5131c9a 100644 --- a/src/main/java/com/github/jaguililla/appointments/output/repositories/JdbcTemplateUsersRepository.java +++ b/src/main/java/com/github/jaguililla/appointments/output/repositories/JdbcTemplateUsersRepository.java @@ -1,17 +1,20 @@ package com.github.jaguililla.appointments.output.repositories; +import static java.util.Collections.emptySet; +import static java.util.Objects.requireNonNull; + import com.github.jaguililla.appointments.domain.UsersRepository; import com.github.jaguililla.appointments.domain.model.User; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.*; -import javax.sql.DataSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; - -import static java.util.Collections.emptySet; -import static java.util.Objects.requireNonNull; +import javax.sql.DataSource; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.UUID; public final class JdbcTemplateUsersRepository implements UsersRepository { diff --git a/src/test/java/com/github/jaguililla/appointments/ApplicationIT.java b/src/test/java/com/github/jaguililla/appointments/ApplicationIT.java index 2fe5c8e..3d2f29f 100644 --- a/src/test/java/com/github/jaguililla/appointments/ApplicationIT.java +++ b/src/test/java/com/github/jaguililla/appointments/ApplicationIT.java @@ -1,5 +1,10 @@ package com.github.jaguililla.appointments; +import static java.util.Objects.requireNonNull; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; + import com.github.jaguililla.appointments.http.controllers.messages.AppointmentRequest; import com.github.jaguililla.appointments.http.controllers.messages.AppointmentResponse; import org.junit.jupiter.api.AfterAll; @@ -8,22 +13,15 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.server.LocalServerPort; - import org.springframework.kafka.core.KafkaTemplate; import org.springframework.test.context.DynamicPropertyRegistry; import org.springframework.test.context.DynamicPropertySource; import org.testcontainers.containers.PostgreSQLContainer; import org.testcontainers.kafka.KafkaContainer; - import java.time.LocalDateTime; import java.util.Arrays; import java.util.UUID; -import static java.util.Objects.requireNonNull; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; - @SpringBootTest( classes = {Application.class}, webEnvironment = RANDOM_PORT diff --git a/src/test/java/com/github/jaguililla/appointments/Asserts.java b/src/test/java/com/github/jaguililla/appointments/Asserts.java index dde532a..89312a1 100644 --- a/src/test/java/com/github/jaguililla/appointments/Asserts.java +++ b/src/test/java/com/github/jaguililla/appointments/Asserts.java @@ -1,10 +1,10 @@ package com.github.jaguililla.appointments; -import org.junit.jupiter.api.function.Executable; - import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrowsExactly; +import org.junit.jupiter.api.function.Executable; + public interface Asserts { static void assertIllegalArgument(String message, Executable executable) { diff --git a/src/test/java/com/github/jaguililla/appointments/TestTemplate.java b/src/test/java/com/github/jaguililla/appointments/TestTemplate.java index f03fffd..4c4c1e3 100644 --- a/src/test/java/com/github/jaguililla/appointments/TestTemplate.java +++ b/src/test/java/com/github/jaguililla/appointments/TestTemplate.java @@ -1,5 +1,7 @@ package com.github.jaguililla.appointments; +import static org.slf4j.LoggerFactory.getLogger; + import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import org.slf4j.Logger; @@ -11,8 +13,6 @@ import org.springframework.web.client.ResponseErrorHandler; import org.springframework.web.client.RestTemplate; -import static org.slf4j.LoggerFactory.getLogger; - /** * Simplification of RestTemplate for testing. It holds the last received response to ease testing * flows. diff --git a/src/test/java/com/github/jaguililla/appointments/domain/model/AppointmentTest.java b/src/test/java/com/github/jaguililla/appointments/domain/model/AppointmentTest.java index 2d755e5..934c298 100644 --- a/src/test/java/com/github/jaguililla/appointments/domain/model/AppointmentTest.java +++ b/src/test/java/com/github/jaguililla/appointments/domain/model/AppointmentTest.java @@ -1,11 +1,5 @@ package com.github.jaguililla.appointments.domain.model; -import org.junit.jupiter.api.Test; - -import java.time.LocalDateTime; -import java.util.List; -import java.util.UUID; - import static com.github.jaguililla.appointments.Asserts.assertIllegalArgument; import static com.github.jaguililla.appointments.Asserts.assertNull; import static java.lang.String.format; @@ -13,6 +7,11 @@ import static java.util.Locale.US; import static org.junit.jupiter.api.Assertions.assertEquals; +import org.junit.jupiter.api.Test; +import java.time.LocalDateTime; +import java.util.List; +import java.util.UUID; + class AppointmentTest { @Test diff --git a/src/test/java/com/github/jaguililla/appointments/domain/model/UserTest.java b/src/test/java/com/github/jaguililla/appointments/domain/model/UserTest.java index 20d2799..db184d8 100644 --- a/src/test/java/com/github/jaguililla/appointments/domain/model/UserTest.java +++ b/src/test/java/com/github/jaguililla/appointments/domain/model/UserTest.java @@ -1,13 +1,12 @@ package com.github.jaguililla.appointments.domain.model; -import org.junit.jupiter.api.Test; - -import java.util.UUID; - import static com.github.jaguililla.appointments.Asserts.assertIllegalArgument; import static com.github.jaguililla.appointments.Asserts.assertNull; import static org.junit.jupiter.api.Assertions.assertEquals; +import org.junit.jupiter.api.Test; +import java.util.UUID; + class UserTest { @Test From ad95c8b5337aea9bab0b0f1c05b48b9d8b4607e3 Mon Sep 17 00:00:00 2001 From: jaguililla Date: Wed, 14 Aug 2024 19:51:00 +0200 Subject: [PATCH 2/2] Apply received feedback and add new features - Package and publish API client - Build application image with Paketo - Tag and publish releases (Docker image, and optionally, application JAR) - Update documentation --- .github/workflows/build.yml | 42 ++++++++++++++ .github/workflows/push.yml | 25 --------- .github/workflows/release.yml | 57 +++++++++++++++++++ .mvn/parent.xml | 100 +++++++++++++++++++++++++++++++++- README.md | 41 +++++++++----- docker-compose.yml | 2 +- pom.xml | 9 +++ 7 files changed, 233 insertions(+), 43 deletions(-) create mode 100644 .github/workflows/build.yml delete mode 100644 .github/workflows/push.yml create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..22cf42a --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,42 @@ + +name: Build Branch +on: + push: + branches-ignore: [ main ] + +permissions: read-all + +jobs: + build: + name: Build + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: SDKMAN Cache + uses: actions/cache@v4 + with: + path: ~/.sdkman + key: "${{ runner.os }}-sdkman-${{ hashFiles('.sdkmanrc') }}" + restore-keys: "${{ runner.os }}-sdkman-" + - name: Maven Cache + uses: actions/cache@v4 + with: + path: ~/.m2/repository + key: "${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}" + restore-keys: "${{ runner.os }}-maven-" + - name: Install SDKMAN + run: curl -s "https://get.sdkman.io?rcupdate=false" | bash + - name: Build Application + run: | + source "$HOME/.sdkman/bin/sdkman-init.sh" + sdk env install + + ./mvnw + - name: Build Client + run: | + export CLIENT_PATH='target/generated-sources/openapi' + export CONTROLLERS_PATH='com/github/jaguililla/appointments/http/controllers' + + rm -rf "${CLIENT_PATH}/src/main/java/${CONTROLLERS_PATH}" + mvn -f "${CLIENT_PATH}/pom.xml" clean install diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml deleted file mode 100644 index d7c9c5f..0000000 --- a/.github/workflows/push.yml +++ /dev/null @@ -1,25 +0,0 @@ - -on: push - -permissions: read-all - -jobs: - push: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: actions/cache@v4 - with: - path: ~/.sdkman - key: "${{ runner.os }}-sdkman-${{ hashFiles('.sdkmanrc') }}" - restore-keys: "${{ runner.os }}-sdkman-" - - uses: actions/cache@v4 - with: - path: ~/.m2/repository - key: "${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}" - restore-keys: "${{ runner.os }}-maven-" - - run: curl -s "https://get.sdkman.io?rcupdate=false" | bash - - run: | - source "$HOME/.sdkman/bin/sdkman-init.sh" - sdk env install - ./mvnw diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..be17502 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,57 @@ + +name: Release Application +on: + push: + branches: [ main ] + +permissions: read-all + +jobs: + release: + name: Release + runs-on: ubuntu-latest + permissions: + contents: write + packages: write + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: SDKMAN Cache + uses: actions/cache@v4 + with: + path: ~/.sdkman + key: "${{ runner.os }}-sdkman-${{ hashFiles('.sdkmanrc') }}" + restore-keys: "${{ runner.os }}-sdkman-" + - name: Maven Cache + uses: actions/cache@v4 + with: + path: ~/.m2/repository + key: "${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}" + restore-keys: "${{ runner.os }}-maven-" + - name: Install SDKMAN + run: curl -s "https://get.sdkman.io?rcupdate=false" | bash + # Required for publishing (Maven settings.xml repository) + - name: Java Setup + uses: actions/setup-java@v4 + with: + java-version: '21' + distribution: temurin + - name: Publish and Tag Application + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + source "$HOME/.sdkman/bin/sdkman-init.sh" + sdk env install + + ./mvnw -B -P release + - name: Publish Client + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + export CLIENT_PATH='target/generated-sources/openapi' + export CONTROLLERS_PATH='com/github/jaguililla/appointments/http/controllers' + export REPOSITORY='https://maven.pkg.github.com/jaguililla/spring_template' + export ALT_REPOSITORY="altDeploymentRepository=github::default::${REPOSITORY}" + + rm -rf "${CLIENT_PATH}/src/main/java/${CONTROLLERS_PATH}" + mvn -f "${CLIENT_PATH}/pom.xml" -B -D ${ALT_REPOSITORY} clean deploy diff --git a/.mvn/parent.xml b/.mvn/parent.xml index 8a915b3..0e4d745 100644 --- a/.mvn/parent.xml +++ b/.mvn/parent.xml @@ -29,6 +29,8 @@ ${project.groupId}.${project.artifactId}.http ${openapi.package}.controllers ${openapi.package}.client + ${project.groupId}/${project.artifactId} + verify undertow ui @@ -108,19 +110,30 @@ spring-boot-maven-plugin + ${image.registry}/${image.name}:${project.version} - ${project.groupId}/${project.artifactId}:${project.version} - ${project.groupId}/${project.artifactId}:latest - ${project.groupId}/${project.artifactId} + ${image.registry}/${image.name}:latest -XX:+AlwaysPreTouch -XX:+UseParallelGC -XX:+UseNUMA + true + true + true ${java.version} + + + image + + build-image + + verify + + org.apache.maven.plugins @@ -207,6 +220,9 @@ true ${openApiNullable} + ${project.groupId} + ${project.artifactId}-client + ${project.version} @@ -231,5 +247,83 @@ + + + release + + + ${release.goal} + + + + org.springframework.boot + spring-boot-maven-plugin + + + + ${env.GITHUB_ACTOR} + ${env.GITHUB_TOKEN} + + + true + + + + org.codehaus.mojo + exec-maven-plugin + 3.3.0 + + git + + + + + config + verify + + exec + + + + config + --global + user.name + ${env.GITHUB_ACTOR} + + + + + tag + verify + + exec + + + + tag + -m + Release ${project.version} + ${project.version} + + + + + push + verify + + exec + + + + push + --tags + + + + + + + + diff --git a/README.md b/README.md index 409c5d2..f8698ea 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ -> # ABOUT +> # ๐ŸŽฏ ABOUT > This is a 'best practices' template project. However, it is an opinionated take on that. > > DISCLAIMER: I'm by no means an expert on Spring Boot (it's not even my preferred tool), one reason @@ -16,15 +16,19 @@ > > Have fun! -# Appointments +# ๐Ÿ—“๏ธ Appointments Application to create appointments (REST API). Appointments are stored in a relational DB (Postgres), and their creation/deletion is published to a Kafka broker. -## Architecture -* Hexagonal Architecture +## ๐Ÿ“˜ Architecture +* [Hexagonal]/[Onion]/[Clean] Architecture * OpenAPI code generation (server and client) -## Stack +[Hexagonal]: https://alistair.cockburn.us/hexagonal-architecture +[Onion]: https://jeffreypalermo.com/2008/07/the-onion-architecture-part-1 +[Clean]: https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html + +## ๐Ÿงฐ Stack * Java 21 * Spring 3.3 (configurable server, 'undertow' by default) * Actuator (healthcheck, etc.) @@ -32,26 +36,26 @@ Application to create appointments (REST API). Appointments are stored in a rela * Postgres * Kafka -## Runtime +## ๐ŸŽ๏ธ Runtime * Cloud Native Buildpacks (building) * Docker Compose (local environment with the infrastructure) -## Test +## ๐Ÿงช Test * ArchUnit (preferred over Java modules: it allows naming checks, etc.) * Testcontainers (used to provide a test instance of Postgres and Kafka) -## Development +## โš’๏ธ Development * SDKMAN (allows to use simpler runners on CI) * Maven Wrapper (Maven can be provided by SDKMAN, however, Maven Wrapper has better IDE support) * Editorconfig (supported by a lot of editors, rules limited though) * CI pipelines for GitHub and GitLab -## Requirements +## ๐Ÿ“‘ Requirements * Docker Compose * JDK 21+ * SDKMAN (optional, recommended) -## Design +## ๐Ÿ“š Design * The REST API controller and client are generated from the OpenAPI spec at build time. * Hexagonal Architecture: domain, ports, and adapters. * Use cases are 'one responsibility services'. Start with services, split when they get bigger. @@ -77,7 +81,16 @@ Application to create appointments (REST API). Appointments are stored in a rela - **appointments.domain.model**: holds the business entities. These are the data structures used by the business logic. Follows the same access rules as its parent package. -## Design Decisions +## ๐Ÿ“– Terms +* UseCase/Case +* Service +* Adapter +* Port +* Domain +* Input/driver adapter +* Output/driven adapter + +## ๐Ÿค” Design Decisions * Minimal: don't use libraries to implement easy stuff (even if that's boring). * Prefer flat structure (avoid empty parent packages). * Less coupling with Spring (easier to migrate, to other frameworks/toolkits). @@ -92,10 +105,10 @@ Application to create appointments (REST API). Appointments are stored in a rela a container for this application. * Atomicity in notifiers (with outbox pattern) should be done with a different notifier adapter. -## Set up +## ๐ŸŽš๏ธ Set up * `sdk env install` -## Commands +## โ–ถ๏ธ Commands All commands assume a Unix like OS. The most important commands to operate the project are: @@ -110,7 +123,7 @@ To run or deploy the application: * Run JAR locally: `java -jar target/appointments-0.1.0.jar` * Run container: `docker-compose --profile local up` -## Service Management +## ๐Ÿค– Service Management * You can check the API spec using [Swagger UI](http://localhost:8080/swagger-ui/index.html). ### Docker diff --git a/docker-compose.yml b/docker-compose.yml index b3183ab..7cc21e4 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -40,7 +40,7 @@ services: depends_on: - postgres - kafka - image: com.github.jaguililla/appointments + image: ghcr.io/jaguililla/spring_template/com.github.jaguililla/appointments environment: GLOBAL_LOG_LEVEL: warn APPLICATION_LOG_LEVEL: info diff --git a/pom.xml b/pom.xml index 3e52e7a..d61dfa4 100644 --- a/pom.xml +++ b/pom.xml @@ -28,8 +28,17 @@ ${openapi.package}.client undertow ui + ghcr.io/jaguililla/spring_template + deploy + + + github + https://maven.pkg.github.com/jaguililla/spring_template + + + org.springframework.boot