Source Code for hexagonal architecture article.

This commit is contained in:
caroline 2019-01-14 19:56:19 +01:00
parent 9e5b55889f
commit 91e50545f8
21 changed files with 491 additions and 0 deletions

View File

@ -0,0 +1,5 @@
/target/
.settings/
.classpath
.project
/bin/

View File

@ -0,0 +1,80 @@
<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>
<groupId>com.baeldung</groupId>
<artifactId>hexagonal-design-pattern</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>hexagonal-design-pattern</name>
<packaging>war</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.3.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>ma.glasnost.orika</groupId>
<artifactId>orika-core</artifactId>
<version>1.4.6</version>
</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>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<configuration>
<warSourceDirectory>WebContent</warSourceDirectory>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
<configuration>
<mainClass>com.baeldung.hexagonal.Application</mainClass>
<outputDirectory>${project.basedir}/docker</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<properties>
<tomcat.version>8.0.43</tomcat.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
</properties>
</project>

View File

@ -0,0 +1,13 @@
package com.baeldung.hexagonal;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

View File

@ -0,0 +1,31 @@
package com.baeldung.hexagonal.adapter.controller;
import java.util.List;
import com.baeldung.hexagonal.core.contract.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.http.HttpStatus;
import com.baeldung.hexagonal.core.contract.User;
@RestController
public class UserController {
@Autowired
private UserService userService;
@PostMapping("/users")
@ResponseStatus(HttpStatus.CREATED)
public void addUser(@RequestBody User user) {
userService.addUser(user);
}
@GetMapping("/users")
public List<User> getUsers() {
return userService.getUsers();
}
}

View File

@ -0,0 +1,11 @@
package com.baeldung.hexagonal.adapter.data;
import com.baeldung.hexagonal.core.contract.dto.UserDTO;
import java.util.List;
public interface UserDataAdapter {
void addUser(UserDTO user);
List<UserDTO> getUsers();
}

View File

@ -0,0 +1,27 @@
package com.baeldung.hexagonal.adapter.data;
import com.baeldung.hexagonal.adapter.data.entity.UserEntity;
import com.baeldung.hexagonal.adapter.data.mapper.ModelMapper;
import com.baeldung.hexagonal.adapter.data.repository.UserRepository;
import com.baeldung.hexagonal.core.contract.dto.UserDTO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
@Component
public class UserDataAdapterImpl implements UserDataAdapter {
@Autowired
private UserRepository userRepository;
@Autowired
private ModelMapper modelMapper;
public void addUser(UserDTO user) {
userRepository.save(modelMapper.map(user, UserEntity.class));
}
public List<UserDTO> getUsers() {
return modelMapper.mapAsList(userRepository.findAll(), UserDTO.class);
}
}

View File

@ -0,0 +1,39 @@
package com.baeldung.hexagonal.adapter.data.entity;
import javax.persistence.*;
@Entity(name = "user")
@Table(name = "user")
public class UserEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String name;
public UserEntity() {
}
public UserEntity(long id, String name) {
super();
this.id = id;
this.name = name;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

View File

@ -0,0 +1,41 @@
package com.baeldung.hexagonal.adapter.data.mapper;
import com.baeldung.hexagonal.adapter.data.entity.UserEntity;
import com.baeldung.hexagonal.core.contract.dto.UserDTO;
import ma.glasnost.orika.CustomMapper;
import ma.glasnost.orika.MapperFactory;
import ma.glasnost.orika.MappingContext;
import ma.glasnost.orika.impl.ConfigurableMapper;
import org.springframework.stereotype.Component;
@Component
public class ModelMapper extends ConfigurableMapper {
public void configure(MapperFactory factory) {
super.configure(factory);
factory.registerClassMap(factory.classMap(UserDTO.class, UserEntity.class)
.customize(new CustomMapper<UserDTO, UserEntity>() {
@Override
public void mapAtoB(UserDTO userDTO, UserEntity userEntity, MappingContext context) {
userEntity.setId(userDTO.getId());
userEntity.setName(userDTO.getName());
}
})
.byDefault()
.toClassMap());
factory.registerClassMap(factory.classMap(UserEntity.class, UserDTO.class)
.customize(new CustomMapper<UserEntity, UserDTO>() {
@Override
public void mapAtoB(UserEntity userEntity, UserDTO userDTO, MappingContext context) {
userDTO.setId(userEntity.getId());
userDTO.setName(userEntity.getName());
}
})
.byDefault()
.toClassMap());
}
}

View File

@ -0,0 +1,8 @@
package com.baeldung.hexagonal.adapter.data.repository;
import com.baeldung.hexagonal.adapter.data.entity.UserEntity;
import org.springframework.data.jpa.repository.JpaRepository;
public interface UserRepository extends JpaRepository<UserEntity, Long> {
}

View File

@ -0,0 +1,24 @@
package com.baeldung.hexagonal.config;
import javax.sql.DataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
@Configuration
public class PersistenceConfig {
@Bean
public DataSource dataSource() {
EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
EmbeddedDatabase db = builder.setType(EmbeddedDatabaseType.H2)
.addScript("mySchema.sql")
.addScript("myData.sql")
.build();
return db;
}
}

View File

@ -0,0 +1,34 @@
package com.baeldung.hexagonal.core.contract;
public class User {
private long id;
private String name;
public User() {
}
public User(long id, String name) {
super();
this.id = id;
this.name = name;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

View File

@ -0,0 +1,10 @@
package com.baeldung.hexagonal.core.contract;
import java.util.List;
public interface UserService {
void addUser(User user);
List<User> getUsers();
}

View File

@ -0,0 +1,24 @@
package com.baeldung.hexagonal.core.contract.dto;
public class UserDTO {
private long id;
private String name;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

View File

@ -0,0 +1,41 @@
package com.baeldung.hexagonal.core.mapper;
import com.baeldung.hexagonal.core.contract.dto.UserDTO;
import com.baeldung.hexagonal.core.contract.User;
import ma.glasnost.orika.CustomMapper;
import ma.glasnost.orika.MapperFactory;
import ma.glasnost.orika.MappingContext;
import ma.glasnost.orika.impl.ConfigurableMapper;
import org.springframework.stereotype.Component;
@Component
public class CoreModelMapper extends ConfigurableMapper {
public void configure(MapperFactory factory) {
super.configure(factory);
factory.registerClassMap(factory.classMap(UserDTO.class, User.class)
.customize(new CustomMapper<UserDTO, User>() {
@Override
public void mapAtoB(UserDTO userDTO, User user, MappingContext context) {
user.setId(userDTO.getId());
user.setName(userDTO.getName());
}
})
.byDefault()
.toClassMap());
factory.registerClassMap(factory.classMap(User.class, UserDTO.class)
.customize(new CustomMapper<User, UserDTO>() {
@Override
public void mapAtoB(User user, UserDTO userDTO, MappingContext context) {
userDTO.setId(user.getId());
userDTO.setName(user.getName());
}
})
.byDefault()
.toClassMap());
}
}

View File

@ -0,0 +1,28 @@
package com.baeldung.hexagonal.core.service;
import com.baeldung.hexagonal.adapter.data.UserDataAdapter;
import com.baeldung.hexagonal.core.contract.UserService;
import com.baeldung.hexagonal.core.contract.dto.UserDTO;
import com.baeldung.hexagonal.core.mapper.CoreModelMapper;
import com.baeldung.hexagonal.core.contract.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserDataAdapter userDataAdapter;
@Autowired
private CoreModelMapper coreModelMapper;
public void addUser(User user) {
userDataAdapter.addUser(coreModelMapper.map(user, UserDTO.class));
}
public List<User> getUsers() {
return coreModelMapper.mapAsList(userDataAdapter.getUsers(), User.class);
}
}

View File

@ -0,0 +1,5 @@
server.port=8081
server.contextPath=/hexagonaldesignapp
logging.level.org.springframework.web: INFO
spring.jpa.hibernate.ddl-auto=update

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="STDOUT" />
</root>
</configuration>

View File

@ -0,0 +1 @@
insert into user(name) values ('ana');

View File

@ -0,0 +1 @@
create table user(id int identity primary key, name varchar(30));

View File

@ -0,0 +1,54 @@
package com.baeldung.hexagonal.test;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import static org.hamcrest.Matchers.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
import com.baeldung.hexagonal.Application;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class)
@WebAppConfiguration
public class UserControllerTest {
private static final String CONTENT_TYPE = "application/json;charset=UTF-8";
private MockMvc mockMvc;
@Autowired
private WebApplicationContext webApplicationContext;
@Before
public void setup() throws Exception {
this.mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext)
.build();
}
@Test
public void whenCreateGetUser_thenOk() throws Exception {
String userJson = "{\"name\":\"john\"}";
this.mockMvc.perform(post("/users").contentType(CONTENT_TYPE)
.content(userJson))
.andExpect(status().isCreated());
this.mockMvc.perform(get("/users"))
.andExpect(status().isOk())
.andExpect(content().contentType(CONTENT_TYPE))
.andExpect(jsonPath("$", hasSize(2)))
.andExpect(jsonPath("$[0].name", is("ana")))
.andExpect(jsonPath("$[1].name", is("john")));
}
}

View File

@ -17,6 +17,7 @@
<module>front-controller</module> <module>front-controller</module>
<module>intercepting-filter</module> <module>intercepting-filter</module>
<module>design-patterns</module> <module>design-patterns</module>
<module>hexagonal-design-pattern</module>
</modules> </modules>
<dependencyManagement> <dependencyManagement>