diff --git a/pom.xml b/pom.xml
index 27c68ac..bf6e3dc 100644
--- a/pom.xml
+++ b/pom.xml
@@ -25,6 +25,14 @@
org.springframework.boot
spring-boot-starter-web
+
+ org.springframework.boot
+ spring-boot-starter-data-jpa
+
+
+ com.h2database
+ h2
+
org.projectlombok
lombok
@@ -37,7 +45,7 @@
org.springdoc
- springdoc-openapi-starter-common
+ springdoc-openapi-starter-webmvc-ui
2.8.13
diff --git a/src/main/java/com/spijkerman/ivo/threekidfamily/ThreeKidFamilyApplication.java b/src/main/java/com/spijkerman/ivo/threekidfamily/ThreeKidFamilyApplication.java
index ea1af2f..85c3a5c 100644
--- a/src/main/java/com/spijkerman/ivo/threekidfamily/ThreeKidFamilyApplication.java
+++ b/src/main/java/com/spijkerman/ivo/threekidfamily/ThreeKidFamilyApplication.java
@@ -2,7 +2,9 @@ package com.spijkerman.ivo.threekidfamily;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
+@EnableJpaRepositories
@SpringBootApplication
public class ThreeKidFamilyApplication {
diff --git a/src/main/java/com/spijkerman/ivo/threekidfamily/domain/person/Person.java b/src/main/java/com/spijkerman/ivo/threekidfamily/domain/person/Person.java
new file mode 100644
index 0000000..5541530
--- /dev/null
+++ b/src/main/java/com/spijkerman/ivo/threekidfamily/domain/person/Person.java
@@ -0,0 +1,4 @@
+package com.spijkerman.ivo.threekidfamily.domain.person;
+
+public record Person() {
+}
diff --git a/src/main/java/com/spijkerman/ivo/threekidfamily/domain/person/PersonController.java b/src/main/java/com/spijkerman/ivo/threekidfamily/domain/person/PersonController.java
new file mode 100644
index 0000000..2a5956d
--- /dev/null
+++ b/src/main/java/com/spijkerman/ivo/threekidfamily/domain/person/PersonController.java
@@ -0,0 +1,38 @@
+package com.spijkerman.ivo.threekidfamily.domain.person;
+
+import com.spijkerman.ivo.threekidfamily.domain.person.dto.PersonUpsertRequest;
+import com.spijkerman.ivo.threekidfamily.domain.person.dto.PersonUpsertResponse;
+import lombok.RequiredArgsConstructor;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+@RestController
+@RequestMapping("/api/v1/people")
+@RequiredArgsConstructor
+public class PersonController {
+
+ private final PersonService personService;
+
+ @PostMapping
+ public ResponseEntity> upsertPerson(
+ @RequestBody PersonUpsertRequest request
+ ) {
+ throw new UnsupportedOperationException();
+ }
+
+ @GetMapping("/{id}")
+ public ResponseEntity> getPerson(
+ @PathVariable int id
+ ) {
+ throw new UnsupportedOperationException();
+ }
+
+ @DeleteMapping
+ public ResponseEntity> deletePeople(
+ @RequestBody List ids
+ ) {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/src/main/java/com/spijkerman/ivo/threekidfamily/domain/person/PersonEntity.java b/src/main/java/com/spijkerman/ivo/threekidfamily/domain/person/PersonEntity.java
new file mode 100644
index 0000000..d25e826
--- /dev/null
+++ b/src/main/java/com/spijkerman/ivo/threekidfamily/domain/person/PersonEntity.java
@@ -0,0 +1,45 @@
+package com.spijkerman.ivo.threekidfamily.domain.person;
+
+import jakarta.annotation.Nullable;
+import jakarta.persistence.ElementCollection;
+import jakarta.persistence.Entity;
+import jakarta.persistence.FetchType;
+import jakarta.persistence.Id;
+import lombok.*;
+
+import java.time.LocalDate;
+import java.util.HashSet;
+import java.util.Set;
+
+@Data
+@Entity
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class PersonEntity {
+
+ @Id
+ @NonNull
+ private Integer id;
+
+ @Nullable
+ private String name;
+
+ @Nullable
+ private LocalDate birthDate;
+
+ // Store only relation IDs instead of JPA mappings to reduce JPA/Hibernate tomfoolery
+ @Nullable
+ private Integer partnerId;
+
+ @NonNull
+ @Singular
+ @ElementCollection(fetch = FetchType.EAGER)
+ private Set childIds = new HashSet<>();
+
+ @NonNull
+ @Singular
+ @ElementCollection(fetch = FetchType.EAGER)
+ private Set parentIds = new HashSet<>();
+
+}
diff --git a/src/main/java/com/spijkerman/ivo/threekidfamily/domain/person/PersonRepository.java b/src/main/java/com/spijkerman/ivo/threekidfamily/domain/person/PersonRepository.java
new file mode 100644
index 0000000..c6ea303
--- /dev/null
+++ b/src/main/java/com/spijkerman/ivo/threekidfamily/domain/person/PersonRepository.java
@@ -0,0 +1,8 @@
+package com.spijkerman.ivo.threekidfamily.domain.person;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public interface PersonRepository extends JpaRepository {
+}
diff --git a/src/main/java/com/spijkerman/ivo/threekidfamily/domain/person/PersonService.java b/src/main/java/com/spijkerman/ivo/threekidfamily/domain/person/PersonService.java
new file mode 100644
index 0000000..6106fe7
--- /dev/null
+++ b/src/main/java/com/spijkerman/ivo/threekidfamily/domain/person/PersonService.java
@@ -0,0 +1,44 @@
+package com.spijkerman.ivo.threekidfamily.domain.person;
+
+import lombok.Locked;
+import lombok.NonNull;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+
+import java.util.Set;
+
+@Service
+@RequiredArgsConstructor
+public class PersonService {
+
+ private final PersonRepository personRepository;
+
+ /**
+ * Upserts a person to storage, overwriting any existing or conflicting data.
+ *
+ * @param person The Person data to be persisted
+ * @return The IDs of the Persons that have been modified because of this operation.
+ */
+ public @NonNull Set upsertPerson(Person person) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Returns all known data related to that specific user.
+ *
+ * @param id The ID of the person to be retrieved.
+ * @return A Person object, may contain all information, or just the ID.
+ */
+ public Person getPersonById(int id) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Deletes all data related to the specified user, and blacklists that ID from later use.
+ * @param id The ID of the person to be deleted and blacklisted.
+ */
+ public void deletePersonById(int id) {
+ throw new UnsupportedOperationException();
+ }
+
+}
diff --git a/src/main/java/com/spijkerman/ivo/threekidfamily/domain/person/dto/PersonUpsertRequest.java b/src/main/java/com/spijkerman/ivo/threekidfamily/domain/person/dto/PersonUpsertRequest.java
new file mode 100644
index 0000000..57dc347
--- /dev/null
+++ b/src/main/java/com/spijkerman/ivo/threekidfamily/domain/person/dto/PersonUpsertRequest.java
@@ -0,0 +1,4 @@
+package com.spijkerman.ivo.threekidfamily.domain.person.dto;
+
+public record PersonUpsertRequest() {
+}
diff --git a/src/main/java/com/spijkerman/ivo/threekidfamily/domain/person/dto/PersonUpsertResponse.java b/src/main/java/com/spijkerman/ivo/threekidfamily/domain/person/dto/PersonUpsertResponse.java
new file mode 100644
index 0000000..b2c1571
--- /dev/null
+++ b/src/main/java/com/spijkerman/ivo/threekidfamily/domain/person/dto/PersonUpsertResponse.java
@@ -0,0 +1,4 @@
+package com.spijkerman.ivo.threekidfamily.domain.person.dto;
+
+public record PersonUpsertResponse() {
+}
diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml
index 2e720a9..6abf4cc 100644
--- a/src/main/resources/application.yaml
+++ b/src/main/resources/application.yaml
@@ -1 +1,5 @@
-spring.application.name: threekidfamily
+spring:
+ datasource:
+ url: jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
+ username: sa
+ password: password
\ No newline at end of file
diff --git a/src/test/java/com/spijkerman/ivo/threekidfamily/ThreeKidFamilyApplicationTests.java b/src/test/java/com/spijkerman/ivo/threekidfamily/ThreeKidFamilyApplicationTests.java
deleted file mode 100644
index e98569b..0000000
--- a/src/test/java/com/spijkerman/ivo/threekidfamily/ThreeKidFamilyApplicationTests.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package com.spijkerman.ivo.threekidfamily;
-
-import org.junit.jupiter.api.Test;
-import org.springframework.boot.test.context.SpringBootTest;
-
-@SpringBootTest
-class ThreeKidFamilyApplicationTests {
-
- @Test
- void contextLoads() {
- }
-
-}
diff --git a/src/test/java/com/spijkerman/ivo/threekidfamily/ThreeKidFamilyIT.java b/src/test/java/com/spijkerman/ivo/threekidfamily/ThreeKidFamilyIT.java
new file mode 100644
index 0000000..4e0c8a8
--- /dev/null
+++ b/src/test/java/com/spijkerman/ivo/threekidfamily/ThreeKidFamilyIT.java
@@ -0,0 +1,53 @@
+package com.spijkerman.ivo.threekidfamily;
+
+import com.spijkerman.ivo.threekidfamily.domain.person.Person;
+import com.spijkerman.ivo.threekidfamily.domain.person.PersonEntity;
+import com.spijkerman.ivo.threekidfamily.domain.person.PersonRepository;
+import com.spijkerman.ivo.threekidfamily.domain.person.PersonService;
+import lombok.val;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.web.client.TestRestTemplate;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.HttpStatus;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
+class ThreeKidFamilyIT {
+
+ @Autowired
+ private TestRestTemplate restTemplate;
+
+ @Autowired
+ private PersonRepository personRepository;
+
+ @Test
+ void contextLoads() {
+ }
+
+ @Test
+ void gibberishNotFound() {
+ val response = restTemplate.exchange("/gibberish", HttpMethod.GET, null, String.class);
+ assertThat(response.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
+ }
+
+ @Test
+ void swaggerUiLoads() {
+ val response = restTemplate.exchange("/swagger-ui.html", HttpMethod.GET, null, String.class);
+ assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
+ }
+
+ @Test
+ void testDbRoundTrip() {
+ val given = PersonEntity.builder().id(12).build();
+ val saved = personRepository.save(given);
+
+ assertThat(saved).isEqualTo(given);
+
+ val retrieved = personRepository.findById(12);
+ assertThat(retrieved).isPresent().hasValue(given);
+ }
+
+}
diff --git a/src/test/resources/application.yaml b/src/test/resources/application.yaml
new file mode 100644
index 0000000..5e86f41
--- /dev/null
+++ b/src/test/resources/application.yaml
@@ -0,0 +1,3 @@
+spring:
+ jpa:
+ show-sql: true
\ No newline at end of file