Add persistence layer with Liquibase migration

JPA entity, repository, persistence adapter for events. Liquibase
changelog creates the events table with BIGSERIAL ID and UUID tokens.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-05 10:56:39 +01:00
parent 830ca55f20
commit c80074093c
6 changed files with 348 additions and 0 deletions

View File

@@ -0,0 +1,135 @@
package de.fete.adapter.out.persistence;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import java.time.LocalDate;
import java.time.OffsetDateTime;
import java.util.UUID;
/** JPA entity mapping to the events table. */
@Entity
@Table(name = "events")
public class EventJpaEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "event_token", nullable = false, unique = true)
private UUID eventToken;
@Column(name = "organizer_token", nullable = false, unique = true)
private UUID organizerToken;
@Column(nullable = false, length = 200)
private String title;
@Column(length = 2000)
private String description;
@Column(name = "date_time", nullable = false)
private OffsetDateTime dateTime;
@Column(length = 500)
private String location;
@Column(name = "expiry_date", nullable = false)
private LocalDate expiryDate;
@Column(name = "created_at", nullable = false)
private OffsetDateTime createdAt;
/** Returns the internal database ID. */
public Long getId() {
return id;
}
/** Sets the internal database ID. */
public void setId(Long id) {
this.id = id;
}
/** Returns the public event token. */
public UUID getEventToken() {
return eventToken;
}
/** Sets the public event token. */
public void setEventToken(UUID eventToken) {
this.eventToken = eventToken;
}
/** Returns the secret organizer token. */
public UUID getOrganizerToken() {
return organizerToken;
}
/** Sets the secret organizer token. */
public void setOrganizerToken(UUID organizerToken) {
this.organizerToken = organizerToken;
}
/** Returns the event title. */
public String getTitle() {
return title;
}
/** Sets the event title. */
public void setTitle(String title) {
this.title = title;
}
/** Returns the event description. */
public String getDescription() {
return description;
}
/** Sets the event description. */
public void setDescription(String description) {
this.description = description;
}
/** Returns the event date and time. */
public OffsetDateTime getDateTime() {
return dateTime;
}
/** Sets the event date and time. */
public void setDateTime(OffsetDateTime dateTime) {
this.dateTime = dateTime;
}
/** Returns the event location. */
public String getLocation() {
return location;
}
/** Sets the event location. */
public void setLocation(String location) {
this.location = location;
}
/** Returns the expiry date. */
public LocalDate getExpiryDate() {
return expiryDate;
}
/** Sets the expiry date. */
public void setExpiryDate(LocalDate expiryDate) {
this.expiryDate = expiryDate;
}
/** Returns the creation timestamp. */
public OffsetDateTime getCreatedAt() {
return createdAt;
}
/** Sets the creation timestamp. */
public void setCreatedAt(OffsetDateTime createdAt) {
this.createdAt = createdAt;
}
}

View File

@@ -0,0 +1,12 @@
package de.fete.adapter.out.persistence;
import java.util.Optional;
import java.util.UUID;
import org.springframework.data.jpa.repository.JpaRepository;
/** Spring Data JPA repository for event entities. */
public interface EventJpaRepository extends JpaRepository<EventJpaEntity, Long> {
/** Finds an event by its public event token. */
Optional<EventJpaEntity> findByEventToken(UUID eventToken);
}

View File

@@ -0,0 +1,59 @@
package de.fete.adapter.out.persistence;
import de.fete.domain.model.Event;
import de.fete.domain.port.out.EventRepository;
import java.util.Optional;
import java.util.UUID;
import org.springframework.stereotype.Repository;
/** Persistence adapter implementing the EventRepository outbound port. */
@Repository
public class EventPersistenceAdapter implements EventRepository {
private final EventJpaRepository jpaRepository;
/** Creates a new adapter with the given JPA repository. */
public EventPersistenceAdapter(EventJpaRepository jpaRepository) {
this.jpaRepository = jpaRepository;
}
@Override
public Event save(Event event) {
EventJpaEntity entity = toEntity(event);
EventJpaEntity saved = jpaRepository.save(entity);
return toDomain(saved);
}
@Override
public Optional<Event> findByEventToken(UUID eventToken) {
return jpaRepository.findByEventToken(eventToken).map(this::toDomain);
}
private EventJpaEntity toEntity(Event event) {
var entity = new EventJpaEntity();
entity.setId(event.getId());
entity.setEventToken(event.getEventToken());
entity.setOrganizerToken(event.getOrganizerToken());
entity.setTitle(event.getTitle());
entity.setDescription(event.getDescription());
entity.setDateTime(event.getDateTime());
entity.setLocation(event.getLocation());
entity.setExpiryDate(event.getExpiryDate());
entity.setCreatedAt(event.getCreatedAt());
return entity;
}
private Event toDomain(EventJpaEntity entity) {
var event = new Event();
event.setId(entity.getId());
event.setEventToken(entity.getEventToken());
event.setOrganizerToken(entity.getOrganizerToken());
event.setTitle(entity.getTitle());
event.setDescription(entity.getDescription());
event.setDateTime(entity.getDateTime());
event.setLocation(entity.getLocation());
event.setExpiryDate(entity.getExpiryDate());
event.setCreatedAt(entity.getCreatedAt());
return event;
}
}

View File

@@ -0,0 +1,44 @@
<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
<changeSet id="001-create-events-table" author="fete">
<createTable tableName="events">
<column name="id" type="bigserial" autoIncrement="true">
<constraints primaryKey="true" nullable="false"/>
</column>
<column name="event_token" type="uuid">
<constraints nullable="false" unique="true"/>
</column>
<column name="organizer_token" type="uuid">
<constraints nullable="false" unique="true"/>
</column>
<column name="title" type="varchar(200)">
<constraints nullable="false"/>
</column>
<column name="description" type="varchar(2000)"/>
<column name="date_time" type="timestamptz">
<constraints nullable="false"/>
</column>
<column name="location" type="varchar(500)"/>
<column name="expiry_date" type="date">
<constraints nullable="false"/>
</column>
<column name="created_at" type="timestamptz" defaultValueComputed="now()">
<constraints nullable="false"/>
</column>
</createTable>
<createIndex tableName="events" indexName="idx_events_event_token">
<column name="event_token"/>
</createIndex>
<createIndex tableName="events" indexName="idx_events_expiry_date">
<column name="expiry_date"/>
</createIndex>
</changeSet>
</databaseChangeLog>

View File

@@ -6,5 +6,6 @@
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
<include file="db/changelog/000-baseline.xml"/>
<include file="db/changelog/001-create-events-table.xml"/>
</databaseChangeLog>