Skip to content
This repository has been archived by the owner on Jul 26, 2024. It is now read-only.

[Vorbereitung] Add persistence layer #1

Merged
merged 3 commits into from
Jun 25, 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
46 changes: 46 additions & 0 deletions account-service-provider/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
<name>Account Service Provider</name>
<properties>
<java.version>17</java.version>
<mapstruct.version>1.5.5.Final</mapstruct.version>
<lombok-mapstruct-binding.version>0.2.0</lombok-mapstruct-binding.version>
</properties>
<dependencies>
<dependency>
Expand All @@ -39,6 +41,20 @@
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>${mapstruct.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
Expand All @@ -47,6 +63,32 @@
</dependencies>

<build>
<pluginManagement>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</path>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${mapstruct.version}</version>
</path>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok-mapstruct-binding</artifactId>
<version>${lombok-mapstruct-binding.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
Expand All @@ -57,6 +99,10 @@
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
<exclude>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,40 +2,24 @@

import de.sample.schulung.accounts.domain.Customer;
import jakarta.validation.ValidationException;
import org.springframework.stereotype.Component;
import org.mapstruct.Mapper;

@Component
public class CustomerDtoMapper {
@Mapper(componentModel = "spring")
public interface CustomerDtoMapper {

// TODO Mapper-Generierung mit MapStruct
CustomerDto map(Customer source);

public CustomerDto map(Customer source) {
var result = new CustomerDto();
result.setUuid(source.getUuid());
result.setName(source.getName());
result.setDateOfBirth(source.getDateOfBirth());
result.setState(this.mapState(source.getState()));
return result;
}

public Customer map(CustomerDto source) {
var result = new Customer();
result.setUuid(source.getUuid());
result.setName(source.getName());
result.setDateOfBirth(source.getDateOfBirth());
result.setState(this.mapState(source.getState()));
return result;
}
Customer map(CustomerDto source);

public String mapState(Customer.CustomerState source) {
default String mapState(Customer.CustomerState source) {
return switch (source) {
case ACTIVE -> "active";
case LOCKED -> "locked";
case DISABLED -> "disabled";
};
}

public Customer.CustomerState mapState(String source) {
default Customer.CustomerState mapState(String source) {
return switch (source) {
case "active" -> Customer.CustomerState.ACTIVE;
case "locked" -> Customer.CustomerState.LOCKED;
Expand All @@ -44,5 +28,4 @@ public Customer.CustomerState mapState(String source) {
};
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@
@Slf4j
public class CustomersInitializer {

// TODO Enable by Auto-configuration
// TODO make Customer Sample Provider injectable
// TODO create one that allows configuration by application.yml

/*
* let's use a separate class for the configuration
* - default constructor
Expand All @@ -40,7 +44,7 @@ public static class CustomerInitialerConfiguration {

@EventListener(ContextRefreshedEvent.class)
public void init() {
if(this.config.enabled) {
if(this.config.enabled && this.service.count() < 1) {
log.info("Initializing customers");
service.createCustomer(
new Customer(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,70 +1,61 @@
package de.sample.schulung.accounts.domain;

import de.sample.schulung.accounts.domain.Customer.CustomerState;
import de.sample.schulung.accounts.domain.events.CustomerCreatedEvent;
import de.sample.schulung.accounts.domain.events.CustomerDeletedEvent;
import de.sample.schulung.accounts.domain.events.CustomerReplacedEvent;
import de.sample.schulung.accounts.domain.sink.CustomersSink;
import de.sample.schulung.accounts.shared.interceptors.PublishEvent;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;

import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import java.util.stream.Stream;

@Validated
@Service
@RequiredArgsConstructor
public class CustomersService {

private final Map<UUID, Customer> customers = new HashMap<>();
private final CustomersSink sink;

public Stream<Customer> getCustomers() {
return customers
.values()
.stream();
return sink.getCustomers();
}

public Stream<Customer> getCustomersByState(@NotNull Customer.CustomerState state) { // TODO enum?
return this.getCustomers()
.filter(customer -> state.equals(customer.getState()));
public Stream<Customer> getCustomersByState(@NotNull CustomerState state) {
return sink.getCustomersByState(state);
}

@PublishEvent(CustomerCreatedEvent.class)
public void createCustomer(@Valid Customer customer) {
var uuid = UUID.randomUUID();
customer.setUuid(uuid);
this.customers.put(customer.getUuid(), customer);
sink.createCustomer(customer);
}

public Optional<Customer> findCustomerById(@NotNull UUID uuid) {
return Optional.ofNullable(
this.customers.get(uuid)
);
return sink.findCustomerById(uuid);
}

@PublishEvent(CustomerReplacedEvent.class)
public void replaceCustomer(@Valid Customer customer) {
if (this.exists(customer.getUuid())) {
this.customers.put(customer.getUuid(), customer);
} else {
throw new NotFoundException();
}
sink.replaceCustomer(customer);
}

@PublishEvent(CustomerDeletedEvent.class)
public void deleteCustomer(@NotNull UUID uuid) {
if (this.exists(uuid)) {
this.customers.remove(uuid);
} else {
throw new NotFoundException();
}
sink.deleteCustomer(uuid);
}

public boolean exists(UUID uuid) {
return this.customers.containsKey(uuid);
return sink.exists(uuid);
}

public long count() {
return sink.count();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package de.sample.schulung.accounts.domain.sink;

import de.sample.schulung.accounts.domain.Customer;
import de.sample.schulung.accounts.domain.Customer.CustomerState;

import java.util.Optional;
import java.util.UUID;
import java.util.stream.Stream;

public interface CustomersSink {

Stream<Customer> getCustomers();
Stream<Customer> getCustomersByState(CustomerState state);
void createCustomer(Customer customer);
Optional<Customer> findCustomerById(UUID uuid);
void replaceCustomer(Customer customer);
void deleteCustomer(UUID uuid);

default boolean exists(UUID uuid) {
return this.getCustomers()
.anyMatch(c -> uuid.equals(c.getUuid()));
}

default long count() {
return this.getCustomers()
.count();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package de.sample.schulung.accounts.domain.sink;

import de.sample.schulung.accounts.domain.Customer;
import de.sample.schulung.accounts.domain.NotFoundException;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;

import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import java.util.stream.Stream;

// TODO: Inject, if there isn't any other implementation available
// @Component
public class CustomersSinkInMemoryImpl implements CustomersSink {

private final Map<UUID, Customer> customers = new HashMap<>();

public Stream<Customer> getCustomers() {
return customers
.values()
.stream();
}

public Stream<Customer> getCustomersByState(@NotNull Customer.CustomerState state) {
return this.getCustomers()
.filter(customer -> state.equals(customer.getState()));
}

public void createCustomer(@Valid Customer customer) {
var uuid = UUID.randomUUID();
customer.setUuid(uuid);
this.customers.put(customer.getUuid(), customer);
}

public Optional<Customer> findCustomerById(@NotNull UUID uuid) {
return Optional.ofNullable(
this.customers.get(uuid)
);
}

public void replaceCustomer(@Valid Customer customer) {
if (this.exists(customer.getUuid())) {
this.customers.put(customer.getUuid(), customer);
} else {
throw new NotFoundException();
}
}

public void deleteCustomer(@NotNull UUID uuid) {
if (this.exists(uuid)) {
this.customers.remove(uuid);
} else {
throw new NotFoundException();
}
}

public boolean exists(UUID uuid) {
return this.customers.containsKey(uuid);
}

@Override
public long count() {
return this.customers.size();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package de.sample.schulung.accounts.persistence;

import de.sample.schulung.accounts.domain.Customer.CustomerState;
import jakarta.persistence.*;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import lombok.Getter;
import lombok.Setter;

import java.time.LocalDate;
import java.util.UUID;

@Getter
@Setter
@Entity(name = "Customer")
@Table(name = "CUSTOMERS")
public class CustomerEntity {

@Id
@GeneratedValue(strategy = GenerationType.UUID)
private UUID uuid;
@Size(min = 3, max = 100)
@NotNull
private String name;
@Column(name = "DATE_OF_BIRTH")
private LocalDate dateOfBirth;
@NotNull
private CustomerState state = CustomerState.ACTIVE;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package de.sample.schulung.accounts.persistence;

import de.sample.schulung.accounts.domain.Customer;
import org.mapstruct.Mapper;
import org.mapstruct.MappingTarget;

@Mapper(componentModel = "spring")
public interface CustomerEntityMapper {

CustomerEntity map(Customer source);

Customer map(CustomerEntity source);

void copy(CustomerEntity source, @MappingTarget Customer target);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package de.sample.schulung.accounts.persistence;

import de.sample.schulung.accounts.domain.Customer.CustomerState;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import java.util.List;
import java.util.UUID;

@Repository
public interface CustomerEntityRepository extends JpaRepository<CustomerEntity, UUID> {

List<CustomerEntity> findByState(CustomerState state);

}
Loading
Loading