add new package
This commit is contained in:
parent
6b33c0d65d
commit
99259d231b
|
@ -0,0 +1 @@
|
||||||
|
/product-service/
|
|
@ -0,0 +1,4 @@
|
||||||
|
## Patterns Modules
|
||||||
|
|
||||||
|
This module contains articles about design patterns.
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
### Relevant Articles:
|
||||||
|
|
||||||
|
- [Clean Architecture with Spring Boot](https://www.baeldung.com/spring-boot-clean-architecture)
|
||||||
|
- [Anemic vs. Rich Domain Objects](https://www.baeldung.com/java-anemic-vs-rich-domain-objects)
|
|
@ -0,0 +1,63 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<artifactId>clean-architecture</artifactId>
|
||||||
|
<version>1.0</version>
|
||||||
|
<name>clean-architecture</name>
|
||||||
|
<description>Project for clean architecture in java</description>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>com.baeldung</groupId>
|
||||||
|
<artifactId>parent-boot-2</artifactId>
|
||||||
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
|
<relativePath>../../parent-boot-2</relativePath>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.h2database</groupId>
|
||||||
|
<artifactId>h2</artifactId>
|
||||||
|
<scope>runtime</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.junit.vintage</groupId>
|
||||||
|
<artifactId>junit-vintage-engine</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.junit.platform</groupId>
|
||||||
|
<artifactId>junit-platform-engine</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.junit.platform</groupId>
|
||||||
|
<artifactId>junit-platform-runner</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
</project>
|
|
@ -0,0 +1,40 @@
|
||||||
|
package com.baeldung.pattern.cleanarchitecture;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
|
||||||
|
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext;
|
||||||
|
import org.springframework.context.ApplicationContext;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
|
||||||
|
import org.springframework.core.type.classreading.MetadataReader;
|
||||||
|
import org.springframework.core.type.classreading.MetadataReaderFactory;
|
||||||
|
import org.springframework.core.type.filter.TypeFilter;
|
||||||
|
|
||||||
|
@SpringBootApplication
|
||||||
|
public class CleanArchitectureApplication {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
SpringApplication.run(CleanArchitectureApplication.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
BeanFactoryPostProcessor beanFactoryPostProcessor(ApplicationContext beanRegistry) {
|
||||||
|
return beanFactory -> {
|
||||||
|
genericApplicationContext((BeanDefinitionRegistry) ((AnnotationConfigServletWebServerApplicationContext) beanRegistry).getBeanFactory());
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void genericApplicationContext(BeanDefinitionRegistry beanRegistry) {
|
||||||
|
ClassPathBeanDefinitionScanner beanDefinitionScanner = new ClassPathBeanDefinitionScanner(beanRegistry);
|
||||||
|
beanDefinitionScanner.addIncludeFilter(removeModelAndEntitiesFilter());
|
||||||
|
beanDefinitionScanner.scan("com.baeldung.pattern.cleanarchitecture");
|
||||||
|
}
|
||||||
|
|
||||||
|
static TypeFilter removeModelAndEntitiesFilter() {
|
||||||
|
return (MetadataReader mr, MetadataReaderFactory mrf) -> !mr.getClassMetadata()
|
||||||
|
.getClassName()
|
||||||
|
.endsWith("Model");
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
package com.baeldung.pattern.cleanarchitecture.usercreation;
|
||||||
|
|
||||||
|
class CommonUser implements User {
|
||||||
|
|
||||||
|
String name;
|
||||||
|
String password;
|
||||||
|
|
||||||
|
CommonUser(String name, String password) {
|
||||||
|
this.name = name;
|
||||||
|
this.password = password;
|
||||||
|
}
|
||||||
|
|
||||||
|
CommonUser() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean passwordIsValid() {
|
||||||
|
return password != null && password.length() > 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getPassword() {
|
||||||
|
return password;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
package com.baeldung.pattern.cleanarchitecture.usercreation;
|
||||||
|
|
||||||
|
class CommonUserFactory implements UserFactory {
|
||||||
|
@Override
|
||||||
|
public User create(String name, String password) {
|
||||||
|
return new CommonUser(name, password);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
package com.baeldung.pattern.cleanarchitecture.usercreation;
|
||||||
|
|
||||||
|
class JpaUser implements UserRegisterDsGateway {
|
||||||
|
|
||||||
|
final JpaUserRepository repository;
|
||||||
|
|
||||||
|
JpaUser(JpaUserRepository repository) {
|
||||||
|
this.repository = repository;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean existsByName(String name) {
|
||||||
|
return repository.existsById(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void save(UserDsRequestModel requestModel) {
|
||||||
|
UserDataMapper accountDataMapper = new UserDataMapper(requestModel.getName(), requestModel.getPassword(), requestModel.getCreationTime());
|
||||||
|
repository.save(accountDataMapper);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
package com.baeldung.pattern.cleanarchitecture.usercreation;
|
||||||
|
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
interface JpaUserRepository extends JpaRepository<UserDataMapper, String> {
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
package com.baeldung.pattern.cleanarchitecture.usercreation;
|
||||||
|
|
||||||
|
interface User {
|
||||||
|
boolean passwordIsValid();
|
||||||
|
|
||||||
|
String getName();
|
||||||
|
|
||||||
|
String getPassword();
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
package com.baeldung.pattern.cleanarchitecture.usercreation;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.Table;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "user")
|
||||||
|
class UserDataMapper {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
String name;
|
||||||
|
|
||||||
|
String password;
|
||||||
|
|
||||||
|
LocalDateTime creationTime;
|
||||||
|
|
||||||
|
public UserDataMapper() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserDataMapper(String name, String password, LocalDateTime creationTime) {
|
||||||
|
super();
|
||||||
|
this.name = name;
|
||||||
|
this.password = password;
|
||||||
|
this.creationTime = creationTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPassword() {
|
||||||
|
return password;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPassword(String password) {
|
||||||
|
this.password = password;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LocalDateTime getCreationTime() {
|
||||||
|
return creationTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCreationTime(LocalDateTime creationTime) {
|
||||||
|
this.creationTime = creationTime;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
package com.baeldung.pattern.cleanarchitecture.usercreation;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
class UserDsRequestModel {
|
||||||
|
|
||||||
|
String name;
|
||||||
|
String password;
|
||||||
|
LocalDateTime creationTime;
|
||||||
|
|
||||||
|
public UserDsRequestModel(String name, String password, LocalDateTime creationTime) {
|
||||||
|
this.name = name;
|
||||||
|
this.password = password;
|
||||||
|
this.creationTime = creationTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPassword() {
|
||||||
|
return password;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPassword(String password) {
|
||||||
|
this.password = password;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LocalDateTime getCreationTime() {
|
||||||
|
return creationTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCreationTime(LocalDateTime creationTime) {
|
||||||
|
this.creationTime = creationTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
package com.baeldung.pattern.cleanarchitecture.usercreation;
|
||||||
|
|
||||||
|
interface UserFactory {
|
||||||
|
User create(String name, String password);
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
package com.baeldung.pattern.cleanarchitecture.usercreation;
|
||||||
|
|
||||||
|
public interface UserInputBoundary {
|
||||||
|
UserResponseModel create(UserRequestModel requestModel);
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
package com.baeldung.pattern.cleanarchitecture.usercreation;
|
||||||
|
|
||||||
|
interface UserPresenter {
|
||||||
|
UserResponseModel prepareSuccessView(UserResponseModel user);
|
||||||
|
|
||||||
|
UserResponseModel prepareFailView(String error);
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
package com.baeldung.pattern.cleanarchitecture.usercreation;
|
||||||
|
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
class UserRegisterController {
|
||||||
|
|
||||||
|
final UserInputBoundary userInput;
|
||||||
|
|
||||||
|
UserRegisterController(UserInputBoundary accountGateway) {
|
||||||
|
this.userInput = accountGateway;
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/user")
|
||||||
|
UserResponseModel create(@RequestBody UserRequestModel requestModel) {
|
||||||
|
return userInput.create(requestModel);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
package com.baeldung.pattern.cleanarchitecture.usercreation;
|
||||||
|
|
||||||
|
interface UserRegisterDsGateway {
|
||||||
|
boolean existsByName(String identifier);
|
||||||
|
|
||||||
|
void save(UserDsRequestModel requestModel);
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
package com.baeldung.pattern.cleanarchitecture.usercreation;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
class UserRegisterInteractor implements UserInputBoundary {
|
||||||
|
|
||||||
|
final UserRegisterDsGateway userDsGateway;
|
||||||
|
final UserPresenter userPresenter;
|
||||||
|
final UserFactory userFactory;
|
||||||
|
|
||||||
|
UserRegisterInteractor(UserRegisterDsGateway userRegisterDfGateway, UserPresenter userPresenter,
|
||||||
|
UserFactory userFactory) {
|
||||||
|
this.userDsGateway = userRegisterDfGateway;
|
||||||
|
this.userPresenter = userPresenter;
|
||||||
|
this.userFactory = userFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UserResponseModel create(UserRequestModel requestModel) {
|
||||||
|
if (userDsGateway.existsByName(requestModel.getName())) {
|
||||||
|
return userPresenter.prepareFailView("User already exists.");
|
||||||
|
}
|
||||||
|
User user = userFactory.create(requestModel.getName(), requestModel.getPassword());
|
||||||
|
if (!user.passwordIsValid()) {
|
||||||
|
return userPresenter.prepareFailView("User password must have more than 5 characters.");
|
||||||
|
}
|
||||||
|
LocalDateTime now = LocalDateTime.now();
|
||||||
|
UserDsRequestModel userDsModel = new UserDsRequestModel(user.getName(), user.getPassword(), now);
|
||||||
|
|
||||||
|
userDsGateway.save(userDsModel);
|
||||||
|
|
||||||
|
UserResponseModel accountResponseModel = new UserResponseModel(user.getName(), now.toString());
|
||||||
|
return userPresenter.prepareSuccessView(accountResponseModel);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
package com.baeldung.pattern.cleanarchitecture.usercreation;
|
||||||
|
|
||||||
|
class UserRequestModel {
|
||||||
|
|
||||||
|
String name;
|
||||||
|
String password;
|
||||||
|
|
||||||
|
public UserRequestModel() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
UserRequestModel(String name, String password) {
|
||||||
|
super();
|
||||||
|
this.name = name;
|
||||||
|
this.password = password;
|
||||||
|
}
|
||||||
|
|
||||||
|
String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
String getPassword() {
|
||||||
|
return password;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setPassword(String password) {
|
||||||
|
this.password = password;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
package com.baeldung.pattern.cleanarchitecture.usercreation;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.web.server.ResponseStatusException;
|
||||||
|
|
||||||
|
class UserResponseFormatter implements UserPresenter {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UserResponseModel prepareSuccessView(UserResponseModel response) {
|
||||||
|
LocalDateTime responseTime = LocalDateTime.parse(response.getCreationTime());
|
||||||
|
response.setCreationTime(responseTime.format(DateTimeFormatter.ofPattern("hh:mm:ss")));
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UserResponseModel prepareFailView(String error) {
|
||||||
|
throw new ResponseStatusException(HttpStatus.CONFLICT, error);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
package com.baeldung.pattern.cleanarchitecture.usercreation;
|
||||||
|
|
||||||
|
public class UserResponseModel {
|
||||||
|
|
||||||
|
String login;
|
||||||
|
String creationTime;
|
||||||
|
|
||||||
|
public UserResponseModel(String login, String creationTime) {
|
||||||
|
this.login = login;
|
||||||
|
this.creationTime = creationTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLogin() {
|
||||||
|
return login;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLogin(String login) {
|
||||||
|
this.login = login;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCreationTime() {
|
||||||
|
return creationTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCreationTime(String creationTime) {
|
||||||
|
this.creationTime = creationTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
package com.baeldung.pattern.richdomainmodel;
|
||||||
|
|
||||||
|
public class Player {
|
||||||
|
private int points ;
|
||||||
|
final String name;
|
||||||
|
|
||||||
|
public Player(String name) {
|
||||||
|
this(name, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Player(String name, int points) {
|
||||||
|
this.name = name;
|
||||||
|
this.points = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void gainPoint() {
|
||||||
|
points++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasScoreBiggerThan(Score score) {
|
||||||
|
return this.points > score.points();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int pointsDifference(Player other) {
|
||||||
|
return points - other.points;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String name() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String score() {
|
||||||
|
return Score.from(points).label();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
package com.baeldung.pattern.richdomainmodel;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
public enum Score {
|
||||||
|
LOVE(0, "Love"), FIFTEEN(1, "Fifteen"), THIRTY(2, "Thirty"), FORTY(3, "Forty");
|
||||||
|
|
||||||
|
private final int points;
|
||||||
|
private final String label;
|
||||||
|
|
||||||
|
Score(int points, String label) {
|
||||||
|
this.points = points;
|
||||||
|
this.label = label;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Score from(int value) {
|
||||||
|
return Arrays.stream(values())
|
||||||
|
.filter(v -> v.points == value)
|
||||||
|
.findFirst()
|
||||||
|
.orElseThrow(() -> new IllegalArgumentException("no such element: " + value));
|
||||||
|
}
|
||||||
|
|
||||||
|
public int points() {
|
||||||
|
return points;
|
||||||
|
}
|
||||||
|
public String label() {
|
||||||
|
return label;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,71 @@
|
||||||
|
package com.baeldung.pattern.richdomainmodel;
|
||||||
|
|
||||||
|
public class TennisGame {
|
||||||
|
private final Player server;
|
||||||
|
private final Player receiver;
|
||||||
|
|
||||||
|
public TennisGame(String server, String receiver) {
|
||||||
|
this.server = new Player(server);
|
||||||
|
this.receiver = new Player(receiver);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void wonPoint(String playerName) {
|
||||||
|
if(server.name().equals(playerName)) {
|
||||||
|
server.gainPoint();
|
||||||
|
} else {
|
||||||
|
receiver.gainPoint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getScore() {
|
||||||
|
if (gameContinues()) {
|
||||||
|
return getGameScore();
|
||||||
|
}
|
||||||
|
return "Win for " + leadingPlayer().name();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getGameScore() {
|
||||||
|
if (isScoreEqual()) {
|
||||||
|
return getEqualScore();
|
||||||
|
}
|
||||||
|
if (isAdvantage()) {
|
||||||
|
return "Advantage " + leadingPlayer().name();
|
||||||
|
}
|
||||||
|
return getSimpleScore();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isScoreEqual() {
|
||||||
|
return server.pointsDifference(receiver) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isAdvantage() {
|
||||||
|
return leadingPlayer().hasScoreBiggerThan(Score.FORTY)
|
||||||
|
&& Math.abs(server.pointsDifference(receiver)) == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isGameFinished() {
|
||||||
|
return leadingPlayer().hasScoreBiggerThan(Score.FORTY)
|
||||||
|
&& Math.abs(server.pointsDifference(receiver)) >= 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Player leadingPlayer() {
|
||||||
|
if (server.pointsDifference(receiver) > 0) {
|
||||||
|
return server;
|
||||||
|
}
|
||||||
|
return receiver;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean gameContinues() {
|
||||||
|
return !isGameFinished();
|
||||||
|
}
|
||||||
|
private String getSimpleScore() {
|
||||||
|
return String.format("%s-%s", server.score(), receiver.score());
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getEqualScore() {
|
||||||
|
if (server.hasScoreBiggerThan(Score.THIRTY)) {
|
||||||
|
return "Deuce";
|
||||||
|
}
|
||||||
|
return String.format("%s-All", server.score());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
server.port=8080
|
||||||
|
server.error.include-message=always
|
|
@ -0,0 +1,50 @@
|
||||||
|
package com.baeldung.pattern.cleanarchitecture.usercreation;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyString;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
|
import org.mockito.ArgumentCaptor;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.web.server.ResponseStatusException;
|
||||||
|
|
||||||
|
class UserResponseFormatterUnitTest {
|
||||||
|
|
||||||
|
UserResponseFormatter userResponseFormatter = new UserResponseFormatter();
|
||||||
|
UserRegisterDsGateway userDsGateway = mock(UserRegisterDsGateway.class);
|
||||||
|
UserPresenter userPresenter = mock(UserPresenter.class);
|
||||||
|
UserFactory userFactory = mock(UserFactory.class);
|
||||||
|
UserInputBoundary userInputBoundary = new UserRegisterInteractor(userDsGateway, userPresenter, userFactory);
|
||||||
|
ArgumentCaptor<String> userRequestModelArgumentCaptor = ArgumentCaptor.forClass(String.class);
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void givenDateAnd3HourTime_whenPrepareSuccessView_thenReturnOnly3HourTime() {
|
||||||
|
UserResponseModel modelResponse = new UserResponseModel("baeldung", "2020-12-20T03:00:00.000");
|
||||||
|
UserResponseModel formattedResponse = userResponseFormatter.prepareSuccessView(modelResponse);
|
||||||
|
|
||||||
|
assertThat(formattedResponse.getCreationTime()).isEqualTo("03:00:00");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void whenPrepareFailView_thenThrowHttpConflictException() {
|
||||||
|
assertThatThrownBy(() -> userResponseFormatter.prepareFailView("Invalid password")).isInstanceOf(ResponseStatusException.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void whenCreateUser_thenSuccess() {
|
||||||
|
|
||||||
|
UserRequestModel userRequestModel = new UserRequestModel("baeldung", "123456");
|
||||||
|
when(userFactory.create(anyString(), anyString())).thenReturn(new CommonUser("baeldung", "123456"));
|
||||||
|
|
||||||
|
userInputBoundary.create(userRequestModel);
|
||||||
|
|
||||||
|
verify(userDsGateway).existsByName(userRequestModelArgumentCaptor.capture());
|
||||||
|
String name = userRequestModel.getName();
|
||||||
|
assertEquals("baeldung", name);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
package com.baeldung.pattern.cleanarchitecture.usercreation;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.mockito.Mockito.*;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyString;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
class UserUnitTest {
|
||||||
|
|
||||||
|
UserRegisterDsGateway userDsGateway = mock(UserRegisterDsGateway.class);
|
||||||
|
UserPresenter userPresenter = mock(UserPresenter.class);
|
||||||
|
UserFactory userFactory = mock(UserFactory.class);
|
||||||
|
UserInputBoundary interactor = new UserRegisterInteractor(userDsGateway, userPresenter, userFactory);
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void given123Password_whenPasswordIsNotValid_thenIsFalse() {
|
||||||
|
User user = new CommonUser("Baeldung", "123");
|
||||||
|
|
||||||
|
assertThat(user.passwordIsValid()).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void givenBaeldungUserAnd123456Password_whenCreate_thenSaveItAndPrepareSuccessView() {
|
||||||
|
|
||||||
|
User user = new CommonUser("baeldung", "123456");
|
||||||
|
UserRequestModel userRequestModel = new UserRequestModel(user.getName(), user.getPassword());
|
||||||
|
when(userFactory.create(anyString(), anyString())).thenReturn(new CommonUser(user.getName(), user.getPassword()));
|
||||||
|
|
||||||
|
interactor.create(userRequestModel);
|
||||||
|
|
||||||
|
verify(userDsGateway, times(1)).save(any(UserDsRequestModel.class));
|
||||||
|
verify(userPresenter, times(1)).prepareSuccessView(any(UserResponseModel.class));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
package com.baeldung.pattern.richdomainmodel;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
public class RichDomainModelUnitTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenATennisGame_whenReceiverWinsThreePoints_thenScoreIsFortyLove() {
|
||||||
|
TennisGame game = new TennisGame("server", "receiver");
|
||||||
|
|
||||||
|
game.wonPoint("server");
|
||||||
|
game.wonPoint("server");
|
||||||
|
game.wonPoint("server");
|
||||||
|
|
||||||
|
assertThat(game.getScore())
|
||||||
|
.isEqualTo("Forty-Love");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenATennisGame_whenEachPlayerWonTwoPoints_thenScoreIsThirtyAll() {
|
||||||
|
TennisGame game = new TennisGame("server", "receiver");
|
||||||
|
|
||||||
|
game.wonPoint("server");
|
||||||
|
game.wonPoint("server");
|
||||||
|
game.wonPoint("receiver");
|
||||||
|
game.wonPoint("receiver");
|
||||||
|
|
||||||
|
assertThat(game.getScore())
|
||||||
|
.isEqualTo("Thirty-All");
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
|
||||||
|
- [Coupling in Java](https://www.baeldung.com/java-coupling-classes-tight-loose)
|
|
@ -0,0 +1,14 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<artifactId>coupling</artifactId>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<artifactId>patterns-modules</artifactId>
|
||||||
|
<groupId>com.baeldung</groupId>
|
||||||
|
<version>1.0.0-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
</project>
|
|
@ -0,0 +1,14 @@
|
||||||
|
package com.baeldung.loose;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class CSVExport implements ExportMetadata {
|
||||||
|
@Override
|
||||||
|
public File export(List<Object> metadata) {
|
||||||
|
System.out.println("Exporting data...");
|
||||||
|
// Export Metadata
|
||||||
|
File outputCSV = null;
|
||||||
|
return outputCSV;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
package com.baeldung.loose;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public interface ExportMetadata {
|
||||||
|
File export(List<Object> metadata);
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
package com.baeldung.loose;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public interface FetchMetadata {
|
||||||
|
List<Object> fetchMetadata();
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
package com.baeldung.loose;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class JSONFetch implements FetchMetadata{
|
||||||
|
@Override
|
||||||
|
public List<Object> fetchMetadata() {
|
||||||
|
System.out.println("Fetching some json data");
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
package com.baeldung.loose;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class MetadataCollector {
|
||||||
|
private final FetchMetadata fetchMetadata;
|
||||||
|
private final ExportMetadata exportMetadata;
|
||||||
|
|
||||||
|
public MetadataCollector(FetchMetadata fetchMetadata, ExportMetadata exportMetadata) {
|
||||||
|
this.fetchMetadata = fetchMetadata;
|
||||||
|
this.exportMetadata = exportMetadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void collectMetadata() {
|
||||||
|
List<Object> metadata = fetchMetadata.fetchMetadata();
|
||||||
|
exportMetadata.export(metadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
public FetchMetadata getFetchMetadata() {
|
||||||
|
return fetchMetadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ExportMetadata getExportMetadata() {
|
||||||
|
return exportMetadata;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package com.baeldung.loose;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class PDFExport implements ExportMetadata {
|
||||||
|
@Override
|
||||||
|
public File export(List<Object> metadata) {
|
||||||
|
System.out.println("PDF Export");
|
||||||
|
// Some logic
|
||||||
|
File outputPDF = null;
|
||||||
|
return outputPDF;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
package com.baeldung.loose;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class XMLFetch implements FetchMetadata {
|
||||||
|
@Override
|
||||||
|
public List<Object> fetchMetadata() {
|
||||||
|
List<Object> metadata = new ArrayList<>();
|
||||||
|
// Do some stuff
|
||||||
|
return metadata;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package com.baeldung.tight;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class CSVExport {
|
||||||
|
|
||||||
|
public File export(List<Object> metadata) {
|
||||||
|
System.out.println("Exporting data...");
|
||||||
|
// Export Metadata
|
||||||
|
File outputCSV = null;
|
||||||
|
return outputCSV;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
package com.baeldung.tight;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class JSONFetch {
|
||||||
|
public List<Object> fetchMetadata() {
|
||||||
|
List<Object> metadata = new ArrayList<>();
|
||||||
|
// Do some stuff
|
||||||
|
return metadata;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
package com.baeldung.tight;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class MetadataCollector {
|
||||||
|
private XMLFetch xmlFetch = new XMLFetch();
|
||||||
|
private JSONFetch jsonFetch = new JSONFetch();
|
||||||
|
private CSVExport csvExport = new CSVExport();
|
||||||
|
private PDFExport pdfExport = new PDFExport();
|
||||||
|
|
||||||
|
public void collectMetadata() {
|
||||||
|
List<Object> metadata = xmlFetch.fetchMetadata();
|
||||||
|
csvExport.export(metadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void collectMetadata(int inputType, int outputType) {
|
||||||
|
if (outputType == 1) {
|
||||||
|
List<Object> metadata = null;
|
||||||
|
if (inputType == 1) {
|
||||||
|
metadata = xmlFetch.fetchMetadata();
|
||||||
|
} else {
|
||||||
|
metadata = jsonFetch.fetchMetadata();
|
||||||
|
}
|
||||||
|
csvExport.export(metadata);
|
||||||
|
} else {
|
||||||
|
List<Object> metadata = null;
|
||||||
|
if (inputType == 1) {
|
||||||
|
metadata = xmlFetch.fetchMetadata();
|
||||||
|
} else {
|
||||||
|
metadata = jsonFetch.fetchMetadata();
|
||||||
|
}
|
||||||
|
pdfExport.export(metadata);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
package com.baeldung.tight;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class PDFExport {
|
||||||
|
public File export(List<Object> metadata) {
|
||||||
|
System.out.println("Exporting data...");
|
||||||
|
// Export Metadata
|
||||||
|
File outputPDF = null;
|
||||||
|
return outputPDF;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
package com.baeldung.tight;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class XMLFetch {
|
||||||
|
public List<Object> fetchMetadata() {
|
||||||
|
List<Object> metadata = new ArrayList<>();
|
||||||
|
// Do some stuff
|
||||||
|
return metadata;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
package com.baeldung.loose;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
class LooselyCouplingUnitTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenMetadataCollector_thenCollectMetadataXMLAndExportCSV() {
|
||||||
|
FetchMetadata metadata = new XMLFetch();
|
||||||
|
ExportMetadata exportMetadata = new CSVExport();
|
||||||
|
MetadataCollector collector = new MetadataCollector(metadata, exportMetadata);
|
||||||
|
collector.collectMetadata();
|
||||||
|
assertTrue(collector.getExportMetadata() instanceof CSVExport);
|
||||||
|
assertTrue(collector.getFetchMetadata() instanceof XMLFetch);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenMetadataCollector_thenCollectMetadataUsingJSONAndExportPDF() {
|
||||||
|
FetchMetadata metadata = new JSONFetch();
|
||||||
|
ExportMetadata exportMetadata = new PDFExport();
|
||||||
|
MetadataCollector collector = new MetadataCollector(metadata, exportMetadata);
|
||||||
|
collector.collectMetadata();
|
||||||
|
assertTrue(collector.getExportMetadata() instanceof PDFExport);
|
||||||
|
assertTrue(collector.getFetchMetadata() instanceof JSONFetch);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenMetadataCollector_thenCollectMetadataUsingXMLAndExportPDF() {
|
||||||
|
FetchMetadata metadata = new XMLFetch();
|
||||||
|
ExportMetadata exportMetadata = new PDFExport();
|
||||||
|
MetadataCollector collector = new MetadataCollector(metadata, exportMetadata);
|
||||||
|
collector.collectMetadata();
|
||||||
|
assertTrue(collector.getExportMetadata() instanceof PDFExport);
|
||||||
|
assertTrue(collector.getFetchMetadata() instanceof XMLFetch);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
package com.baeldung.tight;
|
||||||
|
|
||||||
|
import static org.mockito.Mockito.doNothing;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
class TightlyCouplingUnitTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenMetadataCollector_thenCollectMetadata() {
|
||||||
|
MetadataCollector collector = mock(MetadataCollector.class);
|
||||||
|
doNothing().when(collector)
|
||||||
|
.collectMetadata();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenMetadataCollectorWithDifferentInput_thenCollectMetadata() {
|
||||||
|
MetadataCollector collector = new MetadataCollector();
|
||||||
|
collector.collectMetadata(1, 1);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
This module contains articles about composing together CQRS and Event Sourcing
|
||||||
|
|
||||||
|
## Relevant Articles
|
||||||
|
|
||||||
|
- [CQRS and Event Sourcing in Java](https://www.baeldung.com/cqrs-event-sourcing-java)
|
|
@ -0,0 +1,23 @@
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<artifactId>cqrs-es</artifactId>
|
||||||
|
<version>1.0-SNAPSHOT</version>
|
||||||
|
<name>cqrs-es</name>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>com.baeldung</groupId>
|
||||||
|
<artifactId>patterns-modules</artifactId>
|
||||||
|
<version>1.0.0-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
<version>${lombok.version}</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
</project>
|
|
@ -0,0 +1,30 @@
|
||||||
|
package com.baeldung.patterns.cqrs.aggregates;
|
||||||
|
|
||||||
|
import com.baeldung.patterns.cqrs.commands.CreateUserCommand;
|
||||||
|
import com.baeldung.patterns.cqrs.commands.UpdateUserCommand;
|
||||||
|
import com.baeldung.patterns.cqrs.repository.UserWriteRepository;
|
||||||
|
import com.baeldung.patterns.domain.User;
|
||||||
|
|
||||||
|
public class UserAggregate {
|
||||||
|
|
||||||
|
private UserWriteRepository writeRepository;
|
||||||
|
|
||||||
|
public UserAggregate(UserWriteRepository repository) {
|
||||||
|
this.writeRepository = repository;
|
||||||
|
}
|
||||||
|
|
||||||
|
public User handleCreateUserCommand(CreateUserCommand command) {
|
||||||
|
User user = new User(command.getUserId(), command.getFirstName(), command.getLastName());
|
||||||
|
writeRepository.addUser(user.getUserid(), user);
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public User handleUpdateUserCommand(UpdateUserCommand command) {
|
||||||
|
User user = writeRepository.getUser(command.getUserId());
|
||||||
|
user.setAddresses(command.getAddresses());
|
||||||
|
user.setContacts(command.getContacts());
|
||||||
|
writeRepository.addUser(user.getUserid(), user);
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package com.baeldung.patterns.cqrs.commands;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class CreateUserCommand {
|
||||||
|
|
||||||
|
private String userId;
|
||||||
|
private String firstName;
|
||||||
|
private String lastName;
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
package com.baeldung.patterns.cqrs.commands;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import com.baeldung.patterns.domain.Address;
|
||||||
|
import com.baeldung.patterns.domain.Contact;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class UpdateUserCommand {
|
||||||
|
|
||||||
|
private String userId;
|
||||||
|
private Set<Address> addresses = new HashSet<>();
|
||||||
|
private Set<Contact> contacts = new HashSet<>();
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
package com.baeldung.patterns.cqrs.projections;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import com.baeldung.patterns.cqrs.queries.AddressByRegionQuery;
|
||||||
|
import com.baeldung.patterns.cqrs.queries.ContactByTypeQuery;
|
||||||
|
import com.baeldung.patterns.cqrs.repository.UserReadRepository;
|
||||||
|
import com.baeldung.patterns.domain.Address;
|
||||||
|
import com.baeldung.patterns.domain.Contact;
|
||||||
|
import com.baeldung.patterns.domain.UserAddress;
|
||||||
|
import com.baeldung.patterns.domain.UserContact;
|
||||||
|
|
||||||
|
public class UserProjection {
|
||||||
|
|
||||||
|
private UserReadRepository repository;
|
||||||
|
|
||||||
|
public UserProjection(UserReadRepository repository) {
|
||||||
|
this.repository = repository;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Contact> handle(ContactByTypeQuery query) throws Exception {
|
||||||
|
UserContact userContact = repository.getUserContact(query.getUserId());
|
||||||
|
if (userContact == null)
|
||||||
|
throw new Exception("User does not exist.");
|
||||||
|
return userContact.getContactByType()
|
||||||
|
.get(query.getContactType());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Address> handle(AddressByRegionQuery query) throws Exception {
|
||||||
|
UserAddress userAddress = repository.getUserAddress(query.getUserId());
|
||||||
|
if (userAddress == null)
|
||||||
|
throw new Exception("User does not exist.");
|
||||||
|
return userAddress.getAddressByRegion()
|
||||||
|
.get(query.getState());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
package com.baeldung.patterns.cqrs.projectors;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import com.baeldung.patterns.cqrs.repository.UserReadRepository;
|
||||||
|
import com.baeldung.patterns.domain.Address;
|
||||||
|
import com.baeldung.patterns.domain.Contact;
|
||||||
|
import com.baeldung.patterns.domain.User;
|
||||||
|
import com.baeldung.patterns.domain.UserAddress;
|
||||||
|
import com.baeldung.patterns.domain.UserContact;
|
||||||
|
|
||||||
|
public class UserProjector {
|
||||||
|
|
||||||
|
UserReadRepository readRepository = new UserReadRepository();
|
||||||
|
|
||||||
|
public UserProjector(UserReadRepository readRepository) {
|
||||||
|
this.readRepository = readRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void project(User user) {
|
||||||
|
UserContact userContact = Optional.ofNullable(readRepository.getUserContact(user.getUserid()))
|
||||||
|
.orElse(new UserContact());
|
||||||
|
Map<String, Set<Contact>> contactByType = new HashMap<>();
|
||||||
|
for (Contact contact : user.getContacts()) {
|
||||||
|
Set<Contact> contacts = Optional.ofNullable(contactByType.get(contact.getType()))
|
||||||
|
.orElse(new HashSet<>());
|
||||||
|
contacts.add(contact);
|
||||||
|
contactByType.put(contact.getType(), contacts);
|
||||||
|
}
|
||||||
|
userContact.setContactByType(contactByType);
|
||||||
|
readRepository.addUserContact(user.getUserid(), userContact);
|
||||||
|
|
||||||
|
UserAddress userAddress = Optional.ofNullable(readRepository.getUserAddress(user.getUserid()))
|
||||||
|
.orElse(new UserAddress());
|
||||||
|
Map<String, Set<Address>> addressByRegion = new HashMap<>();
|
||||||
|
for (Address address : user.getAddresses()) {
|
||||||
|
Set<Address> addresses = Optional.ofNullable(addressByRegion.get(address.getState()))
|
||||||
|
.orElse(new HashSet<>());
|
||||||
|
addresses.add(address);
|
||||||
|
addressByRegion.put(address.getState(), addresses);
|
||||||
|
}
|
||||||
|
userAddress.setAddressByRegion(addressByRegion);
|
||||||
|
readRepository.addUserAddress(user.getUserid(), userAddress);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
package com.baeldung.patterns.cqrs.queries;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class AddressByRegionQuery {
|
||||||
|
|
||||||
|
private String userId;
|
||||||
|
private String state;
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
package com.baeldung.patterns.cqrs.queries;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class ContactByTypeQuery {
|
||||||
|
|
||||||
|
private String userId;
|
||||||
|
private String contactType;
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
package com.baeldung.patterns.cqrs.repository;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.baeldung.patterns.domain.UserAddress;
|
||||||
|
import com.baeldung.patterns.domain.UserContact;
|
||||||
|
|
||||||
|
public class UserReadRepository {
|
||||||
|
|
||||||
|
private Map<String, UserAddress> userAddress = new HashMap<>();
|
||||||
|
|
||||||
|
private Map<String, UserContact> userContact = new HashMap<>();
|
||||||
|
|
||||||
|
public void addUserAddress(String id, UserAddress user) {
|
||||||
|
userAddress.put(id, user);
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserAddress getUserAddress(String id) {
|
||||||
|
return userAddress.get(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addUserContact(String id, UserContact user) {
|
||||||
|
userContact.put(id, user);
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserContact getUserContact(String id) {
|
||||||
|
return userContact.get(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
package com.baeldung.patterns.cqrs.repository;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.baeldung.patterns.domain.User;
|
||||||
|
|
||||||
|
public class UserWriteRepository {
|
||||||
|
|
||||||
|
private Map<String, User> store = new HashMap<>();
|
||||||
|
|
||||||
|
public void addUser(String id, User user) {
|
||||||
|
store.put(id, user);
|
||||||
|
}
|
||||||
|
|
||||||
|
public User getUser(String id) {
|
||||||
|
return store.get(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
package com.baeldung.patterns.crud.repository;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.baeldung.patterns.domain.User;
|
||||||
|
|
||||||
|
public class UserRepository {
|
||||||
|
|
||||||
|
private Map<String, User> store = new HashMap<>();
|
||||||
|
|
||||||
|
public void addUser(String id, User user) {
|
||||||
|
store.put(id, user);
|
||||||
|
}
|
||||||
|
|
||||||
|
public User getUser(String id) {
|
||||||
|
return store.get(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
package com.baeldung.patterns.crud.service;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import com.baeldung.patterns.crud.repository.UserRepository;
|
||||||
|
import com.baeldung.patterns.domain.Address;
|
||||||
|
import com.baeldung.patterns.domain.Contact;
|
||||||
|
import com.baeldung.patterns.domain.User;
|
||||||
|
|
||||||
|
public class UserService {
|
||||||
|
|
||||||
|
private UserRepository repository;
|
||||||
|
|
||||||
|
public UserService(UserRepository repository) {
|
||||||
|
this.repository = repository;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void createUser(String userId, String firstName, String lastName) {
|
||||||
|
User user = new User(userId, firstName, lastName);
|
||||||
|
repository.addUser(userId, user);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateUser(String userId, Set<Contact> contacts, Set<Address> addresses) throws Exception {
|
||||||
|
User user = repository.getUser(userId);
|
||||||
|
if (user == null)
|
||||||
|
throw new Exception("User does not exist.");
|
||||||
|
user.setContacts(contacts);
|
||||||
|
user.setAddresses(addresses);
|
||||||
|
repository.addUser(userId, user);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Contact> getContactByType(String userId, String contactType) throws Exception {
|
||||||
|
User user = repository.getUser(userId);
|
||||||
|
if (user == null)
|
||||||
|
throw new Exception("User does not exit.");
|
||||||
|
Set<Contact> contacts = user.getContacts();
|
||||||
|
return contacts.stream()
|
||||||
|
.filter(c -> c.getType()
|
||||||
|
.equals(contactType))
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Address> getAddressByRegion(String userId, String state) throws Exception {
|
||||||
|
User user = repository.getUser(userId);
|
||||||
|
if (user == null)
|
||||||
|
throw new Exception("User does not exist.");
|
||||||
|
Set<Address> addresses = user.getAddresses();
|
||||||
|
return addresses.stream()
|
||||||
|
.filter(a -> a.getState()
|
||||||
|
.equals(state))
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package com.baeldung.patterns.domain;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class Address {
|
||||||
|
|
||||||
|
private String city;
|
||||||
|
private String state;
|
||||||
|
private String postcode;
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
package com.baeldung.patterns.domain;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class Contact {
|
||||||
|
|
||||||
|
private String type;
|
||||||
|
private String detail;
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
package com.baeldung.patterns.domain;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NonNull;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class User {
|
||||||
|
@NonNull
|
||||||
|
private String userid;
|
||||||
|
@NonNull
|
||||||
|
private String firstname;
|
||||||
|
@NonNull
|
||||||
|
private String lastname;
|
||||||
|
private Set<Contact> contacts = new HashSet<>();
|
||||||
|
private Set<Address> addresses = new HashSet<>();
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package com.baeldung.patterns.domain;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class UserAddress {
|
||||||
|
|
||||||
|
private Map<String, Set<Address>> addressByRegion = new HashMap<>();
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package com.baeldung.patterns.domain;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class UserContact {
|
||||||
|
|
||||||
|
private Map<String, Set<Contact>> contactByType = new HashMap<>();
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
package com.baeldung.patterns.es.events;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import lombok.ToString;
|
||||||
|
|
||||||
|
@ToString
|
||||||
|
public abstract class Event {
|
||||||
|
|
||||||
|
public final UUID id = UUID.randomUUID();
|
||||||
|
|
||||||
|
public final Date created = new Date();
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
package com.baeldung.patterns.es.events;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@EqualsAndHashCode(callSuper = false)
|
||||||
|
public class UserAddressAddedEvent extends Event {
|
||||||
|
|
||||||
|
private String city;
|
||||||
|
private String state;
|
||||||
|
private String postCode;
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
package com.baeldung.patterns.es.events;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@EqualsAndHashCode(callSuper = false)
|
||||||
|
public class UserAddressRemovedEvent extends Event {
|
||||||
|
|
||||||
|
private String city;
|
||||||
|
private String state;
|
||||||
|
private String postCode;
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
package com.baeldung.patterns.es.events;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@EqualsAndHashCode(callSuper = false)
|
||||||
|
public class UserContactAddedEvent extends Event {
|
||||||
|
|
||||||
|
private String contactType;
|
||||||
|
private String contactDetails;
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
package com.baeldung.patterns.es.events;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@EqualsAndHashCode(callSuper = false)
|
||||||
|
public class UserContactRemovedEvent extends Event {
|
||||||
|
|
||||||
|
private String contactType;
|
||||||
|
private String contactDetails;
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
package com.baeldung.patterns.es.events;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@EqualsAndHashCode(callSuper = false)
|
||||||
|
public class UserCreatedEvent extends Event {
|
||||||
|
|
||||||
|
private String userId;
|
||||||
|
private String firstName;
|
||||||
|
private String lastName;
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
package com.baeldung.patterns.es.repository;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.baeldung.patterns.es.events.Event;
|
||||||
|
|
||||||
|
public class EventStore {
|
||||||
|
|
||||||
|
private Map<String, List<Event>> store = new HashMap<>();
|
||||||
|
|
||||||
|
public void addEvent(String id, Event event) {
|
||||||
|
List<Event> events = store.get(id);
|
||||||
|
if (events == null) {
|
||||||
|
events = new ArrayList<Event>();
|
||||||
|
events.add(event);
|
||||||
|
store.put(id, events);
|
||||||
|
} else {
|
||||||
|
events.add(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Event> getEvents(String id) {
|
||||||
|
return store.get(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,72 @@
|
||||||
|
package com.baeldung.patterns.es.service;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import com.baeldung.patterns.domain.Address;
|
||||||
|
import com.baeldung.patterns.domain.Contact;
|
||||||
|
import com.baeldung.patterns.domain.User;
|
||||||
|
import com.baeldung.patterns.es.events.UserAddressAddedEvent;
|
||||||
|
import com.baeldung.patterns.es.events.UserAddressRemovedEvent;
|
||||||
|
import com.baeldung.patterns.es.events.UserContactAddedEvent;
|
||||||
|
import com.baeldung.patterns.es.events.UserContactRemovedEvent;
|
||||||
|
import com.baeldung.patterns.es.events.UserCreatedEvent;
|
||||||
|
import com.baeldung.patterns.es.repository.EventStore;
|
||||||
|
|
||||||
|
public class UserService {
|
||||||
|
|
||||||
|
private EventStore repository;
|
||||||
|
|
||||||
|
public UserService(EventStore repository) {
|
||||||
|
this.repository = repository;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void createUser(String userId, String firstName, String lastName) {
|
||||||
|
repository.addEvent(userId, new UserCreatedEvent(userId, firstName, lastName));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateUser(String userId, Set<Contact> contacts, Set<Address> addresses) throws Exception {
|
||||||
|
User user = UserUtility.recreateUserState(repository, userId);
|
||||||
|
if (user == null)
|
||||||
|
throw new Exception("User does not exist.");
|
||||||
|
|
||||||
|
user.getContacts()
|
||||||
|
.stream()
|
||||||
|
.filter(c -> !contacts.contains(c))
|
||||||
|
.forEach(c -> repository.addEvent(userId, new UserContactRemovedEvent(c.getType(), c.getDetail())));
|
||||||
|
contacts.stream()
|
||||||
|
.filter(c -> !user.getContacts()
|
||||||
|
.contains(c))
|
||||||
|
.forEach(c -> repository.addEvent(userId, new UserContactAddedEvent(c.getType(), c.getDetail())));
|
||||||
|
user.getAddresses()
|
||||||
|
.stream()
|
||||||
|
.filter(a -> !addresses.contains(a))
|
||||||
|
.forEach(a -> repository.addEvent(userId, new UserAddressRemovedEvent(a.getCity(), a.getState(), a.getPostcode())));
|
||||||
|
addresses.stream()
|
||||||
|
.filter(a -> !user.getAddresses()
|
||||||
|
.contains(a))
|
||||||
|
.forEach(a -> repository.addEvent(userId, new UserAddressAddedEvent(a.getCity(), a.getState(), a.getPostcode())));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Contact> getContactByType(String userId, String contactType) throws Exception {
|
||||||
|
User user = UserUtility.recreateUserState(repository, userId);
|
||||||
|
if (user == null)
|
||||||
|
throw new Exception("User does not exist.");
|
||||||
|
return user.getContacts()
|
||||||
|
.stream()
|
||||||
|
.filter(c -> c.getType()
|
||||||
|
.equals(contactType))
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Address> getAddressByRegion(String userId, String state) throws Exception {
|
||||||
|
User user = UserUtility.recreateUserState(repository, userId);
|
||||||
|
if (user == null)
|
||||||
|
throw new Exception("User does not exist.");
|
||||||
|
return user.getAddresses()
|
||||||
|
.stream()
|
||||||
|
.filter(a -> a.getState()
|
||||||
|
.equals(state))
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
package com.baeldung.patterns.es.service;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import com.baeldung.patterns.domain.Address;
|
||||||
|
import com.baeldung.patterns.domain.Contact;
|
||||||
|
import com.baeldung.patterns.domain.User;
|
||||||
|
import com.baeldung.patterns.es.events.Event;
|
||||||
|
import com.baeldung.patterns.es.events.UserAddressAddedEvent;
|
||||||
|
import com.baeldung.patterns.es.events.UserAddressRemovedEvent;
|
||||||
|
import com.baeldung.patterns.es.events.UserContactAddedEvent;
|
||||||
|
import com.baeldung.patterns.es.events.UserContactRemovedEvent;
|
||||||
|
import com.baeldung.patterns.es.events.UserCreatedEvent;
|
||||||
|
import com.baeldung.patterns.es.repository.EventStore;
|
||||||
|
|
||||||
|
public class UserUtility {
|
||||||
|
|
||||||
|
public static User recreateUserState(EventStore store, String userId) {
|
||||||
|
User user = null;
|
||||||
|
|
||||||
|
List<Event> events = store.getEvents(userId);
|
||||||
|
for (Event event : events) {
|
||||||
|
if (event instanceof UserCreatedEvent) {
|
||||||
|
UserCreatedEvent e = (UserCreatedEvent) event;
|
||||||
|
user = new User(e.getUserId(), e.getFirstName(), e.getLastName());
|
||||||
|
}
|
||||||
|
if (event instanceof UserAddressAddedEvent) {
|
||||||
|
UserAddressAddedEvent e = (UserAddressAddedEvent) event;
|
||||||
|
Address address = new Address(e.getCity(), e.getState(), e.getPostCode());
|
||||||
|
if (user != null)
|
||||||
|
user.getAddresses()
|
||||||
|
.add(address);
|
||||||
|
}
|
||||||
|
if (event instanceof UserAddressRemovedEvent) {
|
||||||
|
UserAddressRemovedEvent e = (UserAddressRemovedEvent) event;
|
||||||
|
Address address = new Address(e.getCity(), e.getState(), e.getPostCode());
|
||||||
|
if (user != null)
|
||||||
|
user.getAddresses()
|
||||||
|
.remove(address);
|
||||||
|
}
|
||||||
|
if (event instanceof UserContactAddedEvent) {
|
||||||
|
UserContactAddedEvent e = (UserContactAddedEvent) event;
|
||||||
|
Contact contact = new Contact(e.getContactType(), e.getContactDetails());
|
||||||
|
if (user != null)
|
||||||
|
user.getContacts()
|
||||||
|
.add(contact);
|
||||||
|
}
|
||||||
|
if (event instanceof UserContactRemovedEvent) {
|
||||||
|
UserContactRemovedEvent e = (UserContactRemovedEvent) event;
|
||||||
|
Contact contact = new Contact(e.getContactType(), e.getContactDetails());
|
||||||
|
if (user != null)
|
||||||
|
user.getContacts()
|
||||||
|
.remove(contact);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,87 @@
|
||||||
|
package com.baeldung.patterns.escqrs.aggregates;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import com.baeldung.patterns.cqrs.commands.CreateUserCommand;
|
||||||
|
import com.baeldung.patterns.cqrs.commands.UpdateUserCommand;
|
||||||
|
import com.baeldung.patterns.domain.Address;
|
||||||
|
import com.baeldung.patterns.domain.Contact;
|
||||||
|
import com.baeldung.patterns.domain.User;
|
||||||
|
import com.baeldung.patterns.es.events.Event;
|
||||||
|
import com.baeldung.patterns.es.events.UserAddressAddedEvent;
|
||||||
|
import com.baeldung.patterns.es.events.UserAddressRemovedEvent;
|
||||||
|
import com.baeldung.patterns.es.events.UserContactAddedEvent;
|
||||||
|
import com.baeldung.patterns.es.events.UserContactRemovedEvent;
|
||||||
|
import com.baeldung.patterns.es.events.UserCreatedEvent;
|
||||||
|
import com.baeldung.patterns.es.repository.EventStore;
|
||||||
|
import com.baeldung.patterns.es.service.UserUtility;
|
||||||
|
|
||||||
|
public class UserAggregate {
|
||||||
|
|
||||||
|
private EventStore writeRepository;
|
||||||
|
|
||||||
|
public UserAggregate(EventStore repository) {
|
||||||
|
this.writeRepository = repository;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Event> handleCreateUserCommand(CreateUserCommand command) {
|
||||||
|
UserCreatedEvent event = new UserCreatedEvent(command.getUserId(), command.getFirstName(), command.getLastName());
|
||||||
|
writeRepository.addEvent(command.getUserId(), event);
|
||||||
|
return Arrays.asList(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Event> handleUpdateUserCommand(UpdateUserCommand command) {
|
||||||
|
User user = UserUtility.recreateUserState(writeRepository, command.getUserId());
|
||||||
|
List<Event> events = new ArrayList<>();
|
||||||
|
|
||||||
|
List<Contact> contactsToRemove = user.getContacts()
|
||||||
|
.stream()
|
||||||
|
.filter(c -> !command.getContacts()
|
||||||
|
.contains(c))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
for (Contact contact : contactsToRemove) {
|
||||||
|
UserContactRemovedEvent contactRemovedEvent = new UserContactRemovedEvent(contact.getType(), contact.getDetail());
|
||||||
|
events.add(contactRemovedEvent);
|
||||||
|
writeRepository.addEvent(command.getUserId(), contactRemovedEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Contact> contactsToAdd = command.getContacts()
|
||||||
|
.stream()
|
||||||
|
.filter(c -> !user.getContacts()
|
||||||
|
.contains(c))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
for (Contact contact : contactsToAdd) {
|
||||||
|
UserContactAddedEvent contactAddedEvent = new UserContactAddedEvent(contact.getType(), contact.getDetail());
|
||||||
|
events.add(contactAddedEvent);
|
||||||
|
writeRepository.addEvent(command.getUserId(), contactAddedEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Address> addressesToRemove = user.getAddresses()
|
||||||
|
.stream()
|
||||||
|
.filter(a -> !command.getAddresses()
|
||||||
|
.contains(a))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
for (Address address : addressesToRemove) {
|
||||||
|
UserAddressRemovedEvent addressRemovedEvent = new UserAddressRemovedEvent(address.getCity(), address.getState(), address.getPostcode());
|
||||||
|
events.add(addressRemovedEvent);
|
||||||
|
writeRepository.addEvent(command.getUserId(), addressRemovedEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Address> addressesToAdd = command.getAddresses()
|
||||||
|
.stream()
|
||||||
|
.filter(a -> !user.getAddresses()
|
||||||
|
.contains(a))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
for (Address address : addressesToAdd) {
|
||||||
|
UserAddressAddedEvent addressAddedEvent = new UserAddressAddedEvent(address.getCity(), address.getState(), address.getPostcode());
|
||||||
|
events.add(addressAddedEvent);
|
||||||
|
writeRepository.addEvent(command.getUserId(), addressAddedEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
return events;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,91 @@
|
||||||
|
package com.baeldung.patterns.escqrs.projectors;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import com.baeldung.patterns.cqrs.repository.UserReadRepository;
|
||||||
|
import com.baeldung.patterns.domain.Address;
|
||||||
|
import com.baeldung.patterns.domain.Contact;
|
||||||
|
import com.baeldung.patterns.domain.UserAddress;
|
||||||
|
import com.baeldung.patterns.domain.UserContact;
|
||||||
|
import com.baeldung.patterns.es.events.Event;
|
||||||
|
import com.baeldung.patterns.es.events.UserAddressAddedEvent;
|
||||||
|
import com.baeldung.patterns.es.events.UserAddressRemovedEvent;
|
||||||
|
import com.baeldung.patterns.es.events.UserContactAddedEvent;
|
||||||
|
import com.baeldung.patterns.es.events.UserContactRemovedEvent;
|
||||||
|
|
||||||
|
public class UserProjector {
|
||||||
|
|
||||||
|
UserReadRepository readRepository = new UserReadRepository();
|
||||||
|
|
||||||
|
public UserProjector(UserReadRepository readRepository) {
|
||||||
|
this.readRepository = readRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void project(String userId, List<Event> events) {
|
||||||
|
|
||||||
|
for (Event event : events) {
|
||||||
|
if (event instanceof UserAddressAddedEvent)
|
||||||
|
apply(userId, (UserAddressAddedEvent) event);
|
||||||
|
if (event instanceof UserAddressRemovedEvent)
|
||||||
|
apply(userId, (UserAddressRemovedEvent) event);
|
||||||
|
if (event instanceof UserContactAddedEvent)
|
||||||
|
apply(userId, (UserContactAddedEvent) event);
|
||||||
|
if (event instanceof UserContactRemovedEvent)
|
||||||
|
apply(userId, (UserContactRemovedEvent) event);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void apply(String userId, UserAddressAddedEvent event) {
|
||||||
|
Address address = new Address(event.getCity(), event.getState(), event.getPostCode());
|
||||||
|
UserAddress userAddress = Optional.ofNullable(readRepository.getUserAddress(userId))
|
||||||
|
.orElse(new UserAddress());
|
||||||
|
Set<Address> addresses = Optional.ofNullable(userAddress.getAddressByRegion()
|
||||||
|
.get(address.getState()))
|
||||||
|
.orElse(new HashSet<>());
|
||||||
|
addresses.add(address);
|
||||||
|
userAddress.getAddressByRegion()
|
||||||
|
.put(address.getState(), addresses);
|
||||||
|
readRepository.addUserAddress(userId, userAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void apply(String userId, UserAddressRemovedEvent event) {
|
||||||
|
Address address = new Address(event.getCity(), event.getState(), event.getPostCode());
|
||||||
|
UserAddress userAddress = readRepository.getUserAddress(userId);
|
||||||
|
if (userAddress != null) {
|
||||||
|
Set<Address> addresses = userAddress.getAddressByRegion()
|
||||||
|
.get(address.getState());
|
||||||
|
if (addresses != null)
|
||||||
|
addresses.remove(address);
|
||||||
|
readRepository.addUserAddress(userId, userAddress);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void apply(String userId, UserContactAddedEvent event) {
|
||||||
|
Contact contact = new Contact(event.getContactType(), event.getContactDetails());
|
||||||
|
UserContact userContact = Optional.ofNullable(readRepository.getUserContact(userId))
|
||||||
|
.orElse(new UserContact());
|
||||||
|
Set<Contact> contacts = Optional.ofNullable(userContact.getContactByType()
|
||||||
|
.get(contact.getType()))
|
||||||
|
.orElse(new HashSet<>());
|
||||||
|
contacts.add(contact);
|
||||||
|
userContact.getContactByType()
|
||||||
|
.put(contact.getType(), contacts);
|
||||||
|
readRepository.addUserContact(userId, userContact);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void apply(String userId, UserContactRemovedEvent event) {
|
||||||
|
Contact contact = new Contact(event.getContactType(), event.getContactDetails());
|
||||||
|
UserContact userContact = readRepository.getUserContact(userId);
|
||||||
|
if (userContact != null) {
|
||||||
|
Set<Contact> contacts = userContact.getContactByType()
|
||||||
|
.get(contact.getType());
|
||||||
|
if (contacts != null)
|
||||||
|
contacts.remove(contact);
|
||||||
|
readRepository.addUserContact(userId, userContact);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,74 @@
|
||||||
|
package com.baeldung.patterns.cqrs;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import com.baeldung.patterns.cqrs.aggregates.UserAggregate;
|
||||||
|
import com.baeldung.patterns.cqrs.commands.CreateUserCommand;
|
||||||
|
import com.baeldung.patterns.cqrs.commands.UpdateUserCommand;
|
||||||
|
import com.baeldung.patterns.cqrs.projections.UserProjection;
|
||||||
|
import com.baeldung.patterns.cqrs.projectors.UserProjector;
|
||||||
|
import com.baeldung.patterns.cqrs.queries.AddressByRegionQuery;
|
||||||
|
import com.baeldung.patterns.cqrs.queries.ContactByTypeQuery;
|
||||||
|
import com.baeldung.patterns.cqrs.repository.UserReadRepository;
|
||||||
|
import com.baeldung.patterns.cqrs.repository.UserWriteRepository;
|
||||||
|
import com.baeldung.patterns.domain.Address;
|
||||||
|
import com.baeldung.patterns.domain.Contact;
|
||||||
|
import com.baeldung.patterns.domain.User;
|
||||||
|
|
||||||
|
public class ApplicationUnitTest {
|
||||||
|
|
||||||
|
private UserWriteRepository writeRepository;
|
||||||
|
private UserReadRepository readRepository;
|
||||||
|
private UserProjector projector;
|
||||||
|
private UserAggregate userAggregate;
|
||||||
|
private UserProjection userProjection;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
writeRepository = new UserWriteRepository();
|
||||||
|
readRepository = new UserReadRepository();
|
||||||
|
projector = new UserProjector(readRepository);
|
||||||
|
userAggregate = new UserAggregate(writeRepository);
|
||||||
|
userProjection = new UserProjection(readRepository);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenCQRSApplication_whenCommandRun_thenQueryShouldReturnResult() throws Exception {
|
||||||
|
String userId = UUID.randomUUID()
|
||||||
|
.toString();
|
||||||
|
User user = null;
|
||||||
|
CreateUserCommand createUserCommand = new CreateUserCommand(userId, "Tom", "Sawyer");
|
||||||
|
user = userAggregate.handleCreateUserCommand(createUserCommand);
|
||||||
|
projector.project(user);
|
||||||
|
|
||||||
|
UpdateUserCommand updateUserCommand = new UpdateUserCommand(user.getUserid(), Stream.of(new Address("New York", "NY", "10001"), new Address("Los Angeles", "CA", "90001"))
|
||||||
|
.collect(Collectors.toSet()),
|
||||||
|
Stream.of(new Contact("EMAIL", "tom.sawyer@gmail.com"), new Contact("EMAIL", "tom.sawyer@rediff.com"))
|
||||||
|
.collect(Collectors.toSet()));
|
||||||
|
user = userAggregate.handleUpdateUserCommand(updateUserCommand);
|
||||||
|
projector.project(user);
|
||||||
|
|
||||||
|
updateUserCommand = new UpdateUserCommand(userId, Stream.of(new Address("New York", "NY", "10001"), new Address("Housten", "TX", "77001"))
|
||||||
|
.collect(Collectors.toSet()),
|
||||||
|
Stream.of(new Contact("EMAIL", "tom.sawyer@gmail.com"), new Contact("PHONE", "700-000-0001"))
|
||||||
|
.collect(Collectors.toSet()));
|
||||||
|
user = userAggregate.handleUpdateUserCommand(updateUserCommand);
|
||||||
|
projector.project(user);
|
||||||
|
|
||||||
|
ContactByTypeQuery contactByTypeQuery = new ContactByTypeQuery(userId, "EMAIL");
|
||||||
|
assertEquals(Stream.of(new Contact("EMAIL", "tom.sawyer@gmail.com"))
|
||||||
|
.collect(Collectors.toSet()), userProjection.handle(contactByTypeQuery));
|
||||||
|
AddressByRegionQuery addressByRegionQuery = new AddressByRegionQuery(userId, "NY");
|
||||||
|
assertEquals(Stream.of(new Address("New York", "NY", "10001"))
|
||||||
|
.collect(Collectors.toSet()), userProjection.handle(addressByRegionQuery));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
package com.baeldung.patterns.crud;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import com.baeldung.patterns.crud.repository.UserRepository;
|
||||||
|
import com.baeldung.patterns.crud.service.UserService;
|
||||||
|
import com.baeldung.patterns.domain.Address;
|
||||||
|
import com.baeldung.patterns.domain.Contact;
|
||||||
|
|
||||||
|
public class ApplicationUnitTest {
|
||||||
|
|
||||||
|
private UserRepository repository;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
repository = new UserRepository();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenCRUDApplication_whenDataCreated_thenDataCanBeFetched() throws Exception {
|
||||||
|
UserService service = new UserService(repository);
|
||||||
|
String userId = UUID.randomUUID()
|
||||||
|
.toString();
|
||||||
|
|
||||||
|
service.createUser(userId, "Tom", "Sawyer");
|
||||||
|
service.updateUser(userId, Stream.of(new Contact("EMAIL", "tom.sawyer@gmail.com"), new Contact("EMAIL", "tom.sawyer@rediff.com"), new Contact("PHONE", "700-000-0001"))
|
||||||
|
.collect(Collectors.toSet()),
|
||||||
|
Stream.of(new Address("New York", "NY", "10001"), new Address("Los Angeles", "CA", "90001"), new Address("Housten", "TX", "77001"))
|
||||||
|
.collect(Collectors.toSet()));
|
||||||
|
service.updateUser(userId, Stream.of(new Contact("EMAIL", "tom.sawyer@gmail.com"), new Contact("PHONE", "700-000-0001"))
|
||||||
|
.collect(Collectors.toSet()),
|
||||||
|
Stream.of(new Address("New York", "NY", "10001"), new Address("Housten", "TX", "77001"))
|
||||||
|
.collect(Collectors.toSet()));
|
||||||
|
|
||||||
|
assertEquals(Stream.of(new Contact("EMAIL", "tom.sawyer@gmail.com"))
|
||||||
|
.collect(Collectors.toSet()), service.getContactByType(userId, "EMAIL"));
|
||||||
|
assertEquals(Stream.of(new Address("New York", "NY", "10001"))
|
||||||
|
.collect(Collectors.toSet()), service.getAddressByRegion(userId, "NY"));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
package com.baeldung.patterns.es;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import com.baeldung.patterns.domain.Address;
|
||||||
|
import com.baeldung.patterns.domain.Contact;
|
||||||
|
import com.baeldung.patterns.es.repository.EventStore;
|
||||||
|
import com.baeldung.patterns.es.service.UserService;
|
||||||
|
|
||||||
|
public class ApplicationUnitTest {
|
||||||
|
|
||||||
|
private EventStore repository;
|
||||||
|
private UserService service;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
repository = new EventStore();
|
||||||
|
service = new UserService(repository);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenCRUDApplication_whenDataCreated_thenDataCanBeFetched() throws Exception {
|
||||||
|
String userId = UUID.randomUUID()
|
||||||
|
.toString();
|
||||||
|
|
||||||
|
service.createUser(userId, "Tom", "Sawyer");
|
||||||
|
service.updateUser(userId, Stream.of(new Contact("EMAIL", "tom.sawyer@gmail.com"), new Contact("EMAIL", "tom.sawyer@rediff.com"), new Contact("PHONE", "700-000-0001"))
|
||||||
|
.collect(Collectors.toSet()),
|
||||||
|
Stream.of(new Address("New York", "NY", "10001"), new Address("Los Angeles", "CA", "90001"), new Address("Housten", "TX", "77001"))
|
||||||
|
.collect(Collectors.toSet()));
|
||||||
|
service.updateUser(userId, Stream.of(new Contact("EMAIL", "tom.sawyer@gmail.com"), new Contact("PHONE", "700-000-0001"))
|
||||||
|
.collect(Collectors.toSet()),
|
||||||
|
Stream.of(new Address("New York", "NY", "10001"), new Address("Housten", "TX", "77001"))
|
||||||
|
.collect(Collectors.toSet()));
|
||||||
|
|
||||||
|
assertEquals(Stream.of(new Contact("EMAIL", "tom.sawyer@gmail.com"))
|
||||||
|
.collect(Collectors.toSet()), service.getContactByType(userId, "EMAIL"));
|
||||||
|
assertEquals(Stream.of(new Address("New York", "NY", "10001"))
|
||||||
|
.collect(Collectors.toSet()), service.getAddressByRegion(userId, "NY"));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,76 @@
|
||||||
|
package com.baeldung.patterns.escqrs;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import com.baeldung.patterns.cqrs.commands.CreateUserCommand;
|
||||||
|
import com.baeldung.patterns.cqrs.commands.UpdateUserCommand;
|
||||||
|
import com.baeldung.patterns.cqrs.projections.UserProjection;
|
||||||
|
import com.baeldung.patterns.cqrs.queries.AddressByRegionQuery;
|
||||||
|
import com.baeldung.patterns.cqrs.queries.ContactByTypeQuery;
|
||||||
|
import com.baeldung.patterns.cqrs.repository.UserReadRepository;
|
||||||
|
import com.baeldung.patterns.domain.Address;
|
||||||
|
import com.baeldung.patterns.domain.Contact;
|
||||||
|
import com.baeldung.patterns.es.events.Event;
|
||||||
|
import com.baeldung.patterns.es.repository.EventStore;
|
||||||
|
import com.baeldung.patterns.escqrs.aggregates.UserAggregate;
|
||||||
|
import com.baeldung.patterns.escqrs.projectors.UserProjector;
|
||||||
|
|
||||||
|
public class ApplicationUnitTest {
|
||||||
|
|
||||||
|
private EventStore writeRepository;
|
||||||
|
private UserReadRepository readRepository;
|
||||||
|
private UserProjector projector;
|
||||||
|
private UserAggregate userAggregate;
|
||||||
|
private UserProjection userProjection;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
writeRepository = new EventStore();
|
||||||
|
readRepository = new UserReadRepository();
|
||||||
|
projector = new UserProjector(readRepository);
|
||||||
|
userAggregate = new UserAggregate(writeRepository);
|
||||||
|
userProjection = new UserProjection(readRepository);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenCQRSApplication_whenCommandRun_thenQueryShouldReturnResult() throws Exception {
|
||||||
|
String userId = UUID.randomUUID()
|
||||||
|
.toString();
|
||||||
|
List<Event> events = null;
|
||||||
|
CreateUserCommand createUserCommand = new CreateUserCommand(userId, "Kumar", "Chandrakant");
|
||||||
|
events = userAggregate.handleCreateUserCommand(createUserCommand);
|
||||||
|
|
||||||
|
projector.project(userId, events);
|
||||||
|
|
||||||
|
UpdateUserCommand updateUserCommand = new UpdateUserCommand(userId, Stream.of(new Address("New York", "NY", "10001"), new Address("Los Angeles", "CA", "90001"))
|
||||||
|
.collect(Collectors.toSet()),
|
||||||
|
Stream.of(new Contact("EMAIL", "tom.sawyer@gmail.com"), new Contact("EMAIL", "tom.sawyer@rediff.com"))
|
||||||
|
.collect(Collectors.toSet()));
|
||||||
|
events = userAggregate.handleUpdateUserCommand(updateUserCommand);
|
||||||
|
projector.project(userId, events);
|
||||||
|
|
||||||
|
updateUserCommand = new UpdateUserCommand(userId, Stream.of(new Address("New York", "NY", "10001"), new Address("Housten", "TX", "77001"))
|
||||||
|
.collect(Collectors.toSet()),
|
||||||
|
Stream.of(new Contact("EMAIL", "tom.sawyer@gmail.com"), new Contact("PHONE", "700-000-0001"))
|
||||||
|
.collect(Collectors.toSet()));
|
||||||
|
events = userAggregate.handleUpdateUserCommand(updateUserCommand);
|
||||||
|
projector.project(userId, events);
|
||||||
|
|
||||||
|
ContactByTypeQuery contactByTypeQuery = new ContactByTypeQuery(userId, "EMAIL");
|
||||||
|
assertEquals(Stream.of(new Contact("EMAIL", "tom.sawyer@gmail.com"))
|
||||||
|
.collect(Collectors.toSet()), userProjection.handle(contactByTypeQuery));
|
||||||
|
AddressByRegionQuery addressByRegionQuery = new AddressByRegionQuery(userId, "NY");
|
||||||
|
assertEquals(Stream.of(new Address("New York", "NY", "10001"))
|
||||||
|
.collect(Collectors.toSet()), userProjection.handle(addressByRegionQuery));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
### Relevant Articles:
|
||||||
|
- [Service Locator Pattern and Java Implementation](https://www.baeldung.com/java-service-locator-pattern)
|
||||||
|
- [The DAO Pattern in Java](https://www.baeldung.com/java-dao-pattern)
|
||||||
|
- [DAO vs Repository Patterns](https://www.baeldung.com/java-dao-vs-repository)
|
||||||
|
- [Difference Between MVC and MVP Patterns](https://www.baeldung.com/mvc-vs-mvp-pattern)
|
||||||
|
- [The DTO Pattern (Data Transfer Object)](https://www.baeldung.com/java-dto-pattern)
|
||||||
|
- [SEDA With Spring Integration and Apache Camel](https://www.baeldung.com/spring-apache-camel-seda-integration)
|
|
@ -0,0 +1,77 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<artifactId>design-patterns-architectural</artifactId>
|
||||||
|
<version>1.0</version>
|
||||||
|
<name>design-patterns-architectural</name>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>com.baeldung</groupId>
|
||||||
|
<artifactId>patterns-modules</artifactId>
|
||||||
|
<version>1.0.0-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
|
<version>${spring-boot.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
|
<version>${spring-boot.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.rest-assured</groupId>
|
||||||
|
<artifactId>rest-assured</artifactId>
|
||||||
|
<version>${rest-assured.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.hibernate</groupId>
|
||||||
|
<artifactId>hibernate-core</artifactId>
|
||||||
|
<version>${hibernate-core.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>mysql</groupId>
|
||||||
|
<artifactId>mysql-connector-java</artifactId>
|
||||||
|
<version>${mysql-connector.version}</version>
|
||||||
|
<type>jar</type>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-integration</artifactId>
|
||||||
|
<version>${spring-boot-starter-integration.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.integration</groupId>
|
||||||
|
<artifactId>spring-integration-test</artifactId>
|
||||||
|
<version>${spring-integration-test.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.camel</groupId>
|
||||||
|
<artifactId>camel-core</artifactId>
|
||||||
|
<version>${camel-core.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.camel</groupId>
|
||||||
|
<artifactId>camel-test-junit5</artifactId>
|
||||||
|
<version>${camel-test-junit5.version}</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<hibernate-core.version>5.2.16.Final</hibernate-core.version>
|
||||||
|
<mysql-connector.version>6.0.6</mysql-connector.version>
|
||||||
|
<spring-boot.version>2.7.5</spring-boot.version>
|
||||||
|
<rest-assured.version>5.3.0</rest-assured.version>
|
||||||
|
<spring-boot-starter-integration.version>2.7.5</spring-boot-starter-integration.version>
|
||||||
|
<spring-integration-test.version>5.5.14</spring-integration-test.version>
|
||||||
|
<camel-core.version>3.20.4</camel-core.version>
|
||||||
|
<camel-test-junit5.version>3.14.0</camel-test-junit5.version>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
</project>
|
|
@ -0,0 +1,12 @@
|
||||||
|
package com.baeldung.dtopattern;
|
||||||
|
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
|
||||||
|
@SpringBootApplication
|
||||||
|
public class Main {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
SpringApplication.run(Main.class, args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
package com.baeldung.dtopattern.api;
|
||||||
|
|
||||||
|
import com.baeldung.dtopattern.domain.Role;
|
||||||
|
import com.baeldung.dtopattern.domain.User;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static java.util.stream.Collectors.toList;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
class Mapper {
|
||||||
|
public UserDTO toDto(User user) {
|
||||||
|
String name = user.getName();
|
||||||
|
List<String> roles = user
|
||||||
|
.getRoles()
|
||||||
|
.stream()
|
||||||
|
.map(Role::getName)
|
||||||
|
.collect(toList());
|
||||||
|
|
||||||
|
return new UserDTO(name, roles);
|
||||||
|
}
|
||||||
|
|
||||||
|
public User toUser(UserCreationDTO userDTO) {
|
||||||
|
return new User(userDTO.getName(), userDTO.getPassword(), new ArrayList<>());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
package com.baeldung.dtopattern.api;
|
||||||
|
|
||||||
|
import com.baeldung.dtopattern.domain.RoleService;
|
||||||
|
import com.baeldung.dtopattern.domain.User;
|
||||||
|
import com.baeldung.dtopattern.domain.UserService;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static java.util.stream.Collectors.toList;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/users")
|
||||||
|
class UserController {
|
||||||
|
|
||||||
|
private UserService userService;
|
||||||
|
private RoleService roleService;
|
||||||
|
private Mapper mapper;
|
||||||
|
|
||||||
|
public UserController(UserService userService, RoleService roleService, Mapper mapper) {
|
||||||
|
this.userService = userService;
|
||||||
|
this.roleService = roleService;
|
||||||
|
this.mapper = mapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping
|
||||||
|
@ResponseBody
|
||||||
|
public List<UserDTO> getUsers() {
|
||||||
|
return userService.getAll()
|
||||||
|
.stream()
|
||||||
|
.map(mapper::toDto)
|
||||||
|
.collect(toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@PostMapping
|
||||||
|
@ResponseBody
|
||||||
|
public UserIdDTO create(@RequestBody UserCreationDTO userDTO) {
|
||||||
|
User user = mapper.toUser(userDTO);
|
||||||
|
|
||||||
|
userDTO.getRoles()
|
||||||
|
.stream()
|
||||||
|
.map(role -> roleService.getOrCreate(role))
|
||||||
|
.forEach(user::addRole);
|
||||||
|
|
||||||
|
userService.save(user);
|
||||||
|
|
||||||
|
return new UserIdDTO(user.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
package com.baeldung.dtopattern.api;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class UserCreationDTO {
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
private String password;
|
||||||
|
private List<String> roles;
|
||||||
|
|
||||||
|
UserCreationDTO() {}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPassword() {
|
||||||
|
return password;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getRoles() {
|
||||||
|
return roles;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setPassword(String password) {
|
||||||
|
this.password = password;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setRoles(List<String> roles) {
|
||||||
|
this.roles = roles;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
package com.baeldung.dtopattern.api;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class UserDTO {
|
||||||
|
private String name;
|
||||||
|
private List<String> roles;
|
||||||
|
|
||||||
|
public UserDTO(String name, List<String> roles) {
|
||||||
|
this.name = name;
|
||||||
|
this.roles = roles;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getRoles() {
|
||||||
|
return roles;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package com.baeldung.dtopattern.api;
|
||||||
|
|
||||||
|
public class UserIdDTO {
|
||||||
|
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
public UserIdDTO(String id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
package com.baeldung.dtopattern.domain;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
class InMemoryRepository implements UserRepository, RoleRepository {
|
||||||
|
|
||||||
|
private Map<String, User> users = new LinkedHashMap<>();
|
||||||
|
private Map<String, Role> roles = new LinkedHashMap<>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<User> getAll() {
|
||||||
|
return new ArrayList<>(users.values());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void save(User user) {
|
||||||
|
user.setId(UUID.randomUUID().toString());
|
||||||
|
users.put(user.getId(), user);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void save(Role role) {
|
||||||
|
role.setId(UUID.randomUUID().toString());
|
||||||
|
roles.put(role.getId(), role);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Role getRoleById(String id) {
|
||||||
|
return roles.get(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Role getRoleByName(String name) {
|
||||||
|
return roles.values()
|
||||||
|
.stream()
|
||||||
|
.filter(role -> role.getName().equalsIgnoreCase(name))
|
||||||
|
.findFirst()
|
||||||
|
.orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deleteAll() {
|
||||||
|
users.clear();
|
||||||
|
roles.clear();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
package com.baeldung.dtopattern.domain;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public class Role {
|
||||||
|
private String id;
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
public Role(String name) {
|
||||||
|
this.name = Objects.requireNonNull(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setId(String id) {
|
||||||
|
this.id = Objects.requireNonNull(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = Objects.requireNonNull(name);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
package com.baeldung.dtopattern.domain;
|
||||||
|
|
||||||
|
public interface RoleRepository {
|
||||||
|
Role getRoleById(String id);
|
||||||
|
Role getRoleByName(String name);
|
||||||
|
void save(Role role);
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
package com.baeldung.dtopattern.domain;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class RoleService {
|
||||||
|
|
||||||
|
private RoleRepository repository;
|
||||||
|
|
||||||
|
public RoleService(RoleRepository repository) {
|
||||||
|
this.repository = repository;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Role getOrCreate(String name) {
|
||||||
|
Role role = repository.getRoleByName(name);
|
||||||
|
|
||||||
|
if (role == null) {
|
||||||
|
role = new Role(name);
|
||||||
|
repository.save(role);
|
||||||
|
}
|
||||||
|
|
||||||
|
return role;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void save(Role role) {
|
||||||
|
Objects.requireNonNull(role);
|
||||||
|
repository.save(role);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,80 @@
|
||||||
|
package com.baeldung.dtopattern.domain;
|
||||||
|
|
||||||
|
import javax.crypto.*;
|
||||||
|
import javax.crypto.spec.SecretKeySpec;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.security.InvalidKeyException;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public class User {
|
||||||
|
|
||||||
|
private static SecretKeySpec KEY = initKey();
|
||||||
|
|
||||||
|
static SecretKeySpec initKey(){
|
||||||
|
try {
|
||||||
|
SecretKey secretKey = KeyGenerator.getInstance("AES").generateKey();
|
||||||
|
return new SecretKeySpec(secretKey.getEncoded(), "AES");
|
||||||
|
} catch (NoSuchAlgorithmException ex) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String id;
|
||||||
|
private String name;
|
||||||
|
private String password;
|
||||||
|
private List<Role> roles;
|
||||||
|
|
||||||
|
public User(String name, String password, List<Role> roles) {
|
||||||
|
this.name = Objects.requireNonNull(name);
|
||||||
|
this.password = this.encrypt(password);
|
||||||
|
this.roles = Objects.requireNonNull(roles);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPassword() {
|
||||||
|
return password;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPassword(String password) {
|
||||||
|
this.password = password;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addRole(Role role) {
|
||||||
|
roles.add(role);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Role> getRoles() {
|
||||||
|
return Collections.unmodifiableList(roles);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setId(String id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
String encrypt(String password) {
|
||||||
|
Objects.requireNonNull(password);
|
||||||
|
try {
|
||||||
|
Cipher cipher = Cipher.getInstance("AES");
|
||||||
|
cipher.init(Cipher.ENCRYPT_MODE, KEY);
|
||||||
|
final byte[] encryptedBytes = cipher.doFinal(password.getBytes(StandardCharsets.UTF_8));
|
||||||
|
return new String(encryptedBytes, StandardCharsets.UTF_8);
|
||||||
|
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException e) {
|
||||||
|
// do nothing
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
package com.baeldung.dtopattern.domain;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public interface UserRepository {
|
||||||
|
List<User> getAll();
|
||||||
|
void save(User user);
|
||||||
|
void deleteAll();
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
package com.baeldung.dtopattern.domain;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class UserService {
|
||||||
|
|
||||||
|
private UserRepository repository;
|
||||||
|
|
||||||
|
public UserService(UserRepository repository) {
|
||||||
|
this.repository = repository;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<User> getAll() {
|
||||||
|
return repository.getAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void save(User user) {
|
||||||
|
Objects.requireNonNull(user);
|
||||||
|
repository.save(user);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
package com.baeldung.mvc_mvp.mvc;
|
||||||
|
|
||||||
|
public class MvcMainClass {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
|
||||||
|
Product model = retrieveProductFromDatabase();
|
||||||
|
ProductView view = new ProductView();
|
||||||
|
model.setView(view);
|
||||||
|
model.showProduct();
|
||||||
|
|
||||||
|
ProductController controller = new ProductController(model);
|
||||||
|
controller.setName("SmartPhone");
|
||||||
|
model.showProduct();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Product retrieveProductFromDatabase() {
|
||||||
|
Product product = new Product();
|
||||||
|
product.setName("Mobile");
|
||||||
|
product.setDescription("New Brand");
|
||||||
|
product.setPrice(1000.0);
|
||||||
|
return product;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
package com.baeldung.mvc_mvp.mvc;
|
||||||
|
|
||||||
|
public class Product {
|
||||||
|
private String name;
|
||||||
|
private String description;
|
||||||
|
private Double price;
|
||||||
|
private ProductView view;
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDescription() {
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDescription(String description) {
|
||||||
|
this.description = description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Double getPrice() {
|
||||||
|
return price;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPrice(Double price) {
|
||||||
|
this.price = price;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ProductView getView() {
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setView(ProductView view) {
|
||||||
|
this.view = view;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void showProduct() {
|
||||||
|
view.printProductDetails(name, description, price);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
package com.baeldung.mvc_mvp.mvc;
|
||||||
|
|
||||||
|
public class ProductController {
|
||||||
|
private final Product product;
|
||||||
|
|
||||||
|
public ProductController(Product product) {
|
||||||
|
this.product = product;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return product.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
product.setName(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDescription() {
|
||||||
|
return product.getDescription();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDescription(String description) {
|
||||||
|
product.setDescription(description);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Double getPrice() {
|
||||||
|
return product.getPrice();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPrice(Double price) {
|
||||||
|
product.setPrice(price);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
package com.baeldung.mvc_mvp.mvc;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
public class ProductView {
|
||||||
|
private static Logger log = LoggerFactory.getLogger(ProductView.class);
|
||||||
|
|
||||||
|
public void printProductDetails(String name, String description, Double price) {
|
||||||
|
log.info("Product details:");
|
||||||
|
log.info("product Name: " + name);
|
||||||
|
log.info("product Description: " + description);
|
||||||
|
log.info("product price: " + price);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
package com.baeldung.mvc_mvp.mvp;
|
||||||
|
|
||||||
|
public class MvpMainClass {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
|
||||||
|
Product model = retrieveProductFromDatabase();
|
||||||
|
ProductView view = new ProductView();
|
||||||
|
ProductPresenter presenter = new ProductPresenter(model, view);
|
||||||
|
presenter.showProduct();
|
||||||
|
presenter.setName("SmartPhone");
|
||||||
|
presenter.showProduct();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Product retrieveProductFromDatabase() {
|
||||||
|
Product product = new Product();
|
||||||
|
product.setName("Mobile");
|
||||||
|
product.setDescription("New Brand");
|
||||||
|
product.setPrice(1000.0);
|
||||||
|
return product;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
package com.baeldung.mvc_mvp.mvp;
|
||||||
|
|
||||||
|
public class Product {
|
||||||
|
private String name;
|
||||||
|
private String description;
|
||||||
|
private Double price;
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDescription() {
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDescription(String description) {
|
||||||
|
this.description = description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Double getPrice() {
|
||||||
|
return price;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPrice(Double price) {
|
||||||
|
this.price = price;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
package com.baeldung.mvc_mvp.mvp;
|
||||||
|
|
||||||
|
public class ProductPresenter {
|
||||||
|
private final Product product;
|
||||||
|
private final ProductView view;
|
||||||
|
|
||||||
|
public ProductPresenter(Product product, ProductView view) {
|
||||||
|
this.product = product;
|
||||||
|
this.view = view;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return product.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
product.setName(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDescription() {
|
||||||
|
return product.getDescription();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDescription(String description) {
|
||||||
|
product.setDescription(description);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Double getProductPrice() {
|
||||||
|
return product.getPrice();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPrice(Double price) {
|
||||||
|
product.setPrice(price);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void showProduct() {
|
||||||
|
view.printProductDetails(product.getName(), product.getDescription(), product.getPrice());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
package com.baeldung.mvc_mvp.mvp;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
public class ProductView {
|
||||||
|
private static Logger log = LoggerFactory.getLogger(ProductView.class);
|
||||||
|
|
||||||
|
public void printProductDetails(String name, String description, Double price) {
|
||||||
|
log.info("Product details:");
|
||||||
|
log.info("product Name: " + name);
|
||||||
|
log.info("product Description: " + description);
|
||||||
|
log.info("product price: " + price);
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue