Source Code for hexagonal architecture article.
This commit is contained in:
parent
9e5b55889f
commit
91e50545f8
|
@ -0,0 +1,5 @@
|
|||
/target/
|
||||
.settings/
|
||||
.classpath
|
||||
.project
|
||||
/bin/
|
|
@ -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>
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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());
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -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> {
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
package com.baeldung.hexagonal.core.contract;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface UserService {
|
||||
|
||||
void addUser(User user);
|
||||
|
||||
List<User> getUsers();
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
server.port=8081
|
||||
server.contextPath=/hexagonaldesignapp
|
||||
logging.level.org.springframework.web: INFO
|
||||
|
||||
spring.jpa.hibernate.ddl-auto=update
|
|
@ -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>
|
|
@ -0,0 +1 @@
|
|||
insert into user(name) values ('ana');
|
|
@ -0,0 +1 @@
|
|||
create table user(id int identity primary key, name varchar(30));
|
|
@ -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")));
|
||||
}
|
||||
}
|
|
@ -17,6 +17,7 @@
|
|||
<module>front-controller</module>
|
||||
<module>intercepting-filter</module>
|
||||
<module>design-patterns</module>
|
||||
<module>hexagonal-design-pattern</module>
|
||||
</modules>
|
||||
|
||||
<dependencyManagement>
|
||||
|
|
Loading…
Reference in New Issue