Skip to content

Commit aae53fd

Browse files
committed
Inital project setup
- Applies hexagonal architecture design - Adds configuration for H2 in-memeory database & Liqudbase to manage schema
0 parents  commit aae53fd

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+1195
-0
lines changed

.gitattributes

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#
2+
# https://help.github.com/articles/dealing-with-line-endings/
3+
#
4+
# Linux start script should use lf
5+
/gradlew text eol=lf
6+
7+
# These are Windows script files and should use crlf
8+
*.bat text eol=crlf
9+

.gitignore

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# Ignore Gradle project-specific cache directory
2+
.gradle
3+
4+
# Ignore Gradle build output directory
5+
build
6+
7+
### IntelliJ IDEA ###
8+
.idea
9+
*.iws
10+
*.iml
11+
*.ipr
12+
out/
13+
!**/src/main/**/out/
14+
!**/src/test/**/out/
15+
16+
17+
### STS ###
18+
.apt_generated
19+
.classpath
20+
.factorypath
21+
.project
22+
.settings
23+
.springBeans
24+
.sts4-cache
25+
.java-version
26+
27+
### NetBeans ###
28+
/nbproject/private/
29+
/nbbuild/
30+
/dist/
31+
/nbdist/
32+
/.nb-gradle/
33+
/build/
34+
35+
### VS Code ###
36+
.vscode/
37+
38+
### DS_Store ###
39+
*.DS_Store

LICENSE

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2024 Bishal Thapa
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# hexagonal-architecture-catalog-service
2+
An example application about Hexagonal Architecture With Spring Boot.

build.gradle

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
/*
2+
* This file was generated by the Gradle 'init' task.
3+
*
4+
* This is a general purpose Gradle build.
5+
* To learn more about Gradle by exploring our Samples at https://docs.gradle.org/8.7/samples
6+
*/

catalog-service/build.gradle

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
plugins {
2+
id 'java'
3+
id 'org.springframework.boot' version '3.3.0'
4+
id 'io.spring.dependency-management' version '1.1.5'
5+
}
6+
7+
group = 'com.catalog-service'
8+
version = '0.0.1-SNAPSHOT'
9+
sourceCompatibility = '21'
10+
11+
java {
12+
toolchain {
13+
languageVersion = JavaLanguageVersion.of(21)
14+
}
15+
}
16+
17+
repositories {
18+
mavenCentral()
19+
}
20+
21+
dependencies {
22+
implementation 'org.springframework.boot:spring-boot-starter-actuator'
23+
implementation 'org.springframework.boot:spring-boot-starter-data-jdbc'
24+
implementation 'org.springframework.boot:spring-boot-starter-web'
25+
26+
implementation 'com.h2database:h2'
27+
implementation 'org.liquibase:liquibase-core'
28+
29+
runtimeOnly 'io.micrometer:micrometer-registry-prometheus'
30+
testImplementation 'com.tngtech.archunit:archunit:1.3.0'
31+
testImplementation 'org.springframework.boot:spring-boot-starter-test'
32+
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
33+
}
34+
35+
tasks.named('test') {
36+
useJUnitPlatform()
37+
}

catalog-service/h2/app.db.mv.db

24 KB
Binary file not shown.

catalog-service/h2/app.db.trace.db

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
2024-06-10 23:54:45.778000+01:00 jdbc[3]: exception
2+
org.h2.jdbc.JdbcSQLSyntaxErrorException: Table "databasechangeloglock" not found; SQL statement:
3+
SELECT COUNT(*) FROM public.DATABASECHANGELOGLOCK [42102-224]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package com.catalogservice;
2+
3+
import com.catalogservice.config.UseCase;
4+
import com.catalogservice.config.Adaptor;
5+
import org.springframework.boot.SpringApplication;
6+
import org.springframework.boot.autoconfigure.SpringBootApplication;
7+
import org.springframework.context.annotation.ComponentScan;
8+
import org.springframework.context.annotation.FilterType;
9+
10+
@SpringBootApplication
11+
@ComponentScan(
12+
basePackages = "com.observability.catalogservice",
13+
includeFilters = {
14+
@ComponentScan.Filter(type = FilterType.ANNOTATION, value = Adaptor.class),
15+
@ComponentScan.Filter(type = FilterType.ANNOTATION, value = UseCase.class)
16+
}
17+
)
18+
public class CatalogServiceApplication {
19+
20+
public static void main(String[] args) {
21+
SpringApplication.run(CatalogServiceApplication.class, args);
22+
}
23+
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package com.catalogservice.adapter.api;
2+
3+
import com.catalogservice.adapter.api.model.CatalogDto;
4+
import com.catalogservice.adapter.api.model.SaveCatalogDto;
5+
import org.springframework.http.HttpStatus;
6+
import org.springframework.http.ResponseEntity;
7+
import org.springframework.web.bind.annotation.DeleteMapping;
8+
import org.springframework.web.bind.annotation.GetMapping;
9+
import org.springframework.web.bind.annotation.PathVariable;
10+
import org.springframework.web.bind.annotation.PostMapping;
11+
import org.springframework.web.bind.annotation.PutMapping;
12+
import org.springframework.web.bind.annotation.RequestBody;
13+
import org.springframework.web.bind.annotation.RequestMapping;
14+
import org.springframework.web.bind.annotation.RestController;
15+
16+
@RestController
17+
@RequestMapping(value = "/v1")
18+
public class CatalogController {
19+
20+
private final UpdateCatalogAdaptor catalogUpdateAdaptor;
21+
private final FindCatalogAdapter findCatalogAdapter;
22+
23+
public CatalogController(UpdateCatalogAdaptor catalogUpdateAdaptor, FindCatalogAdapter findCatalogAdapter) {
24+
this.catalogUpdateAdaptor = catalogUpdateAdaptor;
25+
this.findCatalogAdapter = findCatalogAdapter;
26+
}
27+
28+
@PostMapping("/catalog")
29+
public ResponseEntity<CatalogDto> saveCatalog(@RequestBody SaveCatalogDto saveCatalogDto) {
30+
CatalogDto createdCatalog = catalogUpdateAdaptor.save(saveCatalogDto.catalogDto());
31+
return ResponseEntity.status(HttpStatus.CREATED).body(createdCatalog);
32+
}
33+
34+
@PutMapping("/catalog/{catalogId}")
35+
public ResponseEntity<CatalogDto> updateCatalog(@PathVariable Long catalogId, @RequestBody SaveCatalogDto catalogDto) {
36+
CatalogDto updateCatalog = catalogUpdateAdaptor.update(catalogId, catalogDto.catalogDto());
37+
return ResponseEntity.status(HttpStatus.OK).body(updateCatalog);
38+
}
39+
40+
@GetMapping("/catalog/{catalogId}")
41+
public ResponseEntity<CatalogDto> getCatalog(@PathVariable Long catalogId) {
42+
CatalogDto catalogDto = findCatalogAdapter.findById(catalogId);
43+
return ResponseEntity.status(HttpStatus.OK).body(catalogDto);
44+
}
45+
46+
@DeleteMapping("/catalog/{catalogId}")
47+
public ResponseEntity<Void> deleteCatalog(@PathVariable Long catalogId) {
48+
return ResponseEntity.noContent().build();
49+
}
50+
51+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package com.catalogservice.adapter.api;
2+
3+
import com.catalogservice.adapter.api.model.CatalogDto;
4+
import com.catalogservice.application.port.api.FindCatalogPort;
5+
import com.catalogservice.application.usecase.FindCatalogUseCase;
6+
import com.catalogservice.application.domain.Catalog;
7+
import com.catalogservice.application.domain.Type;
8+
import com.catalogservice.config.Adaptor;
9+
import java.util.List;
10+
import java.util.stream.Collectors;
11+
12+
@Adaptor
13+
public class FindCatalogAdapter implements FindCatalogPort {
14+
15+
private final FindCatalogUseCase findCatalogUseCase;
16+
17+
public FindCatalogAdapter(FindCatalogUseCase findCatalogUseCase) {
18+
this.findCatalogUseCase = findCatalogUseCase;
19+
}
20+
21+
@Override
22+
public List<CatalogDto> findAll() {
23+
return findCatalogUseCase.findAll()
24+
.stream()
25+
.map(CatalogDto::toCatalogDto)
26+
.collect(Collectors.toList());
27+
}
28+
29+
@Override
30+
public CatalogDto findById(Long id) {
31+
Catalog catalog = this.findCatalogUseCase.findById(id);
32+
return CatalogDto.toCatalogDto(catalog);
33+
}
34+
35+
@Override
36+
public List<CatalogDto> findByType(Type type) {
37+
// Todo
38+
return null;
39+
}
40+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package com.catalogservice.adapter.api;
2+
3+
import com.catalogservice.adapter.api.model.CatalogDto;
4+
import com.catalogservice.application.domain.Catalog;
5+
import com.catalogservice.application.port.api.UpdateCatalogPort;
6+
import com.catalogservice.application.usecase.AddNewCatalogUseCase;
7+
import com.catalogservice.application.usecase.DeleteCatalogUseCase;
8+
import com.catalogservice.config.Adaptor;
9+
import com.catalogservice.application.usecase.UpdateCatalogUseCase;
10+
11+
@Adaptor
12+
public class UpdateCatalogAdaptor implements UpdateCatalogPort {
13+
14+
private final AddNewCatalogUseCase addNewCatalogUseCase;
15+
private final UpdateCatalogUseCase updateCatalogUseCase;
16+
private final DeleteCatalogUseCase deleteCatalogUseCase;
17+
18+
public UpdateCatalogAdaptor(AddNewCatalogUseCase addNewCatalogUseCase, UpdateCatalogUseCase updateCatalogUseCase,
19+
DeleteCatalogUseCase deleteCatalogUseCase) {
20+
this.addNewCatalogUseCase = addNewCatalogUseCase;
21+
this.updateCatalogUseCase = updateCatalogUseCase;
22+
this.deleteCatalogUseCase = deleteCatalogUseCase;
23+
}
24+
25+
@Override
26+
public CatalogDto save(CatalogDto catalogDto) {
27+
Catalog savedCatalog = addNewCatalogUseCase.save(catalogDto.tranformToCatalog());
28+
return CatalogDto.toCatalogDto(savedCatalog);
29+
}
30+
31+
@Override
32+
public CatalogDto update(Long catalogId, CatalogDto updatedCatalogDto) {
33+
Catalog updatedCatalog = this.updateCatalogUseCase.update(catalogId, updatedCatalogDto.tranformToCatalog());
34+
return CatalogDto.toCatalogDto(updatedCatalog);
35+
}
36+
37+
@Override
38+
public void delete(Long catalogId) {
39+
deleteCatalogUseCase.deleteById(catalogId);
40+
}
41+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package com.catalogservice.adapter.api.model;
2+
3+
import com.catalogservice.application.domain.Catalog;
4+
import com.catalogservice.application.domain.Catalog.CatalogBuilder;
5+
import com.catalogservice.application.domain.Price;
6+
import com.catalogservice.application.domain.Type;
7+
import java.math.BigDecimal;
8+
import java.util.Currency;
9+
10+
public record CatalogDto(Long id, String name, BigDecimal price, String currencyCode, String type) {
11+
12+
public Catalog tranformToCatalog() {
13+
return CatalogBuilder.aCatalog()
14+
.withName(this.name())
15+
.withPrice(new Price(this.price(), Currency.getInstance(this.currencyCode())))
16+
.withType(Type.valueOf(this.type()))
17+
.build();
18+
19+
}
20+
21+
public static CatalogDto toCatalogDto(Catalog catalog) {
22+
return new CatalogDto(catalog.id(),
23+
catalog.name(),
24+
catalog.price().value(),
25+
catalog.price().currency().getCurrencyCode(),
26+
catalog.type().name());
27+
}
28+
29+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package com.catalogservice.adapter.api.model;
2+
3+
import java.math.BigDecimal;
4+
5+
public record SaveCatalogDto(String name, BigDecimal price, String currencyCode, String type) {
6+
7+
public CatalogDto catalogDto() {
8+
return new CatalogDto(0l, name, price, currencyCode, type);
9+
}
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package com.catalogservice.adapter.persistence;
2+
3+
import com.catalogservice.adapter.persistence.model.CatalogData;
4+
import org.springframework.data.repository.CrudRepository;
5+
import org.springframework.stereotype.Repository;
6+
7+
@Repository
8+
public interface CatalogRepository extends CrudRepository<CatalogData, Long> {
9+
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package com.catalogservice.adapter.persistence;
2+
3+
import com.catalogservice.adapter.persistence.model.CatalogData;
4+
import com.catalogservice.application.port.persistence.ReadCatalogPort;
5+
import com.catalogservice.application.domain.Catalog;
6+
import com.catalogservice.config.Adaptor;
7+
import java.util.ArrayList;
8+
import java.util.List;
9+
import java.util.Optional;
10+
import org.slf4j.Logger;
11+
import org.slf4j.LoggerFactory;
12+
13+
@Adaptor
14+
public class ReadCatalogAdapter implements ReadCatalogPort {
15+
16+
private static final Logger LOGGER = LoggerFactory.getLogger(ReadCatalogAdapter.class);
17+
18+
private final CatalogRepository catalogRepository;
19+
20+
public ReadCatalogAdapter(CatalogRepository catalogRepository) {
21+
this.catalogRepository = catalogRepository;
22+
}
23+
24+
@Override
25+
public boolean existCatalogById(Long catalogId) {
26+
return this.catalogRepository.existsById(catalogId);
27+
}
28+
29+
@Override
30+
public Optional<Catalog> fetchById(Long catalogId) {
31+
Optional<CatalogData> catalogDataOptional = this.catalogRepository.findById(catalogId);
32+
LOGGER.info("Catalog fetched from database: {}", catalogDataOptional);
33+
return catalogDataOptional.map(CatalogData::mapToCatalog);
34+
}
35+
36+
@Override
37+
public List<Catalog> fetchAll() {
38+
List<Catalog> catalogList = new ArrayList<>();
39+
for (CatalogData catalogData : this.catalogRepository.findAll()) {
40+
catalogList.add(catalogData.mapToCatalog());
41+
}
42+
return catalogList;
43+
}
44+
}

0 commit comments

Comments
 (0)