Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create first release #8

Merged
merged 2 commits into from
Aug 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -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
25 changes: 0 additions & 25 deletions .github/workflows/push.yml

This file was deleted.

57 changes: 57 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -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
100 changes: 97 additions & 3 deletions .mvn/parent.xml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
<openapi.package>${project.groupId}.${project.artifactId}.http</openapi.package>
<controllers.package>${openapi.package}.controllers</controllers.package>
<client.package>${openapi.package}.client</client.package>
<image.name>${project.groupId}/${project.artifactId}</image.name>
<release.goal>verify</release.goal> <!-- verify | deploy -->
<spring.server>undertow</spring.server> <!-- undertow | jetty -->
<openapi.integration>ui</openapi.integration> <!-- ui | api -->

Expand Down Expand Up @@ -108,19 +110,30 @@
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<image>
<name>${image.registry}/${image.name}:${project.version}</name>
<tags>
<tag>${project.groupId}/${project.artifactId}:${project.version}</tag>
<tag>${project.groupId}/${project.artifactId}:latest</tag>
<tag>${project.groupId}/${project.artifactId}</tag>
<tag>${image.registry}/${image.name}:latest</tag>
</tags>
<env>
<JAVA_TOOL_OPTIONS>
-XX:+AlwaysPreTouch -XX:+UseParallelGC -XX:+UseNUMA
</JAVA_TOOL_OPTIONS>
<BP_JVM_JLINK_ENABLED>true</BP_JVM_JLINK_ENABLED>
<BPL_JVM_CDS_ENABLED>true</BPL_JVM_CDS_ENABLED>
<BP_SPRING_AOT_ENABLED>true</BP_SPRING_AOT_ENABLED>
<BP_JVM_VERSION>${java.version}</BP_JVM_VERSION>
</env>
</image>
</configuration>
<executions>
<execution>
<id>image</id>
<goals>
<goal>build-image</goal>
</goals>
<phase>verify</phase>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
Expand Down Expand Up @@ -207,6 +220,9 @@
<configOptions>
<useJakartaEe>true</useJakartaEe>
<openApiNullable>${openApiNullable}</openApiNullable>
<groupId>${project.groupId}</groupId>
<artifactId>${project.artifactId}-client</artifactId>
<artifactVersion>${project.version}</artifactVersion>
</configOptions>
</configuration>
</execution>
Expand All @@ -231,5 +247,83 @@
</dependency>
</dependencies>
</profile>

<profile>
<id>release</id>

<build>
<defaultGoal>${release.goal}</defaultGoal>

<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<docker>
<publishRegistry>
<username>${env.GITHUB_ACTOR}</username>
<password>${env.GITHUB_TOKEN}</password>
</publishRegistry>
</docker>
<publish>true</publish>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.3.0</version>
<configuration>
<executable>git</executable>
</configuration>

<executions>
<execution>
<id>config</id>
<phase>verify</phase>
<goals>
<goal>exec</goal>
</goals>
<configuration>
<arguments>
<argument>config</argument>
<argument>--global</argument>
<argument>user.name</argument>
<argument>${env.GITHUB_ACTOR}</argument>
</arguments>
</configuration>
</execution>
<execution>
<id>tag</id>
<phase>verify</phase>
<goals>
<goal>exec</goal>
</goals>
<configuration>
<arguments>
<argument>tag</argument>
<argument>-m</argument>
<argument>Release ${project.version}</argument>
<argument>${project.version}</argument>
</arguments>
</configuration>
</execution>
<execution>
<id>push</id>
<phase>verify</phase>
<goals>
<goal>exec</goal>
</goals>
<configuration>
<arguments>
<argument>push</argument>
<argument>--tags</argument>
</arguments>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>
41 changes: 27 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -16,42 +16,46 @@
>
> 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.)
* Flyway (chosen over Liquibase for its simplicity)
* 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.
Expand All @@ -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).
Expand All @@ -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:
Expand All @@ -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
Expand Down
2 changes: 1 addition & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
9 changes: 9 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,17 @@
<client.package>${openapi.package}.client</client.package>
<spring.server>undertow</spring.server>
<openapi.integration>ui</openapi.integration>
<image.registry>ghcr.io/jaguililla/spring_template</image.registry>
<release.goal>deploy</release.goal>
</properties>

<distributionManagement>
<repository>
<id>github</id>
<url>https://maven.pkg.github.com/jaguililla/spring_template</url>
</repository>
</distributionManagement>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
Expand Down
Loading