basic ha example
This commit is contained in:
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
### IntelliJ IDEA ###
|
||||||
|
.idea
|
||||||
|
*.iws
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
24
build.gradle
Normal file
24
build.gradle
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
plugins {
|
||||||
|
id 'org.springframework.boot' version '2.2.2.RELEASE'
|
||||||
|
id 'io.spring.dependency-management' version '1.0.8.RELEASE'
|
||||||
|
id 'java'
|
||||||
|
}
|
||||||
|
|
||||||
|
group = 'tech.allegro'
|
||||||
|
version = '0.0.1-SNAPSHOT'
|
||||||
|
sourceCompatibility = '11'
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation 'org.springframework.boot:spring-boot-starter-web'
|
||||||
|
testImplementation('org.springframework.boot:spring-boot-starter-test') {
|
||||||
|
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test {
|
||||||
|
useJUnitPlatform()
|
||||||
|
}
|
||||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
Binary file not shown.
5
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
5
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
distributionBase=GRADLE_USER_HOME
|
||||||
|
distributionPath=wrapper/dists
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-6.0.1-bin.zip
|
||||||
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
zipStorePath=wrapper/dists
|
||||||
172
gradlew
vendored
Executable file
172
gradlew
vendored
Executable file
@@ -0,0 +1,172 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
##
|
||||||
|
## Gradle start up script for UN*X
|
||||||
|
##
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
# Attempt to set APP_HOME
|
||||||
|
# Resolve links: $0 may be a link
|
||||||
|
PRG="$0"
|
||||||
|
# Need this for relative symlinks.
|
||||||
|
while [ -h "$PRG" ] ; do
|
||||||
|
ls=`ls -ld "$PRG"`
|
||||||
|
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||||
|
if expr "$link" : '/.*' > /dev/null; then
|
||||||
|
PRG="$link"
|
||||||
|
else
|
||||||
|
PRG=`dirname "$PRG"`"/$link"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
SAVED="`pwd`"
|
||||||
|
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||||
|
APP_HOME="`pwd -P`"
|
||||||
|
cd "$SAVED" >/dev/null
|
||||||
|
|
||||||
|
APP_NAME="Gradle"
|
||||||
|
APP_BASE_NAME=`basename "$0"`
|
||||||
|
|
||||||
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
DEFAULT_JVM_OPTS=""
|
||||||
|
|
||||||
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
|
MAX_FD="maximum"
|
||||||
|
|
||||||
|
warn () {
|
||||||
|
echo "$*"
|
||||||
|
}
|
||||||
|
|
||||||
|
die () {
|
||||||
|
echo
|
||||||
|
echo "$*"
|
||||||
|
echo
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# OS specific support (must be 'true' or 'false').
|
||||||
|
cygwin=false
|
||||||
|
msys=false
|
||||||
|
darwin=false
|
||||||
|
nonstop=false
|
||||||
|
case "`uname`" in
|
||||||
|
CYGWIN* )
|
||||||
|
cygwin=true
|
||||||
|
;;
|
||||||
|
Darwin* )
|
||||||
|
darwin=true
|
||||||
|
;;
|
||||||
|
MINGW* )
|
||||||
|
msys=true
|
||||||
|
;;
|
||||||
|
NONSTOP* )
|
||||||
|
nonstop=true
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||||
|
|
||||||
|
# Determine the Java command to use to start the JVM.
|
||||||
|
if [ -n "$JAVA_HOME" ] ; then
|
||||||
|
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||||
|
# IBM's JDK on AIX uses strange locations for the executables
|
||||||
|
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||||
|
else
|
||||||
|
JAVACMD="$JAVA_HOME/bin/java"
|
||||||
|
fi
|
||||||
|
if [ ! -x "$JAVACMD" ] ; then
|
||||||
|
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
JAVACMD="java"
|
||||||
|
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Increase the maximum file descriptors if we can.
|
||||||
|
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||||
|
MAX_FD_LIMIT=`ulimit -H -n`
|
||||||
|
if [ $? -eq 0 ] ; then
|
||||||
|
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||||
|
MAX_FD="$MAX_FD_LIMIT"
|
||||||
|
fi
|
||||||
|
ulimit -n $MAX_FD
|
||||||
|
if [ $? -ne 0 ] ; then
|
||||||
|
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For Darwin, add options to specify how the application appears in the dock
|
||||||
|
if $darwin; then
|
||||||
|
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For Cygwin, switch paths to Windows format before running java
|
||||||
|
if $cygwin ; then
|
||||||
|
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||||
|
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||||
|
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||||
|
|
||||||
|
# We build the pattern for arguments to be converted via cygpath
|
||||||
|
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||||
|
SEP=""
|
||||||
|
for dir in $ROOTDIRSRAW ; do
|
||||||
|
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||||
|
SEP="|"
|
||||||
|
done
|
||||||
|
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||||
|
# Add a user-defined pattern to the cygpath arguments
|
||||||
|
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||||
|
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||||
|
fi
|
||||||
|
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||||
|
i=0
|
||||||
|
for arg in "$@" ; do
|
||||||
|
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||||
|
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||||
|
|
||||||
|
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||||
|
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||||
|
else
|
||||||
|
eval `echo args$i`="\"$arg\""
|
||||||
|
fi
|
||||||
|
i=$((i+1))
|
||||||
|
done
|
||||||
|
case $i in
|
||||||
|
(0) set -- ;;
|
||||||
|
(1) set -- "$args0" ;;
|
||||||
|
(2) set -- "$args0" "$args1" ;;
|
||||||
|
(3) set -- "$args0" "$args1" "$args2" ;;
|
||||||
|
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||||
|
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||||
|
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||||
|
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||||
|
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||||
|
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Escape application args
|
||||||
|
save () {
|
||||||
|
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||||
|
echo " "
|
||||||
|
}
|
||||||
|
APP_ARGS=$(save "$@")
|
||||||
|
|
||||||
|
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||||
|
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||||
|
|
||||||
|
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
|
||||||
|
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
|
||||||
|
cd "$(dirname "$0")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
exec "$JAVACMD" "$@"
|
||||||
84
gradlew.bat
vendored
Normal file
84
gradlew.bat
vendored
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
@if "%DEBUG%" == "" @echo off
|
||||||
|
@rem ##########################################################################
|
||||||
|
@rem
|
||||||
|
@rem Gradle startup script for Windows
|
||||||
|
@rem
|
||||||
|
@rem ##########################################################################
|
||||||
|
|
||||||
|
@rem Set local scope for the variables with windows NT shell
|
||||||
|
if "%OS%"=="Windows_NT" setlocal
|
||||||
|
|
||||||
|
set DIRNAME=%~dp0
|
||||||
|
if "%DIRNAME%" == "" set DIRNAME=.
|
||||||
|
set APP_BASE_NAME=%~n0
|
||||||
|
set APP_HOME=%DIRNAME%
|
||||||
|
|
||||||
|
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
set DEFAULT_JVM_OPTS=
|
||||||
|
|
||||||
|
@rem Find java.exe
|
||||||
|
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||||
|
|
||||||
|
set JAVA_EXE=java.exe
|
||||||
|
%JAVA_EXE% -version >NUL 2>&1
|
||||||
|
if "%ERRORLEVEL%" == "0" goto init
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
echo.
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
echo location of your Java installation.
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:findJavaFromJavaHome
|
||||||
|
set JAVA_HOME=%JAVA_HOME:"=%
|
||||||
|
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||||
|
|
||||||
|
if exist "%JAVA_EXE%" goto init
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||||
|
echo.
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
echo location of your Java installation.
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:init
|
||||||
|
@rem Get command-line arguments, handling Windows variants
|
||||||
|
|
||||||
|
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||||
|
|
||||||
|
:win9xME_args
|
||||||
|
@rem Slurp the command line arguments.
|
||||||
|
set CMD_LINE_ARGS=
|
||||||
|
set _SKIP=2
|
||||||
|
|
||||||
|
:win9xME_args_slurp
|
||||||
|
if "x%~1" == "x" goto execute
|
||||||
|
|
||||||
|
set CMD_LINE_ARGS=%*
|
||||||
|
|
||||||
|
:execute
|
||||||
|
@rem Setup the command line
|
||||||
|
|
||||||
|
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||||
|
|
||||||
|
@rem Execute Gradle
|
||||||
|
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||||
|
|
||||||
|
:end
|
||||||
|
@rem End local scope for the variables with windows NT shell
|
||||||
|
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||||
|
|
||||||
|
:fail
|
||||||
|
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||||
|
rem the _cmd.exe /c_ return code!
|
||||||
|
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||||
|
exit /b 1
|
||||||
|
|
||||||
|
:mainEnd
|
||||||
|
if "%OS%"=="Windows_NT" endlocal
|
||||||
|
|
||||||
|
:omega
|
||||||
1
settings.gradle
Normal file
1
settings.gradle
Normal file
@@ -0,0 +1 @@
|
|||||||
|
rootProject.name = 'hexagonal-architecture-by-example'
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package tech.allegro.hexagon;
|
||||||
|
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
|
||||||
|
@SpringBootApplication
|
||||||
|
public class HexagonalArchitectureExampleApplication {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
SpringApplication.run(HexagonalArchitectureExampleApplication.class, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
package tech.allegro.hexagon.articles.adapters.api;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("articles")
|
||||||
|
class ArticleController {
|
||||||
|
|
||||||
|
private final Logger log = LoggerFactory.getLogger(ArticleController.class);
|
||||||
|
|
||||||
|
private final ArticleService service;
|
||||||
|
|
||||||
|
ArticleController(final ArticleService service) {
|
||||||
|
this.service = service;
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("{articleId}")
|
||||||
|
ArticleResponse get(@PathVariable("articleId") final String articleId) {
|
||||||
|
log.info(">>> HTTP GET Request: retrieve an article with id: \"{}\"", articleId);
|
||||||
|
final ArticleResponse articleResponse = service.get(articleId);
|
||||||
|
log.info("<<< HTTP GET Response: article: \"{}\", successfully retrieved", articleResponse.title());
|
||||||
|
return articleResponse;
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping
|
||||||
|
ArticleIdResponse create(@RequestBody final ArticleRequest articleRequest) {
|
||||||
|
log.info(">>> HTTP POST Request: create an article: \"{}\"", articleRequest.title().value());
|
||||||
|
final ArticleIdResponse articleIdResponse = service.create(articleRequest);
|
||||||
|
log.info("<<< HTTP POST Response: article with id: \"{}\", successfully created", articleIdResponse.id());
|
||||||
|
return articleIdResponse;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package tech.allegro.hexagon.articles.adapters.api;
|
||||||
|
|
||||||
|
import tech.allegro.hexagon.articles.domain.model.ArticleId;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
|
||||||
|
class ArticleIdResponse {
|
||||||
|
private final String id;
|
||||||
|
|
||||||
|
private ArticleIdResponse(final String id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonProperty("id")
|
||||||
|
public String id() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ArticleIdResponse of(final ArticleId articleId) {
|
||||||
|
return new ArticleIdResponse(articleId.value());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package tech.allegro.hexagon.articles.adapters.api;
|
||||||
|
|
||||||
|
import tech.allegro.hexagon.articles.domain.model.AuthorId;
|
||||||
|
import tech.allegro.hexagon.articles.domain.model.Content;
|
||||||
|
import tech.allegro.hexagon.articles.domain.model.Title;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
|
||||||
|
class ArticleRequest {
|
||||||
|
private final String title;
|
||||||
|
private final String content;
|
||||||
|
private final String authorId;
|
||||||
|
|
||||||
|
ArticleRequest(@JsonProperty("title") final String title, @JsonProperty("content") final String content, @JsonProperty("authorId") final String authorId) {
|
||||||
|
this.title = title;
|
||||||
|
this.content = content;
|
||||||
|
this.authorId = authorId;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Title title() {
|
||||||
|
return Title.of(title);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Content content() {
|
||||||
|
return Content.of(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AuthorId authorId() {
|
||||||
|
return AuthorId.of(authorId);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
package tech.allegro.hexagon.articles.adapters.api;
|
||||||
|
|
||||||
|
import tech.allegro.hexagon.articles.domain.model.Article;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
|
||||||
|
class ArticleResponse {
|
||||||
|
private final String id;
|
||||||
|
private final String title;
|
||||||
|
private final String content;
|
||||||
|
private final String authorName;
|
||||||
|
|
||||||
|
private ArticleResponse(final String id, final String title, final String content, final String authorName) {
|
||||||
|
this.id = id;
|
||||||
|
this.title = title;
|
||||||
|
this.content = content;
|
||||||
|
this.authorName = authorName;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ArticleResponse of(final Article article) {
|
||||||
|
return new ArticleResponse(article.id().value(),
|
||||||
|
article.title().value(),
|
||||||
|
article.content().value(),
|
||||||
|
article.author().name().value());
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonProperty("id")
|
||||||
|
public String id() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonProperty("title")
|
||||||
|
public String title() {
|
||||||
|
return title;
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonProperty("content")
|
||||||
|
public String content() {
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonProperty("authorName")
|
||||||
|
public String authorName() {
|
||||||
|
return authorName;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package tech.allegro.hexagon.articles.adapters.api;
|
||||||
|
|
||||||
|
import tech.allegro.hexagon.articles.domain.ArticleFacade;
|
||||||
|
import tech.allegro.hexagon.articles.domain.model.ArticleId;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
class ArticleService {
|
||||||
|
|
||||||
|
private final ArticleFacade articleFacade;
|
||||||
|
|
||||||
|
ArticleService(final ArticleFacade articleFacade) {
|
||||||
|
this.articleFacade = articleFacade;
|
||||||
|
}
|
||||||
|
|
||||||
|
ArticleResponse get(final String articleId) {
|
||||||
|
return ArticleResponse.of(articleFacade.get(ArticleId.of(articleId)));
|
||||||
|
}
|
||||||
|
|
||||||
|
ArticleIdResponse create(final ArticleRequest articleRequest) {
|
||||||
|
final ArticleId articleId = articleFacade.create(articleRequest.authorId(), articleRequest.title(), articleRequest.content());
|
||||||
|
return ArticleIdResponse.of(articleId);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package tech.allegro.hexagon.articles.adapters.authorservice;
|
||||||
|
|
||||||
|
class AuthorExternalModel {
|
||||||
|
/**
|
||||||
|
* External author service model implementation comes here
|
||||||
|
*/
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package tech.allegro.hexagon.articles.adapters.authorservice;
|
||||||
|
|
||||||
|
import tech.allegro.hexagon.articles.domain.model.Author;
|
||||||
|
import tech.allegro.hexagon.articles.domain.model.AuthorId;
|
||||||
|
import tech.allegro.hexagon.articles.domain.model.PersonName;
|
||||||
|
import tech.allegro.hexagon.articles.domain.ports.AuthorRepository;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
class ExternalServiceClientAuthorRepository implements AuthorRepository {
|
||||||
|
private final Logger log = LoggerFactory.getLogger(ExternalServiceClientAuthorRepository.class);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Author get(final AuthorId authorId) {
|
||||||
|
/**
|
||||||
|
* external author service integration implementation comes here
|
||||||
|
*/
|
||||||
|
log.info("Author: \"William Shakespeare\" fetched", authorId.value());
|
||||||
|
return Author
|
||||||
|
.author()
|
||||||
|
.withId(authorId)
|
||||||
|
.withName(PersonName.of("William Shakespeare"))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package tech.allegro.hexagon.articles.adapters.config;
|
||||||
|
|
||||||
|
import tech.allegro.hexagon.articles.domain.ArticleFacade;
|
||||||
|
import tech.allegro.hexagon.articles.domain.ports.ArticleAuthorNotifier;
|
||||||
|
import tech.allegro.hexagon.articles.domain.ports.ArticleEventPublisher;
|
||||||
|
import tech.allegro.hexagon.articles.domain.ports.ArticleRepository;
|
||||||
|
import tech.allegro.hexagon.articles.domain.ports.AuthorRepository;
|
||||||
|
import tech.allegro.hexagon.articles.domain.ports.SocialMediaPublisher;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
class ArticleConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
ArticleFacade articleFacade(final ArticleRepository articleRepository,
|
||||||
|
final AuthorRepository authorRepository,
|
||||||
|
final ArticleEventPublisher eventPublisher,
|
||||||
|
final List<SocialMediaPublisher> socialMediaPublishers,
|
||||||
|
final List<ArticleAuthorNotifier> articleAuthorNotifiers) {
|
||||||
|
return new ArticleFacade(eventPublisher, articleRepository, authorRepository, socialMediaPublishers, articleAuthorNotifiers);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package tech.allegro.hexagon.articles.adapters.events;
|
||||||
|
|
||||||
|
class ArticleCreatedEvent {
|
||||||
|
/**
|
||||||
|
* Message broker model implementation comes here
|
||||||
|
*/
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package tech.allegro.hexagon.articles.adapters.events;
|
||||||
|
|
||||||
|
class ArticleRetrievedEvent {
|
||||||
|
/**
|
||||||
|
* Message broker model implementation comes here
|
||||||
|
*/
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package tech.allegro.hexagon.articles.adapters.events;
|
||||||
|
|
||||||
|
import tech.allegro.hexagon.articles.domain.model.Article;
|
||||||
|
import tech.allegro.hexagon.articles.domain.ports.ArticleEventPublisher;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
class MessageBrokerArticleEventPublisher implements ArticleEventPublisher {
|
||||||
|
|
||||||
|
private final Logger log = LoggerFactory.getLogger(MessageBrokerArticleEventPublisher.class);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void publishArticleCreationEvent(final Article article) {
|
||||||
|
/**
|
||||||
|
* message broker integration implementation using {@link ArticleCreatedEvent} comes here
|
||||||
|
*/
|
||||||
|
log.info("Article: \"{}\" creation event published on event bus", article.title().value());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void publishArticleRetrievalEvent(final Article article) {
|
||||||
|
/**
|
||||||
|
* message broker integration implementation using {@link ArticleRetrievedEvent} comes here
|
||||||
|
*/
|
||||||
|
log.info("Article: \"{}\" retrieval event published on event bus", article.title().value());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package tech.allegro.hexagon.articles.adapters.notifications;
|
||||||
|
|
||||||
|
import tech.allegro.hexagon.articles.domain.model.Article;
|
||||||
|
import tech.allegro.hexagon.articles.domain.ports.ArticleAuthorNotifier;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
class ArticleAuthorMailNotifier implements ArticleAuthorNotifier {
|
||||||
|
|
||||||
|
private final Logger log = LoggerFactory.getLogger(ArticleAuthorMailNotifier.class);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void notifyAboutArticleCreation(final Article article) {
|
||||||
|
/**
|
||||||
|
* mail system integration implementation using {@link ArticleMailModel} comes here
|
||||||
|
*/
|
||||||
|
log.info("Mail sent to author: \"{}\"", article.author().name());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package tech.allegro.hexagon.articles.adapters.notifications;
|
||||||
|
|
||||||
|
import tech.allegro.hexagon.articles.domain.model.Article;
|
||||||
|
import tech.allegro.hexagon.articles.domain.ports.ArticleAuthorNotifier;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
class ArticleAuthorSmsNotifier implements ArticleAuthorNotifier {
|
||||||
|
|
||||||
|
private final Logger log = LoggerFactory.getLogger(ArticleAuthorSmsNotifier.class);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void notifyAboutArticleCreation(final Article article) {
|
||||||
|
/**
|
||||||
|
* sms system integration implementation using {@link ArticleSmsModel}comes here
|
||||||
|
*/
|
||||||
|
log.info("SMS sent to author: \"{}\"", article.author().name());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package tech.allegro.hexagon.articles.adapters.notifications;
|
||||||
|
|
||||||
|
class ArticleMailModel {
|
||||||
|
/**
|
||||||
|
* Mail model implementation comes here
|
||||||
|
*/
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package tech.allegro.hexagon.articles.adapters.notifications;
|
||||||
|
|
||||||
|
class ArticleSmsModel {
|
||||||
|
/**
|
||||||
|
* Sms model implementation comes here
|
||||||
|
*/
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package tech.allegro.hexagon.articles.adapters.persistence;
|
||||||
|
|
||||||
|
class ArticleDatabaseModel {
|
||||||
|
/**
|
||||||
|
* Database model implementation comes here
|
||||||
|
*/
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
package tech.allegro.hexagon.articles.adapters.persistence;
|
||||||
|
|
||||||
|
import tech.allegro.hexagon.articles.domain.model.Article;
|
||||||
|
import tech.allegro.hexagon.articles.domain.model.ArticleId;
|
||||||
|
import tech.allegro.hexagon.articles.domain.model.Author;
|
||||||
|
import tech.allegro.hexagon.articles.domain.model.AuthorId;
|
||||||
|
import tech.allegro.hexagon.articles.domain.model.Content;
|
||||||
|
import tech.allegro.hexagon.articles.domain.model.PersonName;
|
||||||
|
import tech.allegro.hexagon.articles.domain.model.Title;
|
||||||
|
import tech.allegro.hexagon.articles.domain.ports.ArticleRepository;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
class DbArticleRepository implements ArticleRepository {
|
||||||
|
|
||||||
|
private final Logger log = LoggerFactory.getLogger(DbArticleRepository.class);
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Article save(final Author author, final Title title, final Content content) {
|
||||||
|
/**
|
||||||
|
* Database integration implementation using {@link ArticleDatabaseModel} comes here
|
||||||
|
*/
|
||||||
|
final String articleId = UUID.randomUUID().toString();
|
||||||
|
log.info("Article: \"{}\" persisted", title.value());
|
||||||
|
return Article.article()
|
||||||
|
.withId(ArticleId.of(articleId))
|
||||||
|
.withAuthor(author)
|
||||||
|
.withTitle(title)
|
||||||
|
.withContent(content)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Article get(final ArticleId id) {
|
||||||
|
/**
|
||||||
|
* Database integration implementation using {@link ArticleDatabaseModel} comes here
|
||||||
|
*/
|
||||||
|
final Title title = Title.of("Hexagonal Architecture");
|
||||||
|
log.info("Article \"{}\" fetched", title.value());
|
||||||
|
return Article.article()
|
||||||
|
.withId(id)
|
||||||
|
.withAuthor(Author
|
||||||
|
.author()
|
||||||
|
.withId(AuthorId.of(UUID.randomUUID().toString()))
|
||||||
|
.withName(PersonName.of("William Shakespeare"))
|
||||||
|
.build())
|
||||||
|
.withTitle(title)
|
||||||
|
.withContent(Content.of("Lorem ipsum"))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package tech.allegro.hexagon.articles.adapters.twitter;
|
||||||
|
|
||||||
|
class ArticleTwitterModel {
|
||||||
|
/**
|
||||||
|
* twitter implementation comes here
|
||||||
|
*/
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package tech.allegro.hexagon.articles.adapters.twitter;
|
||||||
|
|
||||||
|
import tech.allegro.hexagon.articles.domain.model.Article;
|
||||||
|
import tech.allegro.hexagon.articles.domain.ports.SocialMediaPublisher;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
class TwitterArticlePublisher implements SocialMediaPublisher {
|
||||||
|
|
||||||
|
private final Logger log = LoggerFactory.getLogger(TwitterArticlePublisher.class);
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void publish(final Article article) {
|
||||||
|
/**
|
||||||
|
* social media integration implementation using {@link TwitterModel} comes here
|
||||||
|
*/
|
||||||
|
log.info("Article: \"{}\" published on twitter", article.title().value());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
package tech.allegro.hexagon.articles.domain;
|
||||||
|
|
||||||
|
import tech.allegro.hexagon.articles.domain.model.Article;
|
||||||
|
import tech.allegro.hexagon.articles.domain.model.ArticleId;
|
||||||
|
import tech.allegro.hexagon.articles.domain.model.Author;
|
||||||
|
import tech.allegro.hexagon.articles.domain.model.AuthorId;
|
||||||
|
import tech.allegro.hexagon.articles.domain.model.Content;
|
||||||
|
import tech.allegro.hexagon.articles.domain.model.Title;
|
||||||
|
import tech.allegro.hexagon.articles.domain.ports.ArticleAuthorNotifier;
|
||||||
|
import tech.allegro.hexagon.articles.domain.ports.ArticleEventPublisher;
|
||||||
|
import tech.allegro.hexagon.articles.domain.ports.ArticleRepository;
|
||||||
|
import tech.allegro.hexagon.articles.domain.ports.AuthorRepository;
|
||||||
|
import tech.allegro.hexagon.articles.domain.ports.SocialMediaPublisher;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class ArticleFacade {
|
||||||
|
|
||||||
|
private final ArticleEventPublisher eventPublisher;
|
||||||
|
private final ArticleRepository articleRepository;
|
||||||
|
private final AuthorRepository authorRepository;
|
||||||
|
private final List<SocialMediaPublisher> socialMediaPublishers;
|
||||||
|
private final List<ArticleAuthorNotifier> articleAuthorNotifiers;
|
||||||
|
|
||||||
|
public ArticleFacade(final ArticleEventPublisher eventPublisher, final ArticleRepository articleRepository, final AuthorRepository authorRepository, final List<SocialMediaPublisher> socialMediaPublishers, final List<ArticleAuthorNotifier> articleAuthorNotifiers) {
|
||||||
|
this.eventPublisher = eventPublisher;
|
||||||
|
this.articleRepository = articleRepository;
|
||||||
|
this.authorRepository = authorRepository;
|
||||||
|
this.socialMediaPublishers = socialMediaPublishers;
|
||||||
|
this.articleAuthorNotifiers = articleAuthorNotifiers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ArticleId create(final AuthorId authorId, final Title title, final Content content) {
|
||||||
|
final Author author = authorRepository.get(authorId);
|
||||||
|
final Article article = articleRepository.save(author, title, content);
|
||||||
|
eventPublisher.publishArticleCreationEvent(article);
|
||||||
|
socialMediaPublishers.forEach(socialMediaPublisher -> socialMediaPublisher.publish(article));
|
||||||
|
articleAuthorNotifiers.forEach(articleAuthorNotifier -> articleAuthorNotifier.notifyAboutArticleCreation(article));
|
||||||
|
return article.id();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Article get(final ArticleId id) {
|
||||||
|
final Article article = articleRepository.get(id);
|
||||||
|
eventPublisher.publishArticleRetrievalEvent(article);
|
||||||
|
return article;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
package tech.allegro.hexagon.articles.domain.model;
|
||||||
|
|
||||||
|
public class Article {
|
||||||
|
private final ArticleId id;
|
||||||
|
private final Title title;
|
||||||
|
private final Content content;
|
||||||
|
private final Author author;
|
||||||
|
|
||||||
|
|
||||||
|
private Article(final ArticleId id, final Title title, final Content content, final Author author) {
|
||||||
|
this.id = id;
|
||||||
|
this.title = title;
|
||||||
|
this.content = content;
|
||||||
|
this.author = author;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ArticleId id() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Title title() {
|
||||||
|
return title;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Content content() {
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Author author() {
|
||||||
|
return author;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ArticleBuilder article() {
|
||||||
|
return new ArticleBuilder();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static final class ArticleBuilder {
|
||||||
|
private ArticleId id;
|
||||||
|
private Title title;
|
||||||
|
private Content content;
|
||||||
|
private Author author;
|
||||||
|
|
||||||
|
private ArticleBuilder() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public ArticleBuilder withId(ArticleId id) {
|
||||||
|
this.id = id;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ArticleBuilder withTitle(Title title) {
|
||||||
|
this.title = title;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ArticleBuilder withContent(Content content) {
|
||||||
|
this.content = content;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ArticleBuilder withAuthor(Author author) {
|
||||||
|
this.author = author;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Article build() {
|
||||||
|
return new Article(id, title, content, author);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package tech.allegro.hexagon.articles.domain.model;
|
||||||
|
|
||||||
|
public class ArticleId {
|
||||||
|
private final String value;
|
||||||
|
|
||||||
|
private ArticleId(final String value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ArticleId of(final String articleId) {
|
||||||
|
return new ArticleId(articleId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String value() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package tech.allegro.hexagon.articles.domain.model;
|
||||||
|
|
||||||
|
public class Author {
|
||||||
|
private final AuthorId id;
|
||||||
|
private final PersonName name;
|
||||||
|
|
||||||
|
|
||||||
|
public Author(final AuthorId id, final PersonName name) {
|
||||||
|
this.id = id;
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AuthorBuilder author() {
|
||||||
|
return new AuthorBuilder();
|
||||||
|
}
|
||||||
|
|
||||||
|
public PersonName name() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class AuthorBuilder {
|
||||||
|
private AuthorId id;
|
||||||
|
private PersonName name;
|
||||||
|
|
||||||
|
private AuthorBuilder() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public AuthorBuilder withId(AuthorId id) {
|
||||||
|
this.id = id;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AuthorBuilder withName(PersonName name) {
|
||||||
|
this.name = name;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Author build() {
|
||||||
|
return new Author(id, name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package tech.allegro.hexagon.articles.domain.model;
|
||||||
|
|
||||||
|
public class AuthorId {
|
||||||
|
private final String value;
|
||||||
|
|
||||||
|
private AuthorId(final String value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AuthorId of(final String authorId) {
|
||||||
|
return new AuthorId(authorId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String value() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package tech.allegro.hexagon.articles.domain.model;
|
||||||
|
|
||||||
|
public class Content {
|
||||||
|
private final String value;
|
||||||
|
|
||||||
|
private Content(final String value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Content of(final String content) {
|
||||||
|
return new Content(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String value() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package tech.allegro.hexagon.articles.domain.model;
|
||||||
|
|
||||||
|
public class PersonName {
|
||||||
|
private final String value;
|
||||||
|
|
||||||
|
private PersonName(final String value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PersonName of(final String content) {
|
||||||
|
return new PersonName(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String value() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package tech.allegro.hexagon.articles.domain.model;
|
||||||
|
|
||||||
|
public class Title {
|
||||||
|
private final String value;
|
||||||
|
|
||||||
|
private Title(final String value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Title of(final String title) {
|
||||||
|
return new Title(title);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String value() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package tech.allegro.hexagon.articles.domain.ports;
|
||||||
|
|
||||||
|
import tech.allegro.hexagon.articles.domain.model.Article;
|
||||||
|
|
||||||
|
public interface ArticleAuthorNotifier {
|
||||||
|
|
||||||
|
void notifyAboutArticleCreation(Article article);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package tech.allegro.hexagon.articles.domain.ports;
|
||||||
|
|
||||||
|
import tech.allegro.hexagon.articles.domain.model.Article;
|
||||||
|
|
||||||
|
public interface ArticleEventPublisher {
|
||||||
|
|
||||||
|
void publishArticleCreationEvent(Article article);
|
||||||
|
|
||||||
|
void publishArticleRetrievalEvent(Article article);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package tech.allegro.hexagon.articles.domain.ports;
|
||||||
|
|
||||||
|
import tech.allegro.hexagon.articles.domain.model.Article;
|
||||||
|
import tech.allegro.hexagon.articles.domain.model.ArticleId;
|
||||||
|
import tech.allegro.hexagon.articles.domain.model.Author;
|
||||||
|
import tech.allegro.hexagon.articles.domain.model.Content;
|
||||||
|
import tech.allegro.hexagon.articles.domain.model.Title;
|
||||||
|
|
||||||
|
public interface ArticleRepository {
|
||||||
|
|
||||||
|
Article save(Author author, Title title, Content content);
|
||||||
|
|
||||||
|
Article get(ArticleId id);
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package tech.allegro.hexagon.articles.domain.ports;
|
||||||
|
|
||||||
|
import tech.allegro.hexagon.articles.domain.model.Author;
|
||||||
|
import tech.allegro.hexagon.articles.domain.model.AuthorId;
|
||||||
|
|
||||||
|
public interface AuthorRepository {
|
||||||
|
|
||||||
|
Author get(AuthorId authorId);
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package tech.allegro.hexagon.articles.domain.ports;
|
||||||
|
|
||||||
|
import tech.allegro.hexagon.articles.domain.model.Article;
|
||||||
|
|
||||||
|
public interface SocialMediaPublisher {
|
||||||
|
|
||||||
|
void publish(Article article);
|
||||||
|
|
||||||
|
}
|
||||||
1
src/main/resources/application.properties
Normal file
1
src/main/resources/application.properties
Normal file
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user