├── .gitignore ├── README.md ├── cleanarch-from-ntier ├── Dockerfile ├── data │ ├── db.mv.db │ └── db.trace.db ├── docker-compose.yml ├── mvnw ├── mvnw.cmd ├── pom.xml └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── asilva │ │ │ ├── SpringBootCleanarchApplication.java │ │ │ ├── application │ │ │ ├── gateways │ │ │ │ └── UserGateway.java │ │ │ ├── service │ │ │ │ ├── UserObjectValidation.java │ │ │ │ └── UserService.java │ │ │ └── usecases │ │ │ │ └── CreateUser.java │ │ │ ├── domain │ │ │ ├── dto │ │ │ │ ├── request │ │ │ │ │ └── CreateUserRequest.java │ │ │ │ └── response │ │ │ │ │ └── CreateUserResponse.java │ │ │ └── model │ │ │ │ └── UserModel.java │ │ │ ├── infrastructure │ │ │ ├── config │ │ │ │ ├── DatabaseMigration.java │ │ │ │ └── UserDependecyInjection.java │ │ │ ├── exception │ │ │ │ ├── ErrorDetails.java │ │ │ │ ├── GlobalExceptionHandler.java │ │ │ │ └── UserValidationException.java │ │ │ ├── gateways │ │ │ │ └── UserRepositoryGateway.java │ │ │ └── persistence │ │ │ │ ├── entity │ │ │ │ └── UserEntity.java │ │ │ │ └── repository │ │ │ │ └── UserRepository.java │ │ │ └── interfaces │ │ │ ├── controller │ │ │ └── UserController.java │ │ │ └── mappers │ │ │ ├── UserDtoMapper.java │ │ │ └── UserEntityMapper.java │ └── resources │ │ ├── application.properties │ │ └── schema.sql │ └── test │ └── java │ └── com │ └── cleanarch │ ├── SpringBootCleanarchApplicationTests.java │ ├── application │ ├── gateways │ │ └── UserGatewayTest.java │ ├── service │ │ └── UserServiceTest.java │ └── usecases │ │ └── CreateUserTest.java │ ├── domain │ ├── dto │ │ ├── request │ │ │ └── CreateUserRequestTest.java │ │ └── response │ │ │ └── CreateUserResponseTest.java │ └── model │ │ └── UserModelTest.java │ ├── infrastructure │ ├── exception │ │ ├── ErrorDetailsTest.java │ │ ├── GlobalExceptionHandlerTest.java │ │ ├── UserObjectValidationTest.java │ │ └── UserValidationExceptionTest.java │ ├── gateways │ │ └── UserRepositoryGatewayTest.java │ └── persistence │ │ ├── entity │ │ └── UserEntityTest.java │ │ └── repository │ │ └── UserRepositoryTest.java │ ├── interfaces │ ├── controller │ │ └── UserControllerTest.java │ └── mappers │ │ └── UserEntityMapperTest.java │ └── util │ └── UserCreationUtils.java ├── cleanarch-from-scratch ├── Dockerfile ├── build.gradle ├── docker-compose.yml ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── asilva │ │ │ └── springbootcleanarchitecture │ │ │ ├── SpringBootCleanArchitectureApplication.java │ │ │ ├── config │ │ │ ├── DeleteCustomerByIdConfig.java │ │ │ ├── FindCustomerByIdConfig.java │ │ │ ├── InsertCustomerConfig.java │ │ │ ├── KafkaConsumerConfig.java │ │ │ ├── KafkaProducerConfig.java │ │ │ └── UpdateCustomerByIdConfig.java │ │ │ ├── core │ │ │ ├── dataprovider │ │ │ │ ├── DeleteCustomerById.java │ │ │ │ ├── FindAddressByZipCode.java │ │ │ │ ├── FindCustomerById.java │ │ │ │ ├── InsertCustomer.java │ │ │ │ ├── SendCpForValidation.java │ │ │ │ └── UpdateCustomerById.java │ │ │ ├── domain │ │ │ │ ├── Address.java │ │ │ │ └── Customer.java │ │ │ └── usecase │ │ │ │ ├── DeleteCustomerByIdUseCase.java │ │ │ │ ├── FindCustomerByIdUseCase.java │ │ │ │ ├── InsertCustomerUseCase.java │ │ │ │ ├── UpdateCustomerByIdUseCase.java │ │ │ │ └── impl │ │ │ │ ├── DeleteCustomerByIdUseCaseImpl.java │ │ │ │ ├── FindCustomerByIdUseCaseImpl.java │ │ │ │ ├── InsertCustomerUseCaseImpl.java │ │ │ │ └── UpdateCustomerByIdUseCaseImpl.java │ │ │ ├── dataprovider │ │ │ ├── client │ │ │ │ ├── FindAddressByZipCodeClient.java │ │ │ │ └── mapper │ │ │ │ │ └── AddressResponseMapper.java │ │ │ ├── impl │ │ │ │ ├── DeleteCustomerByIdImpl.java │ │ │ │ ├── FindAddressByZipCodeImpl.java │ │ │ │ ├── FindCustomerByIdImpl.java │ │ │ │ ├── InsertCustomerImpl.java │ │ │ │ ├── SendCpfForValidationImpl.java │ │ │ │ └── UpdateCustomerByIdImpl.java │ │ │ └── persistence │ │ │ │ ├── entity │ │ │ │ ├── AddressEntity.java │ │ │ │ └── CustomerEntity.java │ │ │ │ ├── mapper │ │ │ │ └── CustomerEntityMapper.java │ │ │ │ └── repository │ │ │ │ └── CustomerRepository.java │ │ │ └── entrypoint │ │ │ ├── consumer │ │ │ ├── ReceiveValidatedCpfConsumer.java │ │ │ ├── mapper │ │ │ │ └── CustomerMessageMapper.java │ │ │ └── message │ │ │ │ └── CustomerMessage.java │ │ │ ├── controller │ │ │ ├── CustomerController.java │ │ │ └── mapper │ │ │ │ └── CustomerMapper.java │ │ │ └── dto │ │ │ ├── request │ │ │ └── CustomerRequestDto.java │ │ │ └── response │ │ │ ├── AddressResponseDto.java │ │ │ └── CustomerResponseDto.java │ └── resources │ │ ├── META-INF │ │ └── additional-spring-configuration-metadata.json │ │ └── application.properties │ └── test │ └── java │ └── com │ └── asilva │ └── springbootcleanarchitecture │ └── SpringBootCleanArchitectureApplicationTests.java └── docs └── cleanarch.png /.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | .gradle 3 | build/ 4 | target/ 5 | !gradle/wrapper/gradle-wrapper.jar 6 | !**/src/main/**/build/ 7 | !**/src/test/**/build/ 8 | 9 | ### STS ### 10 | .apt_generated 11 | .classpath 12 | .factorypath 13 | .project 14 | .settings 15 | .springBeans 16 | .sts4-cache 17 | bin/ 18 | !**/src/main/**/bin/ 19 | !**/src/test/**/bin/ 20 | 21 | ### IntelliJ IDEA ### 22 | .idea 23 | *.iws 24 | *.iml 25 | *.ipr 26 | out/ 27 | !**/src/main/**/out/ 28 | !**/src/test/**/out/ 29 | 30 | ### NetBeans ### 31 | /nbproject/private/ 32 | /nbbuild/ 33 | /dist/ 34 | /nbdist/ 35 | /.nb-gradle/ 36 | 37 | ### VS Code ### 38 | .vscode/ 39 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Spring boot cleanarch 2 | 3 | Repository containing two simple Spring Boot applications that each follows Uncle Bob's Clean Architecture project architecture. The objective of this repository is simple: To learn about Clean Architecture and how to built scalable, maintanable and testable application with it. 4 | 5 | ## Features 6 | 7 | - Create, read, update, and delete records 8 | 9 | - Store information in H2/ MongoDB database 10 | 11 | ## Architecture 12 | 13 | The applications in this repository are built using Spring Boot and following the Clean Architecture, a design pattern that is composed of several layers, each with its own distinct responsibilities. Here’s a brief overview of each layer: 14 | 15 | ![Uncle Bob's Clean Architecture in an image](docs/cleanarch.png) 16 | 17 | - `Entities/ Domain Objects`: This layer represents the core business objects or concepts that the application is concerned with. It contains the business rules and logic that define the behavior of the application. 18 | 19 | - `Use Cases`: This layer represents the specific actions or operations that can be performed on the entities. It contains the application-specific business rules and logic that define how the entities are used. 20 | 21 | - `Interfaces/ Data Provider`: This layer defines how the domain layer interacts with the other layers of the application, such as the presentation and infrastructure layers. It contains the interfaces that the other layers use to communicate with the domain layer. 22 | 23 | - `Infrastructure/ Entrypoint`: This layer represents the implementation details of the application, such as the database, file system, or external services. It contains the concrete implementations of the interfaces defined in the domain layer. 24 | 25 | ### Advantages of using Clean Architecture 26 | 27 | - `Improved maintainability`: Clean Architecture promotes modularity, testability, and maintainability by separating the domain layer from the other layers of the application. 28 | 29 | - `Loose coupling of components`: Clean Architecture is loosely coupled to the externals you want to use because of the layer of the adapters. This makes it easier to test, develop, and maintain the code of the application. 30 | 31 | - `Improved flexibility`: Clean Architecture makes it easier to reuse components in other projects. 32 | 33 | - `Improved testability`: Clean Architecture makes it easier to test and debug the code, as each layer can be tested independently and mocked as needed. The domain layer does not depend on any external frameworks or libraries, making it easy to write unit tests for the business logic. 34 | 35 | - `Improved team productivity`: Clean Architecture improves team productivity by allowing different developers to work on different layers of the application without interfering with each other. The clear separation of concerns and the use of interfaces make it easier to collaborate and integrate the code. 36 | 37 | - `Improved security`: Clean Architecture improves security by limiting the exposure of the domain layer to the other layers of the application. The domain layer contains the most sensitive and critical data and logic of the application, and by isolating it from the infrastructure layer, it reduces the risk of data breaches or unauthorized access. 38 | 39 | ## Getting Started 40 | 41 | ### Prerequisites 42 | 43 | - Java 17 or higher 44 | - Maven 45 | 46 | ### Installation 47 | 48 | 1. Clone the repository 49 | git clone https://github.com/athirsonsilva/spring-boot-cleanarch.git 50 | 51 | 52 | 2. Change to project directory 53 | 54 | ```bash 55 | cd spring-boot-cleanarch 56 | ``` 57 | 58 | 3. How to run the applications 59 | 60 | 1. To run `cleanarch-from-ntier` application, change to its directory and run 61 | 62 | ```bash 63 | ./mvnw spring-boot:run 64 | ``` 65 | 66 | 2. To run `cleanarch-from-scratch` application, change to its directory and run 67 | 68 | ```bash 69 | ./gradlew bootRun 70 | ``` 71 | 72 | The application will start and be available at `http://localhost:8080`. When accessing the address, you will see the Swagger UI documentation screen. 73 | -------------------------------------------------------------------------------- /cleanarch-from-ntier/Dockerfile: -------------------------------------------------------------------------------- 1 | # 2 | # Build stage 3 | # 4 | FROM maven:3.9.4-amazoncorretto-17 AS build 5 | 6 | COPY src /home/app/src 7 | COPY data /home/app/data 8 | COPY pom.xml /home/app 9 | 10 | RUN mvn -f /home/app/pom.xml clean package -DskipTests 11 | 12 | # 13 | # Package stage 14 | # 15 | FROM amazoncorretto:17 16 | 17 | COPY --from=build /home/app/target/*.jar /usr/local/lib/app.jar 18 | 19 | EXPOSE 80 20 | 21 | ENTRYPOINT ["java","-jar","/usr/local/lib/app.jar"] -------------------------------------------------------------------------------- /cleanarch-from-ntier/data/db.mv.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AthirsonSilva/spring-boot-clean-architecture/d97d91cc563badad10ea2b8c9981783135db7a28/cleanarch-from-ntier/data/db.mv.db -------------------------------------------------------------------------------- /cleanarch-from-ntier/data/db.trace.db: -------------------------------------------------------------------------------- 1 | 2023-11-18 21:27:06 jdbc[3]: exception 2 | org.h2.jdbc.JdbcSQLSyntaxErrorException: Table "user" not found (candidates are: "USER"); SQL statement: 3 | INSERT INTO "user" ("EMAIL", "PASSWORD", "USERNAME") VALUES (?, ?, ?) [42103-214] 4 | 2023-11-18 21:32:40 jdbc[3]: exception 5 | org.h2.jdbc.JdbcSQLSyntaxErrorException: Table "user" not found (candidates are: "USER"); SQL statement: 6 | INSERT INTO "user" ("EMAIL", "PASSWORD", "USERNAME") VALUES (?, ?, ?) [42103-214] 7 | 2023-11-18 21:32:57 jdbc[3]: exception 8 | org.h2.jdbc.JdbcSQLSyntaxErrorException: Table "user" not found (candidates are: "USER"); SQL statement: 9 | INSERT INTO "user" ("EMAIL", "PASSWORD", "USERNAME") VALUES (?, ?, ?) [42103-214] 10 | 2023-11-18 21:33:25 jdbc[3]: exception 11 | org.h2.jdbc.JdbcSQLSyntaxErrorException: Table "user" not found (candidates are: "USER"); SQL statement: 12 | INSERT INTO "user" ("EMAIL", "PASSWORD", "USERNAME") VALUES (?, ?, ?) [42103-214] 13 | 2023-11-18 21:56:32 jdbc[3]: exception 14 | org.h2.jdbc.JdbcSQLSyntaxErrorException: Table "user" not found (candidates are: "USER"); SQL statement: 15 | INSERT INTO "user" ("EMAIL", "PASSWORD", "USERNAME") VALUES (?, ?, ?) [42103-214] 16 | -------------------------------------------------------------------------------- /cleanarch-from-ntier/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3.8" 2 | 3 | services: 4 | cleanarch-from-ntier: 5 | container_name: cleanarch-from-ntier 6 | image: cleanarch-from-ntier:latest 7 | build: 8 | context: ./ 9 | dockerfile: Dockerfile 10 | ports: 11 | - "8080:8080" 12 | restart: on-failure 13 | deploy: 14 | resources: 15 | limits: 16 | cpus: '0.5' 17 | memory: 700M 18 | restart_policy: 19 | condition: on-failure -------------------------------------------------------------------------------- /cleanarch-from-ntier/mvnw: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ---------------------------------------------------------------------------- 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # https://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | # ---------------------------------------------------------------------------- 20 | 21 | # ---------------------------------------------------------------------------- 22 | # Apache Maven Wrapper startup batch script, version 3.2.0 23 | # 24 | # Required ENV vars: 25 | # ------------------ 26 | # JAVA_HOME - location of a JDK home dir 27 | # 28 | # Optional ENV vars 29 | # ----------------- 30 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | # e.g. to debug Maven itself, use 32 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | # ---------------------------------------------------------------------------- 35 | 36 | if [ -z "$MAVEN_SKIP_RC" ] ; then 37 | 38 | if [ -f /usr/local/etc/mavenrc ] ; then 39 | . /usr/local/etc/mavenrc 40 | fi 41 | 42 | if [ -f /etc/mavenrc ] ; then 43 | . /etc/mavenrc 44 | fi 45 | 46 | if [ -f "$HOME/.mavenrc" ] ; then 47 | . "$HOME/.mavenrc" 48 | fi 49 | 50 | fi 51 | 52 | # OS specific support. $var _must_ be set to either true or false. 53 | cygwin=false; 54 | darwin=false; 55 | mingw=false 56 | case "$(uname)" in 57 | CYGWIN*) cygwin=true ;; 58 | MINGW*) mingw=true;; 59 | Darwin*) darwin=true 60 | # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home 61 | # See https://developer.apple.com/library/mac/qa/qa1170/_index.html 62 | if [ -z "$JAVA_HOME" ]; then 63 | if [ -x "/usr/libexec/java_home" ]; then 64 | JAVA_HOME="$(/usr/libexec/java_home)"; export JAVA_HOME 65 | else 66 | JAVA_HOME="/Library/Java/Home"; export JAVA_HOME 67 | fi 68 | fi 69 | ;; 70 | esac 71 | 72 | if [ -z "$JAVA_HOME" ] ; then 73 | if [ -r /etc/gentoo-release ] ; then 74 | JAVA_HOME=$(java-config --jre-home) 75 | fi 76 | fi 77 | 78 | # For Cygwin, ensure paths are in UNIX format before anything is touched 79 | if $cygwin ; then 80 | [ -n "$JAVA_HOME" ] && 81 | JAVA_HOME=$(cygpath --unix "$JAVA_HOME") 82 | [ -n "$CLASSPATH" ] && 83 | CLASSPATH=$(cygpath --path --unix "$CLASSPATH") 84 | fi 85 | 86 | # For Mingw, ensure paths are in UNIX format before anything is touched 87 | if $mingw ; then 88 | [ -n "$JAVA_HOME" ] && [ -d "$JAVA_HOME" ] && 89 | JAVA_HOME="$(cd "$JAVA_HOME" || (echo "cannot cd into $JAVA_HOME."; exit 1); pwd)" 90 | fi 91 | 92 | if [ -z "$JAVA_HOME" ]; then 93 | javaExecutable="$(which javac)" 94 | if [ -n "$javaExecutable" ] && ! [ "$(expr "\"$javaExecutable\"" : '\([^ ]*\)')" = "no" ]; then 95 | # readlink(1) is not available as standard on Solaris 10. 96 | readLink=$(which readlink) 97 | if [ ! "$(expr "$readLink" : '\([^ ]*\)')" = "no" ]; then 98 | if $darwin ; then 99 | javaHome="$(dirname "\"$javaExecutable\"")" 100 | javaExecutable="$(cd "\"$javaHome\"" && pwd -P)/javac" 101 | else 102 | javaExecutable="$(readlink -f "\"$javaExecutable\"")" 103 | fi 104 | javaHome="$(dirname "\"$javaExecutable\"")" 105 | javaHome=$(expr "$javaHome" : '\(.*\)/bin') 106 | JAVA_HOME="$javaHome" 107 | export JAVA_HOME 108 | fi 109 | fi 110 | fi 111 | 112 | if [ -z "$JAVACMD" ] ; then 113 | if [ -n "$JAVA_HOME" ] ; then 114 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 115 | # IBM's JDK on AIX uses strange locations for the executables 116 | JAVACMD="$JAVA_HOME/jre/sh/java" 117 | else 118 | JAVACMD="$JAVA_HOME/bin/java" 119 | fi 120 | else 121 | JAVACMD="$(\unset -f command 2>/dev/null; \command -v java)" 122 | fi 123 | fi 124 | 125 | if [ ! -x "$JAVACMD" ] ; then 126 | echo "Error: JAVA_HOME is not defined correctly." >&2 127 | echo " We cannot execute $JAVACMD" >&2 128 | exit 1 129 | fi 130 | 131 | if [ -z "$JAVA_HOME" ] ; then 132 | echo "Warning: JAVA_HOME environment variable is not set." 133 | fi 134 | 135 | # traverses directory structure from process work directory to filesystem root 136 | # first directory with .mvn subdirectory is considered project base directory 137 | find_maven_basedir() { 138 | if [ -z "$1" ] 139 | then 140 | echo "Path not specified to find_maven_basedir" 141 | return 1 142 | fi 143 | 144 | basedir="$1" 145 | wdir="$1" 146 | while [ "$wdir" != '/' ] ; do 147 | if [ -d "$wdir"/.mvn ] ; then 148 | basedir=$wdir 149 | break 150 | fi 151 | # workaround for JBEAP-8937 (on Solaris 10/Sparc) 152 | if [ -d "${wdir}" ]; then 153 | wdir=$(cd "$wdir/.." || exit 1; pwd) 154 | fi 155 | # end of workaround 156 | done 157 | printf '%s' "$(cd "$basedir" || exit 1; pwd)" 158 | } 159 | 160 | # concatenates all lines of a file 161 | concat_lines() { 162 | if [ -f "$1" ]; then 163 | # Remove \r in case we run on Windows within Git Bash 164 | # and check out the repository with auto CRLF management 165 | # enabled. Otherwise, we may read lines that are delimited with 166 | # \r\n and produce $'-Xarg\r' rather than -Xarg due to word 167 | # splitting rules. 168 | tr -s '\r\n' ' ' < "$1" 169 | fi 170 | } 171 | 172 | log() { 173 | if [ "$MVNW_VERBOSE" = true ]; then 174 | printf '%s\n' "$1" 175 | fi 176 | } 177 | 178 | BASE_DIR=$(find_maven_basedir "$(dirname "$0")") 179 | if [ -z "$BASE_DIR" ]; then 180 | exit 1; 181 | fi 182 | 183 | MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}; export MAVEN_PROJECTBASEDIR 184 | log "$MAVEN_PROJECTBASEDIR" 185 | 186 | ########################################################################################## 187 | # Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 188 | # This allows using the maven wrapper in projects that prohibit checking in binary data. 189 | ########################################################################################## 190 | wrapperJarPath="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" 191 | if [ -r "$wrapperJarPath" ]; then 192 | log "Found $wrapperJarPath" 193 | else 194 | log "Couldn't find $wrapperJarPath, downloading it ..." 195 | 196 | if [ -n "$MVNW_REPOURL" ]; then 197 | wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" 198 | else 199 | wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" 200 | fi 201 | while IFS="=" read -r key value; do 202 | # Remove '\r' from value to allow usage on windows as IFS does not consider '\r' as a separator ( considers space, tab, new line ('\n'), and custom '=' ) 203 | safeValue=$(echo "$value" | tr -d '\r') 204 | case "$key" in (wrapperUrl) wrapperUrl="$safeValue"; break ;; 205 | esac 206 | done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties" 207 | log "Downloading from: $wrapperUrl" 208 | 209 | if $cygwin; then 210 | wrapperJarPath=$(cygpath --path --windows "$wrapperJarPath") 211 | fi 212 | 213 | if command -v wget > /dev/null; then 214 | log "Found wget ... using wget" 215 | [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--quiet" 216 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then 217 | wget $QUIET "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" 218 | else 219 | wget $QUIET --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" 220 | fi 221 | elif command -v curl > /dev/null; then 222 | log "Found curl ... using curl" 223 | [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--silent" 224 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then 225 | curl $QUIET -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath" 226 | else 227 | curl $QUIET --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath" 228 | fi 229 | else 230 | log "Falling back to using Java to download" 231 | javaSource="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.java" 232 | javaClass="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.class" 233 | # For Cygwin, switch paths to Windows format before running javac 234 | if $cygwin; then 235 | javaSource=$(cygpath --path --windows "$javaSource") 236 | javaClass=$(cygpath --path --windows "$javaClass") 237 | fi 238 | if [ -e "$javaSource" ]; then 239 | if [ ! -e "$javaClass" ]; then 240 | log " - Compiling MavenWrapperDownloader.java ..." 241 | ("$JAVA_HOME/bin/javac" "$javaSource") 242 | fi 243 | if [ -e "$javaClass" ]; then 244 | log " - Running MavenWrapperDownloader.java ..." 245 | ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$wrapperUrl" "$wrapperJarPath") || rm -f "$wrapperJarPath" 246 | fi 247 | fi 248 | fi 249 | fi 250 | ########################################################################################## 251 | # End of extension 252 | ########################################################################################## 253 | 254 | # If specified, validate the SHA-256 sum of the Maven wrapper jar file 255 | wrapperSha256Sum="" 256 | while IFS="=" read -r key value; do 257 | case "$key" in (wrapperSha256Sum) wrapperSha256Sum=$value; break ;; 258 | esac 259 | done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties" 260 | if [ -n "$wrapperSha256Sum" ]; then 261 | wrapperSha256Result=false 262 | if command -v sha256sum > /dev/null; then 263 | if echo "$wrapperSha256Sum $wrapperJarPath" | sha256sum -c > /dev/null 2>&1; then 264 | wrapperSha256Result=true 265 | fi 266 | elif command -v shasum > /dev/null; then 267 | if echo "$wrapperSha256Sum $wrapperJarPath" | shasum -a 256 -c > /dev/null 2>&1; then 268 | wrapperSha256Result=true 269 | fi 270 | else 271 | echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." 272 | echo "Please install either command, or disable validation by removing 'wrapperSha256Sum' from your maven-wrapper.properties." 273 | exit 1 274 | fi 275 | if [ $wrapperSha256Result = false ]; then 276 | echo "Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised." >&2 277 | echo "Investigate or delete $wrapperJarPath to attempt a clean download." >&2 278 | echo "If you updated your Maven version, you need to update the specified wrapperSha256Sum property." >&2 279 | exit 1 280 | fi 281 | fi 282 | 283 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" 284 | 285 | # For Cygwin, switch paths to Windows format before running java 286 | if $cygwin; then 287 | [ -n "$JAVA_HOME" ] && 288 | JAVA_HOME=$(cygpath --path --windows "$JAVA_HOME") 289 | [ -n "$CLASSPATH" ] && 290 | CLASSPATH=$(cygpath --path --windows "$CLASSPATH") 291 | [ -n "$MAVEN_PROJECTBASEDIR" ] && 292 | MAVEN_PROJECTBASEDIR=$(cygpath --path --windows "$MAVEN_PROJECTBASEDIR") 293 | fi 294 | 295 | # Provide a "standardized" way to retrieve the CLI args that will 296 | # work with both Windows and non-Windows executions. 297 | MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $*" 298 | export MAVEN_CMD_LINE_ARGS 299 | 300 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 301 | 302 | # shellcheck disable=SC2086 # safe args 303 | exec "$JAVACMD" \ 304 | $MAVEN_OPTS \ 305 | $MAVEN_DEBUG_OPTS \ 306 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ 307 | "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ 308 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" 309 | -------------------------------------------------------------------------------- /cleanarch-from-ntier/mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM https://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Apache Maven Wrapper startup batch script, version 3.2.0 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 28 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending 29 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 30 | @REM e.g. to debug Maven itself, use 31 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 32 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 33 | @REM ---------------------------------------------------------------------------- 34 | 35 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 36 | @echo off 37 | @REM set title of command window 38 | title %0 39 | @REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' 40 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 41 | 42 | @REM set %HOME% to equivalent of $HOME 43 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 44 | 45 | @REM Execute a user defined script before this one 46 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 47 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 48 | if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %* 49 | if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %* 50 | :skipRcPre 51 | 52 | @setlocal 53 | 54 | set ERROR_CODE=0 55 | 56 | @REM To isolate internal variables from possible post scripts, we use another setlocal 57 | @setlocal 58 | 59 | @REM ==== START VALIDATION ==== 60 | if not "%JAVA_HOME%" == "" goto OkJHome 61 | 62 | echo. 63 | echo Error: JAVA_HOME not found in your environment. >&2 64 | echo Please set the JAVA_HOME variable in your environment to match the >&2 65 | echo location of your Java installation. >&2 66 | echo. 67 | goto error 68 | 69 | :OkJHome 70 | if exist "%JAVA_HOME%\bin\java.exe" goto init 71 | 72 | echo. 73 | echo Error: JAVA_HOME is set to an invalid directory. >&2 74 | echo JAVA_HOME = "%JAVA_HOME%" >&2 75 | echo Please set the JAVA_HOME variable in your environment to match the >&2 76 | echo location of your Java installation. >&2 77 | echo. 78 | goto error 79 | 80 | @REM ==== END VALIDATION ==== 81 | 82 | :init 83 | 84 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 85 | @REM Fallback to current working directory if not found. 86 | 87 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 88 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 89 | 90 | set EXEC_DIR=%CD% 91 | set WDIR=%EXEC_DIR% 92 | :findBaseDir 93 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 94 | cd .. 95 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 96 | set WDIR=%CD% 97 | goto findBaseDir 98 | 99 | :baseDirFound 100 | set MAVEN_PROJECTBASEDIR=%WDIR% 101 | cd "%EXEC_DIR%" 102 | goto endDetectBaseDir 103 | 104 | :baseDirNotFound 105 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 106 | cd "%EXEC_DIR%" 107 | 108 | :endDetectBaseDir 109 | 110 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 111 | 112 | @setlocal EnableExtensions EnableDelayedExpansion 113 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 114 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 115 | 116 | :endReadAdditionalConfig 117 | 118 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 119 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 120 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 121 | 122 | set WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" 123 | 124 | FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( 125 | IF "%%A"=="wrapperUrl" SET WRAPPER_URL=%%B 126 | ) 127 | 128 | @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 129 | @REM This allows using the maven wrapper in projects that prohibit checking in binary data. 130 | if exist %WRAPPER_JAR% ( 131 | if "%MVNW_VERBOSE%" == "true" ( 132 | echo Found %WRAPPER_JAR% 133 | ) 134 | ) else ( 135 | if not "%MVNW_REPOURL%" == "" ( 136 | SET WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" 137 | ) 138 | if "%MVNW_VERBOSE%" == "true" ( 139 | echo Couldn't find %WRAPPER_JAR%, downloading it ... 140 | echo Downloading from: %WRAPPER_URL% 141 | ) 142 | 143 | powershell -Command "&{"^ 144 | "$webclient = new-object System.Net.WebClient;"^ 145 | "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ 146 | "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ 147 | "}"^ 148 | "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%WRAPPER_URL%', '%WRAPPER_JAR%')"^ 149 | "}" 150 | if "%MVNW_VERBOSE%" == "true" ( 151 | echo Finished downloading %WRAPPER_JAR% 152 | ) 153 | ) 154 | @REM End of extension 155 | 156 | @REM If specified, validate the SHA-256 sum of the Maven wrapper jar file 157 | SET WRAPPER_SHA_256_SUM="" 158 | FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( 159 | IF "%%A"=="wrapperSha256Sum" SET WRAPPER_SHA_256_SUM=%%B 160 | ) 161 | IF NOT %WRAPPER_SHA_256_SUM%=="" ( 162 | powershell -Command "&{"^ 163 | "$hash = (Get-FileHash \"%WRAPPER_JAR%\" -Algorithm SHA256).Hash.ToLower();"^ 164 | "If('%WRAPPER_SHA_256_SUM%' -ne $hash){"^ 165 | " Write-Output 'Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised.';"^ 166 | " Write-Output 'Investigate or delete %WRAPPER_JAR% to attempt a clean download.';"^ 167 | " Write-Output 'If you updated your Maven version, you need to update the specified wrapperSha256Sum property.';"^ 168 | " exit 1;"^ 169 | "}"^ 170 | "}" 171 | if ERRORLEVEL 1 goto error 172 | ) 173 | 174 | @REM Provide a "standardized" way to retrieve the CLI args that will 175 | @REM work with both Windows and non-Windows executions. 176 | set MAVEN_CMD_LINE_ARGS=%* 177 | 178 | %MAVEN_JAVA_EXE% ^ 179 | %JVM_CONFIG_MAVEN_PROPS% ^ 180 | %MAVEN_OPTS% ^ 181 | %MAVEN_DEBUG_OPTS% ^ 182 | -classpath %WRAPPER_JAR% ^ 183 | "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^ 184 | %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 185 | if ERRORLEVEL 1 goto error 186 | goto end 187 | 188 | :error 189 | set ERROR_CODE=1 190 | 191 | :end 192 | @endlocal & set ERROR_CODE=%ERROR_CODE% 193 | 194 | if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost 195 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 196 | if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat" 197 | if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd" 198 | :skipRcPost 199 | 200 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 201 | if "%MAVEN_BATCH_PAUSE%"=="on" pause 202 | 203 | if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE% 204 | 205 | cmd /C exit /B %ERROR_CODE% 206 | -------------------------------------------------------------------------------- /cleanarch-from-ntier/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | org.springframework.boot 8 | spring-boot-starter-parent 9 | 3.1.5 10 | 11 | 12 | com.asilva 13 | cleanarch-from-ntier 14 | 0.0.1-SNAPSHOT 15 | cleanarch-from-ntier 16 | Demo project for Spring Boot 17 | 18 | 17 19 | 20 | 21 | 22 | 23 | org.springframework.boot 24 | spring-boot-starter-actuator 25 | 26 | 27 | 28 | org.springframework.boot 29 | spring-boot-starter-data-jdbc 30 | 31 | 32 | org.springframework.boot 33 | spring-boot-starter-web 34 | 35 | 36 | org.springframework.boot 37 | spring-boot-devtools 38 | runtime 39 | true 40 | 41 | 42 | com.h2database 43 | h2 44 | runtime 45 | 46 | 47 | org.projectlombok 48 | lombok 49 | true 50 | 51 | 52 | org.springdoc 53 | springdoc-openapi-starter-webmvc-ui 54 | 2.0.4 55 | 56 | 57 | 58 | org.springframework.boot 59 | spring-boot-starter-test 60 | test 61 | 62 | 63 | junit 64 | junit 65 | test 66 | 67 | 68 | org.junit.jupiter 69 | junit-jupiter 70 | test 71 | 72 | 73 | org.jacoco 74 | jacoco-maven-plugin 75 | 0.8.11 76 | 77 | 78 | 79 | 80 | 81 | 82 | org.jacoco 83 | jacoco-maven-plugin 84 | 0.8.11 85 | 86 | 87 | 88 | prepare-agent 89 | 90 | 91 | 92 | report 93 | prepare-package 94 | 95 | report 96 | 97 | 98 | 99 | 100 | 101 | 102 | org.springframework.boot 103 | spring-boot-maven-plugin 104 | 105 | 106 | 107 | org.projectlombok 108 | lombok 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | -------------------------------------------------------------------------------- /cleanarch-from-ntier/src/main/java/com/asilva/SpringBootCleanarchApplication.java: -------------------------------------------------------------------------------- 1 | package com.asilva; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class SpringBootCleanarchApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(SpringBootCleanarchApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /cleanarch-from-ntier/src/main/java/com/asilva/application/gateways/UserGateway.java: -------------------------------------------------------------------------------- 1 | package com.cleanarch.application.gateways; 2 | 3 | import com.cleanarch.domain.model.UserModel; 4 | 5 | public interface UserGateway { 6 | 7 | public UserModel create(UserModel user); 8 | 9 | } 10 | -------------------------------------------------------------------------------- /cleanarch-from-ntier/src/main/java/com/asilva/application/service/UserObjectValidation.java: -------------------------------------------------------------------------------- 1 | package com.cleanarch.application.service; 2 | 3 | import com.cleanarch.domain.dto.request.CreateUserRequest; 4 | import com.cleanarch.domain.model.UserModel; 5 | import com.cleanarch.infrastructure.persistence.entity.UserEntity; 6 | 7 | public class UserObjectValidation { 8 | 9 | public String validateUserEntity(UserEntity userEntity) { 10 | if (userEntity.getUsername() == null || userEntity.getUsername().isEmpty()) 11 | return "Username is required"; 12 | 13 | else if (userEntity.getPassword() == null || userEntity.getPassword().isEmpty()) 14 | return "Password is required"; 15 | 16 | else if (userEntity.getEmail() == null || userEntity.getEmail().isEmpty()) 17 | return "Email is required"; 18 | 19 | return null; 20 | } 21 | 22 | public String validateUserModel(UserModel userModel) { 23 | if (userModel.username() == null || userModel.username().isEmpty()) 24 | return "Username is required"; 25 | 26 | else if (userModel.password() == null || userModel.password().isEmpty()) 27 | return "Password is required"; 28 | 29 | else if (userModel.email() == null || userModel.email().isEmpty()) 30 | return "Email is required"; 31 | 32 | return null; 33 | } 34 | 35 | public String validateCreateUserRequest(CreateUserRequest createUserRequest) { 36 | if (createUserRequest.username() == null || createUserRequest.username().isEmpty()) 37 | return "Username is required"; 38 | 39 | else if (createUserRequest.password() == null || createUserRequest.password().isEmpty()) 40 | return "Password is required"; 41 | 42 | else if (createUserRequest.email() == null || createUserRequest.email().isEmpty()) 43 | return "Email is required"; 44 | 45 | return null; 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /cleanarch-from-ntier/src/main/java/com/asilva/application/service/UserService.java: -------------------------------------------------------------------------------- 1 | package com.cleanarch.application.service; 2 | 3 | import com.cleanarch.application.usecases.CreateUser; 4 | import com.cleanarch.domain.dto.response.CreateUserResponse; 5 | import com.cleanarch.domain.model.UserModel; 6 | import com.cleanarch.infrastructure.exception.UserValidationException; 7 | 8 | import lombok.RequiredArgsConstructor; 9 | 10 | @RequiredArgsConstructor 11 | public class UserService { 12 | 13 | private final CreateUser createUser; 14 | private final UserObjectValidation userValidationService; 15 | 16 | public CreateUserResponse create(UserModel user) { 17 | String validationMessage = userValidationService.validateUserModel(user); 18 | 19 | if (validationMessage != null) 20 | throw new UserValidationException(validationMessage); 21 | 22 | UserModel modelObj = createUser.execute(user); 23 | 24 | return new CreateUserResponse(modelObj.username(), modelObj.email()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /cleanarch-from-ntier/src/main/java/com/asilva/application/usecases/CreateUser.java: -------------------------------------------------------------------------------- 1 | package com.cleanarch.application.usecases; 2 | 3 | import com.cleanarch.application.gateways.UserGateway; 4 | import com.cleanarch.domain.model.UserModel; 5 | 6 | import lombok.RequiredArgsConstructor; 7 | 8 | @RequiredArgsConstructor 9 | public class CreateUser { 10 | 11 | private final UserGateway userGateway; 12 | 13 | public UserModel execute(UserModel user) { 14 | return userGateway.create(user); 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /cleanarch-from-ntier/src/main/java/com/asilva/domain/dto/request/CreateUserRequest.java: -------------------------------------------------------------------------------- 1 | package com.cleanarch.domain.dto.request; 2 | 3 | public record CreateUserRequest(String username, String email, String password) { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /cleanarch-from-ntier/src/main/java/com/asilva/domain/dto/response/CreateUserResponse.java: -------------------------------------------------------------------------------- 1 | package com.cleanarch.domain.dto.response; 2 | 3 | public record CreateUserResponse(String username, String email) { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /cleanarch-from-ntier/src/main/java/com/asilva/domain/model/UserModel.java: -------------------------------------------------------------------------------- 1 | package com.cleanarch.domain.model; 2 | 3 | public record UserModel(String username, String password, String email) { 4 | } 5 | -------------------------------------------------------------------------------- /cleanarch-from-ntier/src/main/java/com/asilva/infrastructure/config/DatabaseMigration.java: -------------------------------------------------------------------------------- 1 | package com.cleanarch.infrastructure.config; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.core.io.ClassPathResource; 6 | import org.springframework.core.io.Resource; 7 | import org.springframework.jdbc.datasource.DriverManagerDataSource; 8 | import org.springframework.jdbc.datasource.init.DatabasePopulator; 9 | import org.springframework.jdbc.datasource.init.DatabasePopulatorUtils; 10 | import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator; 11 | 12 | @Configuration 13 | public class DatabaseMigration { 14 | 15 | @Bean 16 | DriverManagerDataSource dataSource() { 17 | DriverManagerDataSource dataSource = new DriverManagerDataSource(); 18 | dataSource.setDriverClassName("org.h2.Driver"); 19 | dataSource.setUrl("jdbc:h2:file:./data/db"); 20 | dataSource.setUsername("sa"); 21 | dataSource.setPassword(""); 22 | 23 | Resource initSchema = new ClassPathResource("schema.sql"); 24 | DatabasePopulator databasePopulator = new ResourceDatabasePopulator(initSchema); 25 | DatabasePopulatorUtils.execute(databasePopulator, dataSource); 26 | 27 | return dataSource; 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /cleanarch-from-ntier/src/main/java/com/asilva/infrastructure/config/UserDependecyInjection.java: -------------------------------------------------------------------------------- 1 | package com.cleanarch.infrastructure.config; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | 6 | import com.cleanarch.application.gateways.UserGateway; 7 | import com.cleanarch.application.service.UserObjectValidation; 8 | import com.cleanarch.application.service.UserService; 9 | import com.cleanarch.application.usecases.CreateUser; 10 | import com.cleanarch.infrastructure.gateways.UserRepositoryGateway; 11 | import com.cleanarch.infrastructure.persistence.repository.UserRepository; 12 | import com.cleanarch.interfaces.mappers.UserDtoMapper; 13 | import com.cleanarch.interfaces.mappers.UserEntityMapper; 14 | 15 | @Configuration 16 | public class UserDependecyInjection { 17 | 18 | @Bean 19 | CreateUser createUser(UserGateway userGateway) { 20 | return new CreateUser(userGateway); 21 | } 22 | 23 | @Bean 24 | UserGateway userGateway(UserRepository userRepository, UserEntityMapper userMapper) { 25 | return new UserRepositoryGateway(userRepository, userMapper); 26 | } 27 | 28 | @Bean 29 | UserEntityMapper userEntityMapper() { 30 | return new UserEntityMapper(); 31 | } 32 | 33 | @Bean 34 | UserDtoMapper userDtoMapper() { 35 | return new UserDtoMapper(); 36 | } 37 | 38 | @Bean 39 | UserObjectValidation userValidationService() { 40 | return new UserObjectValidation(); 41 | } 42 | 43 | @Bean 44 | UserService userService(CreateUser createUser, UserObjectValidation userValidationService) { 45 | return new UserService(createUser, userValidationService); 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /cleanarch-from-ntier/src/main/java/com/asilva/infrastructure/exception/ErrorDetails.java: -------------------------------------------------------------------------------- 1 | package com.cleanarch.infrastructure.exception; 2 | 3 | public record ErrorDetails(String message, Integer statusCode) { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /cleanarch-from-ntier/src/main/java/com/asilva/infrastructure/exception/GlobalExceptionHandler.java: -------------------------------------------------------------------------------- 1 | package com.cleanarch.infrastructure.exception; 2 | 3 | import org.springframework.http.HttpStatus; 4 | import org.springframework.http.ResponseEntity; 5 | import org.springframework.web.bind.annotation.ControllerAdvice; 6 | import org.springframework.web.bind.annotation.ExceptionHandler; 7 | 8 | @ControllerAdvice 9 | public class GlobalExceptionHandler { 10 | 11 | @ExceptionHandler(UserValidationException.class) 12 | public ResponseEntity handleUserValidationException(UserValidationException exception) { 13 | ErrorDetails errorDetails = new ErrorDetails(exception.getMessage(), HttpStatus.BAD_REQUEST.value()); 14 | 15 | return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(errorDetails); 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /cleanarch-from-ntier/src/main/java/com/asilva/infrastructure/exception/UserValidationException.java: -------------------------------------------------------------------------------- 1 | package com.cleanarch.infrastructure.exception; 2 | 3 | public class UserValidationException extends RuntimeException { 4 | 5 | private static final long serialVersionUID = 1L; 6 | 7 | public UserValidationException(String message) { 8 | super(message); 9 | } 10 | 11 | } 12 | -------------------------------------------------------------------------------- /cleanarch-from-ntier/src/main/java/com/asilva/infrastructure/gateways/UserRepositoryGateway.java: -------------------------------------------------------------------------------- 1 | package com.cleanarch.infrastructure.gateways; 2 | 3 | import com.cleanarch.application.gateways.UserGateway; 4 | import com.cleanarch.domain.model.UserModel; 5 | import com.cleanarch.infrastructure.persistence.entity.UserEntity; 6 | import com.cleanarch.infrastructure.persistence.repository.UserRepository; 7 | import com.cleanarch.interfaces.mappers.UserEntityMapper; 8 | 9 | import lombok.RequiredArgsConstructor; 10 | 11 | @RequiredArgsConstructor 12 | public class UserRepositoryGateway implements UserGateway { 13 | 14 | private final UserRepository userRepository; 15 | private final UserEntityMapper userMapper; 16 | 17 | @Override 18 | public UserModel create(UserModel user) { 19 | UserEntity userEntity = userMapper.toEntity(user); 20 | UserEntity saved = userRepository.save(userEntity); 21 | 22 | return userMapper.toDomain(saved); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /cleanarch-from-ntier/src/main/java/com/asilva/infrastructure/persistence/entity/UserEntity.java: -------------------------------------------------------------------------------- 1 | package com.cleanarch.infrastructure.persistence.entity; 2 | 3 | import org.springframework.data.annotation.Id; 4 | import org.springframework.data.relational.core.mapping.Table; 5 | 6 | import lombok.Data; 7 | 8 | @Data 9 | @Table("USER") 10 | public class UserEntity { 11 | 12 | @Id 13 | private Long id; 14 | private String username; 15 | private String password; 16 | private String email; 17 | 18 | } 19 | -------------------------------------------------------------------------------- /cleanarch-from-ntier/src/main/java/com/asilva/infrastructure/persistence/repository/UserRepository.java: -------------------------------------------------------------------------------- 1 | package com.cleanarch.infrastructure.persistence.repository; 2 | 3 | import org.springframework.data.repository.CrudRepository; 4 | 5 | import com.cleanarch.infrastructure.persistence.entity.UserEntity; 6 | 7 | public interface UserRepository extends CrudRepository { 8 | 9 | } 10 | -------------------------------------------------------------------------------- /cleanarch-from-ntier/src/main/java/com/asilva/interfaces/controller/UserController.java: -------------------------------------------------------------------------------- 1 | package com.cleanarch.interfaces.controller; 2 | 3 | import org.springframework.http.HttpStatus; 4 | import org.springframework.http.ResponseEntity; 5 | import org.springframework.web.bind.annotation.PostMapping; 6 | import org.springframework.web.bind.annotation.RequestBody; 7 | import org.springframework.web.bind.annotation.RequestMapping; 8 | import org.springframework.web.bind.annotation.RestController; 9 | 10 | import com.cleanarch.application.service.UserService; 11 | import com.cleanarch.domain.dto.request.CreateUserRequest; 12 | import com.cleanarch.domain.dto.response.CreateUserResponse; 13 | import com.cleanarch.interfaces.mappers.UserDtoMapper; 14 | 15 | import lombok.RequiredArgsConstructor; 16 | 17 | @RestController 18 | @RequestMapping("api/v1/users") 19 | @RequiredArgsConstructor 20 | public class UserController { 21 | 22 | private final UserService userService; 23 | private final UserDtoMapper userDtoMapper; 24 | 25 | @PostMapping 26 | public ResponseEntity create(@RequestBody CreateUserRequest request) { 27 | return ResponseEntity 28 | .status(HttpStatus.CREATED) 29 | .body(userService.create(userDtoMapper.toDomain(request))); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /cleanarch-from-ntier/src/main/java/com/asilva/interfaces/mappers/UserDtoMapper.java: -------------------------------------------------------------------------------- 1 | package com.cleanarch.interfaces.mappers; 2 | 3 | import com.cleanarch.domain.dto.request.CreateUserRequest; 4 | import com.cleanarch.domain.dto.response.CreateUserResponse; 5 | import com.cleanarch.domain.model.UserModel; 6 | 7 | public class UserDtoMapper { 8 | 9 | public CreateUserResponse toResponse(UserModel domainObject) { 10 | CreateUserResponse createUserResponse = new CreateUserResponse( 11 | domainObject.username(), domainObject.email()); 12 | 13 | return createUserResponse; 14 | } 15 | 16 | public UserModel toDomain(CreateUserRequest response) { 17 | UserModel userModel = new UserModel(response.username(), response.password(), response.email()); 18 | 19 | return userModel; 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /cleanarch-from-ntier/src/main/java/com/asilva/interfaces/mappers/UserEntityMapper.java: -------------------------------------------------------------------------------- 1 | package com.cleanarch.interfaces.mappers; 2 | 3 | import com.cleanarch.domain.model.UserModel; 4 | import com.cleanarch.infrastructure.persistence.entity.UserEntity; 5 | 6 | public class UserEntityMapper { 7 | 8 | public UserEntity toEntity(UserModel domainObject) { 9 | UserEntity userEntity = new UserEntity(); 10 | 11 | userEntity.setUsername(domainObject.username()); 12 | userEntity.setPassword(domainObject.password()); 13 | userEntity.setEmail(domainObject.email()); 14 | 15 | return userEntity; 16 | } 17 | 18 | public UserModel toDomain(UserEntity entityObject) { 19 | UserModel userModel = new UserModel( 20 | entityObject.getUsername(), entityObject.getPassword(), 21 | entityObject.getEmail()); 22 | 23 | return userModel; 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /cleanarch-from-ntier/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.h2.console.enabled=true 2 | 3 | spring.datasource.url=jdbc:h2:file:./data/db 4 | spring.datasource.username=sa 5 | spring.datasource.password= 6 | 7 | spring.sql.init.mode=always 8 | 9 | springdoc.api-docs.enabled=true 10 | springdoc.api-docs.path=/v3/api-docs 11 | 12 | springdoc.swagger-ui.enabled=true 13 | springdoc.swagger-ui.path=/ -------------------------------------------------------------------------------- /cleanarch-from-ntier/src/main/resources/schema.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS `user` ( 2 | id bigint AUTO_INCREMENT primary key, 3 | username varchar(255) not null, 4 | password varchar(255) not null, 5 | email varchar(255) not null 6 | ); -------------------------------------------------------------------------------- /cleanarch-from-ntier/src/test/java/com/cleanarch/SpringBootCleanarchApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.cleanarch; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class SpringBootCleanarchApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /cleanarch-from-ntier/src/test/java/com/cleanarch/application/gateways/UserGatewayTest.java: -------------------------------------------------------------------------------- 1 | package com.cleanarch.application.gateways; 2 | 3 | import org.assertj.core.api.Assertions; 4 | import org.junit.jupiter.api.DisplayName; 5 | import org.junit.jupiter.api.Test; 6 | import org.junit.jupiter.api.extension.ExtendWith; 7 | import org.mockito.Mock; 8 | import org.mockito.Mockito; 9 | import org.mockito.junit.jupiter.MockitoExtension; 10 | 11 | import com.cleanarch.domain.model.UserModel; 12 | import com.cleanarch.util.UserCreationUtils; 13 | 14 | @ExtendWith(MockitoExtension.class) 15 | public class UserGatewayTest { 16 | 17 | @Mock 18 | private UserGateway userGateway; 19 | 20 | @Test 21 | @DisplayName("Given a user, when the user is created, then the created user is returned") 22 | public void testUserCreation_WhenUserIsCreated_ThenReturnCreatedUser() { 23 | // Given a user 24 | UserModel user = UserCreationUtils.createDummyUserModel(); 25 | 26 | // When the user is created 27 | Mockito.when(userGateway.create(user)).thenReturn(user); 28 | UserModel returnedUser = userGateway.create(user); 29 | 30 | // Then the created user is returned 31 | Assertions.assertThat(returnedUser).isNotNull(); 32 | Assertions.assertThat(returnedUser.username()).isEqualTo("username"); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /cleanarch-from-ntier/src/test/java/com/cleanarch/application/service/UserServiceTest.java: -------------------------------------------------------------------------------- 1 | package com.cleanarch.application.service; 2 | 3 | import org.assertj.core.api.Assertions; 4 | import org.junit.jupiter.api.DisplayName; 5 | import org.junit.jupiter.api.Test; 6 | import org.junit.jupiter.api.extension.ExtendWith; 7 | import org.mockito.Mock; 8 | import org.mockito.Mockito; 9 | import org.mockito.junit.jupiter.MockitoExtension; 10 | 11 | import com.cleanarch.domain.dto.response.CreateUserResponse; 12 | import com.cleanarch.domain.model.UserModel; 13 | import com.cleanarch.util.UserCreationUtils; 14 | 15 | @ExtendWith(MockitoExtension.class) 16 | public class UserServiceTest { 17 | 18 | @Mock 19 | private UserService userService; 20 | 21 | @Test 22 | @DisplayName("Given a user, when the user is created, then the created user is returned") 23 | public void testUserCreation_WhenUserIsCreated_ThenReturnCreatedUser() { 24 | // Given a user 25 | UserModel user = UserCreationUtils.createDummyUserModel(); 26 | 27 | // When the user is created 28 | Mockito 29 | .when(userService.create(user)) 30 | .thenReturn(new CreateUserResponse(user.username(), user.email())); 31 | CreateUserResponse returnedUser = userService.create(user); 32 | 33 | // Then the created user is returned 34 | Assertions.assertThat(returnedUser).isNotNull(); 35 | Assertions.assertThat(returnedUser.username()).isEqualTo("username"); 36 | Assertions.assertThat(returnedUser.email()).isEqualTo("email"); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /cleanarch-from-ntier/src/test/java/com/cleanarch/application/usecases/CreateUserTest.java: -------------------------------------------------------------------------------- 1 | package com.cleanarch.application.usecases; 2 | 3 | import org.assertj.core.api.Assertions; 4 | import org.junit.jupiter.api.DisplayName; 5 | import org.junit.jupiter.api.Test; 6 | import org.junit.jupiter.api.extension.ExtendWith; 7 | import org.mockito.InjectMocks; 8 | import org.mockito.Mock; 9 | import org.mockito.Mockito; 10 | import org.mockito.junit.jupiter.MockitoExtension; 11 | 12 | import com.cleanarch.application.gateways.UserGateway; 13 | import com.cleanarch.domain.model.UserModel; 14 | import com.cleanarch.util.UserCreationUtils; 15 | 16 | @ExtendWith(MockitoExtension.class) 17 | public class CreateUserTest { 18 | 19 | @Mock 20 | private UserGateway userGateway; 21 | 22 | @InjectMocks 23 | private CreateUser createUser; 24 | 25 | @Test 26 | @DisplayName("Given a user, when the user is created, then the user is returned") 27 | public void testUserCreation_WhenUserIsCreated_ThenReturnCreatedUser() { 28 | // Given a user 29 | UserModel user = UserCreationUtils.createDummyUserModel(); 30 | 31 | // When the user is created 32 | Mockito.when(createUser.execute(user)).thenReturn(user); 33 | createUser.execute(user); 34 | 35 | // Then the created user is returned 36 | Assertions.assertThat(user).isNotNull(); 37 | Assertions.assertThat(user.username()).isEqualTo("username"); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /cleanarch-from-ntier/src/test/java/com/cleanarch/domain/dto/request/CreateUserRequestTest.java: -------------------------------------------------------------------------------- 1 | package com.cleanarch.domain.dto.request; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | import static org.junit.jupiter.api.Assertions.assertNotNull; 5 | 6 | import org.junit.Test; 7 | import org.junit.jupiter.api.DisplayName; 8 | 9 | import com.cleanarch.util.UserCreationUtils; 10 | 11 | public class CreateUserRequestTest { 12 | 13 | @Test 14 | @DisplayName("Given a CreateUserRequest object with username, password and email, when the object is created, then the CreateUserRequest object should not be null") 15 | public void testCreateUserRequest_WhenObjectIsCreated_ThenObjectShouldNotBeNull() { 16 | // Given a CreateUserRequest object with username, password and email 17 | CreateUserRequest createUserRequest = UserCreationUtils.createDummyCreateUserRequest(); 18 | 19 | // Then the CreateUserRequest object should not be null 20 | assertNotNull(createUserRequest); 21 | assertEquals("username", createUserRequest.username()); 22 | assertEquals("password", createUserRequest.password()); 23 | assertEquals("email", createUserRequest.email()); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /cleanarch-from-ntier/src/test/java/com/cleanarch/domain/dto/response/CreateUserResponseTest.java: -------------------------------------------------------------------------------- 1 | package com.cleanarch.domain.dto.response; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | import static org.junit.Assert.assertNotNull; 5 | 6 | import org.junit.Test; 7 | import org.junit.jupiter.api.DisplayName; 8 | 9 | import com.cleanarch.util.UserCreationUtils; 10 | 11 | public class CreateUserResponseTest { 12 | 13 | @Test 14 | @DisplayName("Given a CreateUserResponse object with username and email, when the object is created, then the CreateUserResponse object should not be null") 15 | public void testCreateUserResponse_WhenObjectIsCreated_ThenObjectShouldNotBeNull() { 16 | // Given a CreateUserRequest object with username, password and email 17 | CreateUserResponse createUserResponse = UserCreationUtils.createDummyCreateUserResponse(); 18 | 19 | // Then the CreateUserRequest object should not be null 20 | assertNotNull(createUserResponse); 21 | assertEquals("username", createUserResponse.username()); 22 | assertEquals("email", createUserResponse.email()); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /cleanarch-from-ntier/src/test/java/com/cleanarch/domain/model/UserModelTest.java: -------------------------------------------------------------------------------- 1 | package com.cleanarch.domain.model; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | import static org.junit.jupiter.api.Assertions.assertNotNull; 5 | 6 | import org.junit.jupiter.api.DisplayName; 7 | import org.junit.jupiter.api.Test; 8 | 9 | import com.cleanarch.util.UserCreationUtils; 10 | 11 | public class UserModelTest { 12 | 13 | @Test 14 | @DisplayName("Given a UserModel object with username, password and email, when the object is created, then the UserModel object should not be null") 15 | public void testUserModel_WhenObjectIsCreated_ThenObjectShouldNotBeNull() { 16 | // Given a UserModel object with username, password and email 17 | UserModel userModel = UserCreationUtils.createDummyUserModel(); 18 | 19 | // Then the UserModel object should not be null 20 | assertNotNull(userModel); 21 | assertEquals("username", userModel.username()); 22 | assertEquals("password", userModel.password()); 23 | assertEquals("email", userModel.email()); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /cleanarch-from-ntier/src/test/java/com/cleanarch/infrastructure/exception/ErrorDetailsTest.java: -------------------------------------------------------------------------------- 1 | package com.cleanarch.infrastructure.exception; 2 | 3 | import org.assertj.core.api.Assertions; 4 | import org.junit.jupiter.api.DisplayName; 5 | import org.junit.jupiter.api.Test; 6 | 7 | public class ErrorDetailsTest { 8 | 9 | @Test 10 | @DisplayName("Given an ErrorDetails object, when the object is created, then the ErrorDetails object should not be null") 11 | public void testErrorDetails_WhenObjectIsCreated_ThenObjectShouldNotBeNull() { 12 | // Given an ErrorDetails object with some dummy data 13 | ErrorDetails errorDetails = new ErrorDetails("expectedData", 400); 14 | 15 | // Then the ErrorDetails object should not be null 16 | Assertions.assertThat(errorDetails).isNotNull(); 17 | Assertions.assertThat(errorDetails.statusCode()).isEqualTo(400); 18 | } 19 | 20 | } -------------------------------------------------------------------------------- /cleanarch-from-ntier/src/test/java/com/cleanarch/infrastructure/exception/GlobalExceptionHandlerTest.java: -------------------------------------------------------------------------------- 1 | package com.cleanarch.infrastructure.exception; 2 | 3 | import static org.assertj.core.api.Assertions.assertThat; 4 | import static org.mockito.Mockito.verify; 5 | import static org.mockito.Mockito.when; 6 | 7 | import org.junit.jupiter.api.DisplayName; 8 | import org.junit.jupiter.api.Test; 9 | import org.junit.jupiter.api.extension.ExtendWith; 10 | import org.mockito.InjectMocks; 11 | import org.mockito.Mock; 12 | import org.mockito.junit.jupiter.MockitoExtension; 13 | import org.springframework.http.HttpStatus; 14 | import org.springframework.http.ResponseEntity; 15 | 16 | @ExtendWith(MockitoExtension.class) 17 | public class GlobalExceptionHandlerTest { 18 | 19 | @Mock 20 | private UserValidationException userValidationException; 21 | 22 | @InjectMocks 23 | private GlobalExceptionHandler globalExceptionHandler; 24 | 25 | @Test 26 | @DisplayName("Given a UserValidationException, when the exception is handled, then the error details are returned") 27 | public void testHandleUserValidationException_WhenExceptionIsHandled_ThenReturnErrorDetails() { 28 | // Arrange 29 | String errorMessage = "Validation failed"; 30 | ErrorDetails expectedErrorDetails = new ErrorDetails(errorMessage, HttpStatus.BAD_REQUEST.value()); 31 | 32 | when(userValidationException.getMessage()).thenReturn(errorMessage); 33 | 34 | // Act 35 | ResponseEntity responseEntity = globalExceptionHandler 36 | .handleUserValidationException(userValidationException); 37 | 38 | // Assert 39 | assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.BAD_REQUEST); 40 | assertThat(responseEntity.getBody()).isEqualTo(expectedErrorDetails); 41 | 42 | verify(userValidationException).getMessage(); 43 | } 44 | } -------------------------------------------------------------------------------- /cleanarch-from-ntier/src/test/java/com/cleanarch/infrastructure/exception/UserObjectValidationTest.java: -------------------------------------------------------------------------------- 1 | package com.cleanarch.infrastructure.exception; 2 | 3 | import static org.assertj.core.api.Assertions.assertThat; 4 | 5 | import org.junit.jupiter.api.BeforeEach; 6 | import org.junit.jupiter.api.DisplayName; 7 | import org.junit.jupiter.api.Test; 8 | 9 | import com.cleanarch.application.service.UserObjectValidation; 10 | import com.cleanarch.domain.dto.request.CreateUserRequest; 11 | import com.cleanarch.domain.model.UserModel; 12 | import com.cleanarch.infrastructure.persistence.entity.UserEntity; 13 | 14 | public class UserObjectValidationTest { 15 | 16 | private UserObjectValidation userObjectValidation; 17 | 18 | @BeforeEach 19 | public void setUp() { 20 | userObjectValidation = new UserObjectValidation(); 21 | } 22 | 23 | @Test 24 | @DisplayName("Test validateUserEntity method when username is null") 25 | public void testValidateUserEntity_WhenUsernameIsNull_ReturnErrorMessage() { 26 | // Arrange 27 | UserEntity userEntity = new UserEntity(); 28 | userEntity.setUsername(null); 29 | 30 | // Act 31 | String result = userObjectValidation.validateUserEntity(userEntity); 32 | 33 | // Assert 34 | assertThat(result).isEqualTo("Username is required"); 35 | } 36 | 37 | @Test 38 | @DisplayName("Test validateUserEntity method when username is empty") 39 | public void testValidateUserEntity_WhenUsernameIsEmpty_ReturnErrorMessage() { 40 | // Arrange 41 | UserEntity userEntity = new UserEntity(); 42 | userEntity.setUsername(""); 43 | 44 | // Act 45 | String result = userObjectValidation.validateUserEntity(userEntity); 46 | 47 | // Assert 48 | assertThat(result).isEqualTo("Username is required"); 49 | } 50 | 51 | // Add more test cases for validateUserEntity method 52 | 53 | @Test 54 | @DisplayName("Test validateUserModel method when username is null") 55 | public void testValidateUserModel_WhenUsernameIsNull_ReturnErrorMessage() { 56 | // Arrange 57 | UserModel userModel = new UserModel(null, null, null); 58 | 59 | // Act 60 | String result = userObjectValidation.validateUserModel(userModel); 61 | 62 | // Assert 63 | assertThat(result).isEqualTo("Username is required"); 64 | } 65 | 66 | @Test 67 | @DisplayName("Test validateUserModel method when username is empty") 68 | public void testValidateUserModel_WhenUsernameIsEmpty_ReturnErrorMessage() { 69 | // Arrange 70 | UserModel userModel = new UserModel("", null, null); 71 | 72 | // Act 73 | String result = userObjectValidation.validateUserModel(userModel); 74 | 75 | // Assert 76 | assertThat(result).isEqualTo("Username is required"); 77 | } 78 | 79 | // Add more test cases for validateUserModel method 80 | 81 | @Test 82 | @DisplayName("Test validateCreateUserRequest method when username is null") 83 | public void testValidateCreateUserRequest_WhenUsernameIsNull_ReturnErrorMessage() { 84 | // Arrange 85 | CreateUserRequest createUserRequest = new CreateUserRequest(null, null, null); 86 | 87 | // Act 88 | String result = userObjectValidation.validateCreateUserRequest(createUserRequest); 89 | 90 | // Assert 91 | assertThat(result).isEqualTo("Username is required"); 92 | } 93 | 94 | @Test 95 | @DisplayName("Test validateCreateUserRequest method when username is empty") 96 | public void testValidateCreateUserRequest_WhenUsernameIsEmpty_ReturnErrorMessage() { 97 | // Arrange 98 | CreateUserRequest createUserRequest = new CreateUserRequest("", null, null); 99 | 100 | // Act 101 | String result = userObjectValidation.validateCreateUserRequest(createUserRequest); 102 | 103 | // Assert 104 | assertThat(result).isEqualTo("Username is required"); 105 | } 106 | 107 | // Add more test cases for validateCreateUserRequest method 108 | 109 | } -------------------------------------------------------------------------------- /cleanarch-from-ntier/src/test/java/com/cleanarch/infrastructure/exception/UserValidationExceptionTest.java: -------------------------------------------------------------------------------- 1 | package com.cleanarch.infrastructure.exception; 2 | 3 | import static org.assertj.core.api.Assertions.assertThat; 4 | import static org.mockito.Mockito.verify; 5 | import static org.mockito.Mockito.when; 6 | 7 | import org.junit.jupiter.api.BeforeEach; 8 | import org.junit.jupiter.api.DisplayName; 9 | import org.junit.jupiter.api.Test; 10 | import org.junit.jupiter.api.extension.ExtendWith; 11 | import org.mockito.InjectMocks; 12 | import org.mockito.Mock; 13 | import org.mockito.junit.jupiter.MockitoExtension; 14 | import org.springframework.http.HttpStatus; 15 | import org.springframework.http.ResponseEntity; 16 | 17 | @ExtendWith(MockitoExtension.class) 18 | public class UserValidationExceptionTest { 19 | 20 | @Mock 21 | private UserValidationException userValidationException; 22 | 23 | @InjectMocks 24 | private GlobalExceptionHandler globalExceptionHandler; 25 | 26 | @BeforeEach 27 | public void setup() { 28 | when(userValidationException.getMessage()).thenReturn("Validation failed"); 29 | } 30 | 31 | @Test 32 | @DisplayName("Given a UserValidationException, when the exception is handled, then the error details are returned") 33 | public void testHandleUserValidationException_WhenExceptionIsHandled_ThenReturnErrorDetails() { 34 | // Arrange 35 | String errorMessage = "Validation failed"; 36 | ErrorDetails expectedErrorDetails = new ErrorDetails(errorMessage, HttpStatus.BAD_REQUEST.value()); 37 | 38 | // Act 39 | ResponseEntity responseEntity = globalExceptionHandler 40 | .handleUserValidationException(userValidationException); 41 | 42 | // Assert 43 | assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.BAD_REQUEST); 44 | assertThat(responseEntity.getBody()).isEqualTo(expectedErrorDetails); 45 | 46 | verify(userValidationException).getMessage(); 47 | } 48 | } -------------------------------------------------------------------------------- /cleanarch-from-ntier/src/test/java/com/cleanarch/infrastructure/gateways/UserRepositoryGatewayTest.java: -------------------------------------------------------------------------------- 1 | package com.cleanarch.infrastructure.gateways; 2 | 3 | import org.assertj.core.api.Assertions; 4 | import org.junit.jupiter.api.DisplayName; 5 | import org.junit.jupiter.api.Test; 6 | import org.junit.jupiter.api.extension.ExtendWith; 7 | import org.mockito.Mock; 8 | import org.mockito.Mockito; 9 | import org.mockito.junit.jupiter.MockitoExtension; 10 | 11 | import com.cleanarch.domain.model.UserModel; 12 | 13 | @ExtendWith(MockitoExtension.class) 14 | public class UserRepositoryGatewayTest { 15 | 16 | @Mock 17 | private UserRepositoryGateway userRepositoryGateway; 18 | 19 | @Test 20 | @DisplayName("Given an UserModel object, when the create method is called, then an UserEntity object is returned") 21 | void testCreate_WhenCreateMethodIsCalled_ThenReturnUserEntityObject() { 22 | // given an UserModel object 23 | UserModel userModel = new UserModel("username", "password", "email"); 24 | 25 | // when the create method is called 26 | Mockito.when(userRepositoryGateway.create(userModel)).thenReturn(userModel); 27 | UserModel returnedUserModel = userRepositoryGateway.create(userModel); 28 | 29 | // then an UserEntity object is returned 30 | Assertions.assertThat(returnedUserModel).isNotNull(); 31 | Assertions.assertThat(returnedUserModel.username()).isEqualTo("username"); 32 | Assertions.assertThat(returnedUserModel.password()).isEqualTo("password"); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /cleanarch-from-ntier/src/test/java/com/cleanarch/infrastructure/persistence/entity/UserEntityTest.java: -------------------------------------------------------------------------------- 1 | package com.cleanarch.infrastructure.persistence.entity; 2 | 3 | import org.assertj.core.api.Assertions; 4 | import org.junit.jupiter.api.DisplayName; 5 | import org.junit.jupiter.api.Test; 6 | 7 | import com.cleanarch.util.UserCreationUtils; 8 | 9 | public class UserEntityTest { 10 | 11 | @Test 12 | @DisplayName("Given a UserEntity object with username, password and email, when the object is created, then the UserEntity object should not be null") 13 | void testUserEntity_WhenObjectIsCreated_ThenObjectShouldNotBeNull() { 14 | // given a UserEntity object with username, password and email 15 | // when the object is created 16 | UserEntity userEntity = UserCreationUtils.createDummyUserEntity(); 17 | 18 | // then 19 | Assertions.assertThat(userEntity).isNotNull(); 20 | Assertions.assertThat(userEntity.getUsername()).isEqualTo("username"); 21 | Assertions.assertThat(userEntity.getPassword()).isEqualTo("password"); 22 | Assertions.assertThat(userEntity.getEmail()).isEqualTo("email"); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /cleanarch-from-ntier/src/test/java/com/cleanarch/infrastructure/persistence/repository/UserRepositoryTest.java: -------------------------------------------------------------------------------- 1 | package com.cleanarch.infrastructure.persistence.repository; 2 | 3 | import org.assertj.core.api.Assertions; 4 | import org.junit.jupiter.api.DisplayName; 5 | import org.junit.jupiter.api.Test; 6 | import org.junit.jupiter.api.extension.ExtendWith; 7 | import org.mockito.Mock; 8 | import org.mockito.Mockito; 9 | import org.mockito.junit.jupiter.MockitoExtension; 10 | 11 | import com.cleanarch.infrastructure.persistence.entity.UserEntity; 12 | import com.cleanarch.util.UserCreationUtils; 13 | 14 | @ExtendWith(MockitoExtension.class) 15 | public class UserRepositoryTest { 16 | 17 | @Mock 18 | private UserRepository userRepository; 19 | 20 | @Test 21 | @DisplayName("Given a UserEntity object, when the save method is called, then a UserEntity object is returned") 22 | void testSave_WhenSaveMethodIsCalled_ThenReturnUserEntityObject() { 23 | // given a UserEntity object 24 | UserEntity userEntity = UserCreationUtils.createDummyUserEntity(); 25 | 26 | // when the save method is called 27 | Mockito.when(userRepository.save(userEntity)).thenReturn(userEntity); 28 | 29 | // then 30 | Assertions.assertThat(userRepository.save(userEntity)).isNotNull(); 31 | Assertions.assertThat(userRepository.save(userEntity).getUsername()).isEqualTo("username"); 32 | Assertions.assertThat(userRepository.save(userEntity).getPassword()).isEqualTo("password"); 33 | Assertions.assertThat(userRepository.save(userEntity).getEmail()).isEqualTo("email"); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /cleanarch-from-ntier/src/test/java/com/cleanarch/interfaces/controller/UserControllerTest.java: -------------------------------------------------------------------------------- 1 | package com.cleanarch.interfaces.controller; 2 | 3 | import org.assertj.core.api.Assertions; 4 | import org.junit.jupiter.api.BeforeEach; 5 | import org.junit.jupiter.api.DisplayName; 6 | import org.junit.jupiter.api.Test; 7 | import org.mockito.Mock; 8 | import org.mockito.Mockito; 9 | import org.mockito.MockitoAnnotations; 10 | import org.springframework.http.HttpStatus; 11 | import org.springframework.http.ResponseEntity; 12 | 13 | import com.cleanarch.application.service.UserService; 14 | import com.cleanarch.domain.dto.request.CreateUserRequest; 15 | import com.cleanarch.domain.dto.response.CreateUserResponse; 16 | import com.cleanarch.domain.model.UserModel; 17 | import com.cleanarch.interfaces.mappers.UserDtoMapper; 18 | import com.cleanarch.util.UserCreationUtils; 19 | 20 | public class UserControllerTest { 21 | 22 | @Mock 23 | private UserService userService; 24 | 25 | @Mock 26 | private UserDtoMapper userDtoMapper; 27 | 28 | private UserController userController; 29 | 30 | @BeforeEach 31 | public void setUp() { 32 | MockitoAnnotations.openMocks(this); 33 | userController = new UserController(userService, userDtoMapper); 34 | } 35 | 36 | @Test 37 | @DisplayName("Given a user, when the user is created, then the created user is returned") 38 | public void testUserCreation_WhenUserIsCreated_ThenReturnCreatedUser() { 39 | // Arrange 40 | CreateUserRequest request = UserCreationUtils.createDummyCreateUserRequest(); 41 | UserModel userModel = UserCreationUtils.createDummyUserModel(); 42 | CreateUserResponse expectedResponse = UserCreationUtils.createDummyCreateUserResponse(); 43 | 44 | Mockito.when(userDtoMapper.toDomain(request)).thenReturn(userModel); 45 | Mockito.when(userService.create(userModel)).thenReturn(expectedResponse); 46 | 47 | // Act 48 | ResponseEntity responseEntity = userController.create(request); 49 | 50 | // Assert 51 | Assertions.assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.CREATED); 52 | Assertions.assertThat(responseEntity.getBody()).isEqualTo(expectedResponse); 53 | 54 | Mockito.verify(userDtoMapper).toDomain(request); 55 | Mockito.verify(userService).create(userModel); 56 | } 57 | } -------------------------------------------------------------------------------- /cleanarch-from-ntier/src/test/java/com/cleanarch/interfaces/mappers/UserEntityMapperTest.java: -------------------------------------------------------------------------------- 1 | package com.cleanarch.interfaces.mappers; 2 | 3 | import org.assertj.core.api.Assertions; 4 | import org.junit.jupiter.api.DisplayName; 5 | import org.junit.jupiter.api.Test; 6 | 7 | import com.cleanarch.domain.model.UserModel; 8 | import com.cleanarch.infrastructure.persistence.entity.UserEntity; 9 | import com.cleanarch.util.UserCreationUtils; 10 | 11 | public class UserEntityMapperTest { 12 | 13 | private UserEntityMapper userEntityMapper = new UserEntityMapper(); 14 | 15 | @Test 16 | @DisplayName("Given a UserEntity object, when the object is mapped to UserModel, then the UserModel object should be returned") 17 | void testMapUserModelToUserEntity_WhenObjectIsMappedToUserEntity_ThenUserEntityObjectShouldBeReturned() { 18 | // given a UserModel object with username, password and email 19 | UserModel userModel = new UserModel("username", "password", "email"); 20 | 21 | // when the object is mapped to UserEntity 22 | UserEntity userEntity = userEntityMapper.toEntity(userModel); 23 | 24 | // then 25 | Assertions.assertThat(userEntity).isNotNull(); 26 | Assertions.assertThat(userEntity.getUsername()).isEqualTo(userModel.username()); 27 | Assertions.assertThat(userEntity.getPassword()).isEqualTo(userModel.password()); 28 | Assertions.assertThat(userEntity.getEmail()).isEqualTo(userModel.email()); 29 | } 30 | 31 | @Test 32 | @DisplayName("Given a UserModel object, when the object is mapped to UserEntity, then the UserEntity object should be returned") 33 | void testMapUserEntityToUserModel_WhenObjectIsMappedToUserModel_ThenUserModelObjectShouldBeReturned() { 34 | // given a UserModel object with username, password and email 35 | UserEntity userEntity = UserCreationUtils.createDummyUserEntity(); 36 | 37 | // when the object is mapped to UserEntity 38 | UserModel userModel = userEntityMapper.toDomain(userEntity); 39 | 40 | // then 41 | Assertions.assertThat(userModel).isNotNull(); 42 | Assertions.assertThat(userModel.username()).isEqualTo(userEntity.getUsername()); 43 | Assertions.assertThat(userModel.password()).isEqualTo(userEntity.getPassword()); 44 | Assertions.assertThat(userModel.email()).isEqualTo(userEntity.getEmail()); 45 | } 46 | 47 | @Test 48 | @DisplayName("Given a null UserEntity object, when the object is mapped to UserModel, then the UserModel object should be returned") 49 | void testMapUserModelToUserEntity_WhenObjectIsNull_ThenUserEntityObjectShouldBeReturned() { 50 | // given 51 | UserModel userModel = new UserModel(null, null, null); 52 | 53 | // when 54 | UserEntity userEntity = userEntityMapper.toEntity(userModel); 55 | 56 | // then 57 | Assertions.assertThat(userEntity).isNotNull(); 58 | Assertions.assertThat(userEntity.getUsername()).isNull(); 59 | } 60 | 61 | @Test 62 | @DisplayName("Given a null UserModel object, when the object is mapped to UserEntity, then the UserEntity object should be returned") 63 | void testMapUserEntityToUserModel_WhenObjectIsNull_ThenUserModelObjectShouldBeReturned() { 64 | // given 65 | UserEntity userEntity = new UserEntity(); 66 | 67 | // when 68 | UserModel userModel = userEntityMapper.toDomain(userEntity); 69 | 70 | // then 71 | Assertions.assertThat(userModel).isNotNull(); 72 | Assertions.assertThat(userModel.username()).isNull(); 73 | } 74 | 75 | } -------------------------------------------------------------------------------- /cleanarch-from-ntier/src/test/java/com/cleanarch/util/UserCreationUtils.java: -------------------------------------------------------------------------------- 1 | package com.cleanarch.util; 2 | 3 | import com.cleanarch.domain.dto.request.CreateUserRequest; 4 | import com.cleanarch.domain.dto.response.CreateUserResponse; 5 | import com.cleanarch.domain.model.UserModel; 6 | import com.cleanarch.infrastructure.persistence.entity.UserEntity; 7 | 8 | public class UserCreationUtils { 9 | 10 | public static UserModel createDummyUserModel() { 11 | return new UserModel("username", "password", "email"); 12 | } 13 | 14 | public static UserModel createDummyUserModel(String username, String password, String email) { 15 | return new UserModel(username, password, email); 16 | } 17 | 18 | public static CreateUserResponse createDummyCreateUserResponse() { 19 | return new CreateUserResponse("username", "email"); 20 | } 21 | 22 | public static CreateUserResponse createDummyCreateUserResponse(String username, String email) { 23 | return new CreateUserResponse(username, email); 24 | } 25 | 26 | public static CreateUserRequest createDummyCreateUserRequest() { 27 | return new CreateUserRequest("username", "email", "password"); 28 | } 29 | 30 | public static CreateUserRequest createDummyCreateUserRequest(String username, String password, String email) { 31 | return new CreateUserRequest(username, password, email); 32 | } 33 | 34 | public static UserEntity createDummyUserEntity() { 35 | UserEntity userEntity = new UserEntity(); 36 | userEntity.setUsername("username"); 37 | userEntity.setPassword("password"); 38 | userEntity.setEmail("email"); 39 | 40 | return userEntity; 41 | } 42 | 43 | public static UserEntity createDummyUserEntity(String username, String password, String email) { 44 | UserEntity userEntity = new UserEntity(); 45 | userEntity.setUsername(username); 46 | userEntity.setPassword(password); 47 | userEntity.setEmail(email); 48 | 49 | return userEntity; 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /cleanarch-from-scratch/Dockerfile: -------------------------------------------------------------------------------- 1 | # Purpose: Dockerfile for building cleanarch-from-scratch 2 | # Build: docker build -t cleanarch-from-scratch . 3 | FROM gradle:8.4.0-jdk17 AS build 4 | 5 | COPY src /home/app/src 6 | COPY build.gradle /home/app 7 | COPY settings.gradle /home/app 8 | COPY gradle /home/app/gradle 9 | 10 | WORKDIR /home/app 11 | 12 | RUN gradle build 13 | 14 | 15 | # Purpose: Sequence of commands to run cleanarch-from-scratch 16 | # Run: docker run -p 8080:8080 cleanarch-from-scratch 17 | FROM amazoncorretto:17 18 | 19 | COPY --from=build /home/app/build/libs/spring-boot-clean-architecture-0.0.1-SNAPSHOT.jar /usr/local/lib/app.jar 20 | 21 | EXPOSE 80 22 | 23 | ENTRYPOINT ["java","-jar","/usr/local/lib/app.jar"] 24 | 25 | -------------------------------------------------------------------------------- /cleanarch-from-scratch/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java' 3 | id 'org.springframework.boot' version '3.1.5' 4 | id 'io.spring.dependency-management' version '1.1.3' 5 | } 6 | 7 | group = 'com.asilva' 8 | version = '0.0.1-SNAPSHOT' 9 | 10 | java { 11 | sourceCompatibility = '17' 12 | } 13 | 14 | configurations { 15 | compileOnly { 16 | extendsFrom annotationProcessor 17 | } 18 | } 19 | 20 | repositories { 21 | mavenCentral() 22 | } 23 | 24 | ext { 25 | set('springCloudVersion', "2021.0.4") 26 | } 27 | 28 | dependencies { 29 | implementation 'org.springframework.boot:spring-boot-starter-data-mongodb' 30 | implementation 'org.springframework.boot:spring-boot-starter-validation' 31 | implementation 'org.springframework.boot:spring-boot-starter-web' 32 | implementation 'org.springframework.kafka:spring-kafka' 33 | implementation 'org.springframework.cloud:spring-cloud-starter-openfeign' 34 | implementation 'org.springframework.kafka:spring-kafka' 35 | implementation 'org.springdoc:springdoc-openapi:1.5.12' 36 | implementation 'org.springdoc:springdoc-openapi-webflux-ui:1.5.12' 37 | implementation 'org.mapstruct:mapstruct:1.5.2.Final' 38 | 39 | compileOnly 'org.projectlombok:lombok' 40 | 41 | developmentOnly 'org.springframework.boot:spring-boot-devtools' 42 | 43 | annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor' 44 | annotationProcessor 'org.projectlombok:lombok' 45 | annotationProcessor 'org.mapstruct:mapstruct-processor:1.5.2.Final' 46 | 47 | testImplementation 'org.springframework.boot:spring-boot-starter-test' 48 | testImplementation 'org.springframework.kafka:spring-kafka-test' 49 | } 50 | 51 | dependencyManagement { 52 | imports { 53 | mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}" 54 | } 55 | } 56 | 57 | tasks.named('bootBuildImage') { 58 | builder = 'paketobuildpacks/builder-jammy-base:latest' 59 | } 60 | 61 | tasks.named('test') { 62 | useJUnitPlatform() 63 | } -------------------------------------------------------------------------------- /cleanarch-from-scratch/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3.8" 2 | 3 | services: 4 | cleanarch-from-scratch: 5 | container_name: cleanarch-from-scratch 6 | image: cleanarch-from-scratch:latest 7 | build: 8 | context: ./ 9 | dockerfile: Dockerfile 10 | ports: 11 | - "8080:8080" 12 | restart: on-failure 13 | deploy: 14 | resources: 15 | limits: 16 | cpus: '0.5' 17 | memory: 700M 18 | restart_policy: 19 | condition: on-failure -------------------------------------------------------------------------------- /cleanarch-from-scratch/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AthirsonSilva/spring-boot-clean-architecture/d97d91cc563badad10ea2b8c9981783135db7a28/cleanarch-from-scratch/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /cleanarch-from-scratch/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /cleanarch-from-scratch/gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | # 21 | # Gradle start up script for POSIX generated by Gradle. 22 | # 23 | # Important for running: 24 | # 25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 26 | # noncompliant, but you have some other compliant shell such as ksh or 27 | # bash, then to run this script, type that shell name before the whole 28 | # command line, like: 29 | # 30 | # ksh Gradle 31 | # 32 | # Busybox and similar reduced shells will NOT work, because this script 33 | # requires all of these POSIX shell features: 34 | # * functions; 35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 37 | # * compound commands having a testable exit status, especially «case»; 38 | # * various built-in commands including «command», «set», and «ulimit». 39 | # 40 | # Important for patching: 41 | # 42 | # (2) This script targets any POSIX shell, so it avoids extensions provided 43 | # by Bash, Ksh, etc; in particular arrays are avoided. 44 | # 45 | # The "traditional" practice of packing multiple parameters into a 46 | # space-separated string is a well documented source of bugs and security 47 | # problems, so this is (mostly) avoided, by progressively accumulating 48 | # options in "$@", and eventually passing that to Java. 49 | # 50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 52 | # see the in-line comments for details. 53 | # 54 | # There are tweaks for specific operating systems such as AIX, CygWin, 55 | # Darwin, MinGW, and NonStop. 56 | # 57 | # (3) This script is generated from the Groovy template 58 | # https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 59 | # within the Gradle project. 60 | # 61 | # You can find Gradle at https://github.com/gradle/gradle/. 62 | # 63 | ############################################################################## 64 | 65 | # Attempt to set APP_HOME 66 | 67 | # Resolve links: $0 may be a link 68 | app_path=$0 69 | 70 | # Need this for daisy-chained symlinks. 71 | while 72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 73 | [ -h "$app_path" ] 74 | do 75 | ls=$( ls -ld "$app_path" ) 76 | link=${ls#*' -> '} 77 | case $link in #( 78 | /*) app_path=$link ;; #( 79 | *) app_path=$APP_HOME$link ;; 80 | esac 81 | done 82 | 83 | # This is normally unused 84 | # shellcheck disable=SC2034 85 | APP_BASE_NAME=${0##*/} 86 | # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) 87 | APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit 88 | 89 | # Use the maximum available, or set MAX_FD != -1 to use that value. 90 | MAX_FD=maximum 91 | 92 | warn () { 93 | echo "$*" 94 | } >&2 95 | 96 | die () { 97 | echo 98 | echo "$*" 99 | echo 100 | exit 1 101 | } >&2 102 | 103 | # OS specific support (must be 'true' or 'false'). 104 | cygwin=false 105 | msys=false 106 | darwin=false 107 | nonstop=false 108 | case "$( uname )" in #( 109 | CYGWIN* ) cygwin=true ;; #( 110 | Darwin* ) darwin=true ;; #( 111 | MSYS* | MINGW* ) msys=true ;; #( 112 | NONSTOP* ) nonstop=true ;; 113 | esac 114 | 115 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 116 | 117 | 118 | # Determine the Java command to use to start the JVM. 119 | if [ -n "$JAVA_HOME" ] ; then 120 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 121 | # IBM's JDK on AIX uses strange locations for the executables 122 | JAVACMD=$JAVA_HOME/jre/sh/java 123 | else 124 | JAVACMD=$JAVA_HOME/bin/java 125 | fi 126 | if [ ! -x "$JAVACMD" ] ; then 127 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 128 | 129 | Please set the JAVA_HOME variable in your environment to match the 130 | location of your Java installation." 131 | fi 132 | else 133 | JAVACMD=java 134 | if ! command -v java >/dev/null 2>&1 135 | then 136 | die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 137 | 138 | Please set the JAVA_HOME variable in your environment to match the 139 | location of your Java installation." 140 | fi 141 | fi 142 | 143 | # Increase the maximum file descriptors if we can. 144 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 145 | case $MAX_FD in #( 146 | max*) 147 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. 148 | # shellcheck disable=SC2039,SC3045 149 | MAX_FD=$( ulimit -H -n ) || 150 | warn "Could not query maximum file descriptor limit" 151 | esac 152 | case $MAX_FD in #( 153 | '' | soft) :;; #( 154 | *) 155 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. 156 | # shellcheck disable=SC2039,SC3045 157 | ulimit -n "$MAX_FD" || 158 | warn "Could not set maximum file descriptor limit to $MAX_FD" 159 | esac 160 | fi 161 | 162 | # Collect all arguments for the java command, stacking in reverse order: 163 | # * args from the command line 164 | # * the main class name 165 | # * -classpath 166 | # * -D...appname settings 167 | # * --module-path (only if needed) 168 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 169 | 170 | # For Cygwin or MSYS, switch paths to Windows format before running java 171 | if "$cygwin" || "$msys" ; then 172 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 173 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 174 | 175 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 176 | 177 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 178 | for arg do 179 | if 180 | case $arg in #( 181 | -*) false ;; # don't mess with options #( 182 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 183 | [ -e "$t" ] ;; #( 184 | *) false ;; 185 | esac 186 | then 187 | arg=$( cygpath --path --ignore --mixed "$arg" ) 188 | fi 189 | # Roll the args list around exactly as many times as the number of 190 | # args, so each arg winds up back in the position where it started, but 191 | # possibly modified. 192 | # 193 | # NB: a `for` loop captures its iteration list before it begins, so 194 | # changing the positional parameters here affects neither the number of 195 | # iterations, nor the values presented in `arg`. 196 | shift # remove old arg 197 | set -- "$@" "$arg" # push replacement arg 198 | done 199 | fi 200 | 201 | 202 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 203 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 204 | 205 | # Collect all arguments for the java command: 206 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, 207 | # and any embedded shellness will be escaped. 208 | # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be 209 | # treated as '${Hostname}' itself on the command line. 210 | 211 | set -- \ 212 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 213 | -classpath "$CLASSPATH" \ 214 | org.gradle.wrapper.GradleWrapperMain \ 215 | "$@" 216 | 217 | # Stop when "xargs" is not available. 218 | if ! command -v xargs >/dev/null 2>&1 219 | then 220 | die "xargs is not available" 221 | fi 222 | 223 | # Use "xargs" to parse quoted args. 224 | # 225 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 226 | # 227 | # In Bash we could simply go: 228 | # 229 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 230 | # set -- "${ARGS[@]}" "$@" 231 | # 232 | # but POSIX shell has neither arrays nor command substitution, so instead we 233 | # post-process each arg (as a line of input to sed) to backslash-escape any 234 | # character that might be a shell metacharacter, then use eval to reverse 235 | # that process (while maintaining the separation between arguments), and wrap 236 | # the whole thing up as a single "set" statement. 237 | # 238 | # This will of course break if any of these variables contains a newline or 239 | # an unmatched quote. 240 | # 241 | 242 | eval "set -- $( 243 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 244 | xargs -n1 | 245 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 246 | tr '\n' ' ' 247 | )" '"$@"' 248 | 249 | exec "$JAVACMD" "$@" 250 | -------------------------------------------------------------------------------- /cleanarch-from-scratch/gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | @rem This is normally unused 30 | set APP_BASE_NAME=%~n0 31 | set APP_HOME=%DIRNAME% 32 | 33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 35 | 36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 38 | 39 | @rem Find java.exe 40 | if defined JAVA_HOME goto findJavaFromJavaHome 41 | 42 | set JAVA_EXE=java.exe 43 | %JAVA_EXE% -version >NUL 2>&1 44 | if %ERRORLEVEL% equ 0 goto execute 45 | 46 | echo. 47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 48 | echo. 49 | echo Please set the JAVA_HOME variable in your environment to match the 50 | echo location of your Java installation. 51 | 52 | goto fail 53 | 54 | :findJavaFromJavaHome 55 | set JAVA_HOME=%JAVA_HOME:"=% 56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 57 | 58 | if exist "%JAVA_EXE%" goto execute 59 | 60 | echo. 61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 62 | echo. 63 | echo Please set the JAVA_HOME variable in your environment to match the 64 | echo location of your Java installation. 65 | 66 | goto fail 67 | 68 | :execute 69 | @rem Setup the command line 70 | 71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 72 | 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if %ERRORLEVEL% equ 0 goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | set EXIT_CODE=%ERRORLEVEL% 85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 87 | exit /b %EXIT_CODE% 88 | 89 | :mainEnd 90 | if "%OS%"=="Windows_NT" endlocal 91 | 92 | :omega 93 | -------------------------------------------------------------------------------- /cleanarch-from-scratch/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'cleanarch-from-scratch' -------------------------------------------------------------------------------- /cleanarch-from-scratch/src/main/java/com/asilva/springbootcleanarchitecture/SpringBootCleanArchitectureApplication.java: -------------------------------------------------------------------------------- 1 | package com.asilva.springbootcleanarchitecture; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.ImportAutoConfiguration; 5 | import org.springframework.boot.autoconfigure.SpringBootApplication; 6 | import org.springframework.cloud.commons.httpclient.HttpClientConfiguration; 7 | import org.springframework.cloud.openfeign.EnableFeignClients; 8 | import org.springframework.cloud.openfeign.FeignAutoConfiguration; 9 | import org.springframework.data.mongodb.repository.config.EnableMongoRepositories; 10 | 11 | @SpringBootApplication 12 | @EnableFeignClients 13 | @EnableMongoRepositories 14 | @ImportAutoConfiguration({ FeignAutoConfiguration.class, HttpClientConfiguration.class }) 15 | public class SpringBootCleanArchitectureApplication { 16 | 17 | public static void main(String[] args) { 18 | SpringApplication.run(SpringBootCleanArchitectureApplication.class, args); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /cleanarch-from-scratch/src/main/java/com/asilva/springbootcleanarchitecture/config/DeleteCustomerByIdConfig.java: -------------------------------------------------------------------------------- 1 | package com.asilva.springbootcleanarchitecture.config; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | 6 | import com.asilva.springbootcleanarchitecture.core.usecase.DeleteCustomerByIdUseCase; 7 | import com.asilva.springbootcleanarchitecture.core.usecase.FindCustomerByIdUseCase; 8 | import com.asilva.springbootcleanarchitecture.core.usecase.impl.DeleteCustomerByIdUseCaseImpl; 9 | import com.asilva.springbootcleanarchitecture.dataprovider.impl.DeleteCustomerByIdImpl; 10 | 11 | @Configuration 12 | public class DeleteCustomerByIdConfig { 13 | 14 | @Bean 15 | protected DeleteCustomerByIdUseCase deleteCustomerById(DeleteCustomerByIdImpl deleteCustomerByIdImpl, 16 | FindCustomerByIdUseCase findCustomerByIdUseCase) { 17 | return new DeleteCustomerByIdUseCaseImpl(findCustomerByIdUseCase, deleteCustomerByIdImpl); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /cleanarch-from-scratch/src/main/java/com/asilva/springbootcleanarchitecture/config/FindCustomerByIdConfig.java: -------------------------------------------------------------------------------- 1 | package com.asilva.springbootcleanarchitecture.config; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | 6 | import com.asilva.springbootcleanarchitecture.core.usecase.FindCustomerByIdUseCase; 7 | import com.asilva.springbootcleanarchitecture.core.usecase.impl.FindCustomerByIdUseCaseImpl; 8 | import com.asilva.springbootcleanarchitecture.dataprovider.impl.FindCustomerByIdImpl; 9 | 10 | @Configuration 11 | public class FindCustomerByIdConfig { 12 | 13 | @Bean 14 | protected FindCustomerByIdUseCase findCustomerById(FindCustomerByIdImpl findCustomerByIdImpl) { 15 | return new FindCustomerByIdUseCaseImpl(findCustomerByIdImpl); 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /cleanarch-from-scratch/src/main/java/com/asilva/springbootcleanarchitecture/config/InsertCustomerConfig.java: -------------------------------------------------------------------------------- 1 | package com.asilva.springbootcleanarchitecture.config; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | 6 | import com.asilva.springbootcleanarchitecture.core.usecase.InsertCustomerUseCase; 7 | import com.asilva.springbootcleanarchitecture.core.usecase.impl.InsertCustomerUseCaseImpl; 8 | import com.asilva.springbootcleanarchitecture.dataprovider.impl.FindAddressByZipCodeImpl; 9 | import com.asilva.springbootcleanarchitecture.dataprovider.impl.InsertCustomerImpl; 10 | import com.asilva.springbootcleanarchitecture.dataprovider.impl.SendCpfForValidationImpl; 11 | 12 | @Configuration 13 | public class InsertCustomerConfig { 14 | 15 | @Bean 16 | protected InsertCustomerUseCase insertCustomer( 17 | FindAddressByZipCodeImpl findAddressByZipCodeImpl, 18 | InsertCustomerImpl insertCustomerImpl, 19 | SendCpfForValidationImpl sendCpfForValidationImpl) { 20 | return new InsertCustomerUseCaseImpl(findAddressByZipCodeImpl, insertCustomerImpl, sendCpfForValidationImpl); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /cleanarch-from-scratch/src/main/java/com/asilva/springbootcleanarchitecture/config/KafkaConsumerConfig.java: -------------------------------------------------------------------------------- 1 | package com.asilva.springbootcleanarchitecture.config; 2 | 3 | import static org.apache.kafka.clients.consumer.ConsumerConfig.AUTO_OFFSET_RESET_CONFIG; 4 | import static org.apache.kafka.clients.consumer.ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG; 5 | import static org.apache.kafka.clients.consumer.ConsumerConfig.GROUP_ID_CONFIG; 6 | import static org.apache.kafka.clients.consumer.ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG; 7 | import static org.apache.kafka.clients.consumer.ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG; 8 | 9 | import java.util.HashMap; 10 | 11 | import org.apache.kafka.common.serialization.StringDeserializer; 12 | import org.springframework.context.annotation.Bean; 13 | import org.springframework.context.annotation.Configuration; 14 | import org.springframework.kafka.annotation.EnableKafka; 15 | import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory; 16 | import org.springframework.kafka.core.ConsumerFactory; 17 | import org.springframework.kafka.core.DefaultKafkaConsumerFactory; 18 | import org.springframework.kafka.support.serializer.JsonDeserializer; 19 | 20 | import com.asilva.springbootcleanarchitecture.entrypoint.consumer.message.CustomerMessage; 21 | 22 | @EnableKafka 23 | @Configuration 24 | public class KafkaConsumerConfig { 25 | @Bean 26 | protected ConsumerFactory consumerFactory() { 27 | HashMap props = new HashMap<>(); 28 | props.put(BOOTSTRAP_SERVERS_CONFIG, "localhost:9092"); 29 | props.put(GROUP_ID_CONFIG, "cleanarch"); 30 | props.put(KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); 31 | props.put(VALUE_DESERIALIZER_CLASS_CONFIG, JsonDeserializer.class); 32 | props.put(AUTO_OFFSET_RESET_CONFIG, "earliest"); 33 | 34 | return new DefaultKafkaConsumerFactory<>(props, new StringDeserializer(), 35 | new JsonDeserializer<>(CustomerMessage.class)); 36 | } 37 | 38 | @Bean 39 | protected ConcurrentKafkaListenerContainerFactory kafkaListenerContainerFactory() { 40 | ConcurrentKafkaListenerContainerFactory factory = new ConcurrentKafkaListenerContainerFactory<>(); 41 | factory.setConsumerFactory(consumerFactory()); 42 | return factory; 43 | } 44 | } -------------------------------------------------------------------------------- /cleanarch-from-scratch/src/main/java/com/asilva/springbootcleanarchitecture/config/KafkaProducerConfig.java: -------------------------------------------------------------------------------- 1 | package com.asilva.springbootcleanarchitecture.config; 2 | 3 | import static org.apache.kafka.clients.consumer.ConsumerConfig.GROUP_ID_CONFIG; 4 | import static org.apache.kafka.clients.producer.ProducerConfig.BOOTSTRAP_SERVERS_CONFIG; 5 | import static org.apache.kafka.clients.producer.ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG; 6 | import static org.apache.kafka.clients.producer.ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG; 7 | 8 | import java.util.HashMap; 9 | 10 | import org.apache.kafka.common.serialization.StringSerializer; 11 | import org.springframework.context.annotation.Bean; 12 | import org.springframework.context.annotation.Configuration; 13 | import org.springframework.kafka.core.DefaultKafkaProducerFactory; 14 | import org.springframework.kafka.core.KafkaTemplate; 15 | import org.springframework.kafka.core.ProducerFactory; 16 | 17 | @Configuration 18 | public class KafkaProducerConfig { 19 | 20 | @Bean 21 | protected ProducerFactory producerFactory() { 22 | HashMap config = new HashMap<>(); 23 | config.put(BOOTSTRAP_SERVERS_CONFIG, "localhost:9092"); 24 | config.put(GROUP_ID_CONFIG, "cleanarch"); 25 | config.put(KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class); 26 | config.put(VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class); 27 | 28 | return new DefaultKafkaProducerFactory<>(config); 29 | } 30 | 31 | @Bean 32 | protected KafkaTemplate kafkaTemplate() { 33 | return new KafkaTemplate<>(producerFactory()); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /cleanarch-from-scratch/src/main/java/com/asilva/springbootcleanarchitecture/config/UpdateCustomerByIdConfig.java: -------------------------------------------------------------------------------- 1 | package com.asilva.springbootcleanarchitecture.config; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | 6 | import com.asilva.springbootcleanarchitecture.core.usecase.FindCustomerByIdUseCase; 7 | import com.asilva.springbootcleanarchitecture.core.usecase.UpdateCustomerByIdUseCase; 8 | import com.asilva.springbootcleanarchitecture.core.usecase.impl.UpdateCustomerByIdUseCaseImpl; 9 | import com.asilva.springbootcleanarchitecture.dataprovider.impl.FindAddressByZipCodeImpl; 10 | import com.asilva.springbootcleanarchitecture.dataprovider.impl.UpdateCustomerByIdImpl; 11 | 12 | @Configuration 13 | public class UpdateCustomerByIdConfig { 14 | 15 | @Bean 16 | protected UpdateCustomerByIdUseCase updateCustomerById( 17 | FindCustomerByIdUseCase findCustomerByIdUseCase, 18 | FindAddressByZipCodeImpl findAddressByZipCodeImpl, 19 | UpdateCustomerByIdImpl updateCustomerByIdImpl) { 20 | return new UpdateCustomerByIdUseCaseImpl(findCustomerByIdUseCase, findAddressByZipCodeImpl, updateCustomerByIdImpl); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /cleanarch-from-scratch/src/main/java/com/asilva/springbootcleanarchitecture/core/dataprovider/DeleteCustomerById.java: -------------------------------------------------------------------------------- 1 | package com.asilva.springbootcleanarchitecture.core.dataprovider; 2 | 3 | public interface DeleteCustomerById { 4 | 5 | public void delete(final String id); 6 | 7 | } 8 | -------------------------------------------------------------------------------- /cleanarch-from-scratch/src/main/java/com/asilva/springbootcleanarchitecture/core/dataprovider/FindAddressByZipCode.java: -------------------------------------------------------------------------------- 1 | package com.asilva.springbootcleanarchitecture.core.dataprovider; 2 | 3 | import com.asilva.springbootcleanarchitecture.core.domain.Address; 4 | 5 | public interface FindAddressByZipCode { 6 | 7 | public Address find(final String zipCode); 8 | 9 | } 10 | -------------------------------------------------------------------------------- /cleanarch-from-scratch/src/main/java/com/asilva/springbootcleanarchitecture/core/dataprovider/FindCustomerById.java: -------------------------------------------------------------------------------- 1 | package com.asilva.springbootcleanarchitecture.core.dataprovider; 2 | 3 | import java.util.Optional; 4 | 5 | import com.asilva.springbootcleanarchitecture.core.domain.Customer; 6 | 7 | public interface FindCustomerById { 8 | 9 | public Optional findById(final String id); 10 | 11 | } 12 | -------------------------------------------------------------------------------- /cleanarch-from-scratch/src/main/java/com/asilva/springbootcleanarchitecture/core/dataprovider/InsertCustomer.java: -------------------------------------------------------------------------------- 1 | package com.asilva.springbootcleanarchitecture.core.dataprovider; 2 | 3 | import com.asilva.springbootcleanarchitecture.core.domain.Customer; 4 | 5 | public interface InsertCustomer { 6 | 7 | public void insert(Customer customer, String cep); 8 | 9 | } 10 | -------------------------------------------------------------------------------- /cleanarch-from-scratch/src/main/java/com/asilva/springbootcleanarchitecture/core/dataprovider/SendCpForValidation.java: -------------------------------------------------------------------------------- 1 | package com.asilva.springbootcleanarchitecture.core.dataprovider; 2 | 3 | public interface SendCpForValidation { 4 | 5 | public void send(String cpf); 6 | 7 | } 8 | -------------------------------------------------------------------------------- /cleanarch-from-scratch/src/main/java/com/asilva/springbootcleanarchitecture/core/dataprovider/UpdateCustomerById.java: -------------------------------------------------------------------------------- 1 | package com.asilva.springbootcleanarchitecture.core.dataprovider; 2 | 3 | import com.asilva.springbootcleanarchitecture.core.domain.Customer; 4 | 5 | public interface UpdateCustomerById { 6 | 7 | public void update(final Customer customer); 8 | 9 | } 10 | -------------------------------------------------------------------------------- /cleanarch-from-scratch/src/main/java/com/asilva/springbootcleanarchitecture/core/domain/Address.java: -------------------------------------------------------------------------------- 1 | package com.asilva.springbootcleanarchitecture.core.domain; 2 | 3 | public class Address { 4 | 5 | private String street; 6 | private String city; 7 | private String state; 8 | 9 | public String getStreet() { 10 | return street; 11 | } 12 | 13 | public void setStreet(String street) { 14 | this.street = street; 15 | } 16 | 17 | public String getCity() { 18 | return city; 19 | } 20 | 21 | public void setCity(String city) { 22 | this.city = city; 23 | } 24 | 25 | public String getState() { 26 | return state; 27 | } 28 | 29 | public void setState(String state) { 30 | this.state = state; 31 | } 32 | 33 | public Address() { 34 | } 35 | 36 | public Address(String street, String city, String state) { 37 | this.street = street; 38 | this.city = city; 39 | this.state = state; 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /cleanarch-from-scratch/src/main/java/com/asilva/springbootcleanarchitecture/core/domain/Customer.java: -------------------------------------------------------------------------------- 1 | package com.asilva.springbootcleanarchitecture.core.domain; 2 | 3 | public class Customer { 4 | 5 | private String id; 6 | private String name; 7 | private String cpf; 8 | private Address address; 9 | private Boolean isValidCpf; 10 | 11 | public String getId() { 12 | return id; 13 | } 14 | 15 | public void setId(String id) { 16 | this.id = id; 17 | } 18 | 19 | public String getName() { 20 | return name; 21 | } 22 | 23 | public void setName(String name) { 24 | this.name = name; 25 | } 26 | 27 | public String getCpf() { 28 | return cpf; 29 | } 30 | 31 | public void setCpf(String cpf) { 32 | this.cpf = cpf; 33 | } 34 | 35 | public Address getAddress() { 36 | return address; 37 | } 38 | 39 | public void setAddress(Address address) { 40 | this.address = address; 41 | } 42 | 43 | public Boolean getIsValidCpf() { 44 | return isValidCpf; 45 | } 46 | 47 | public void setIsValidCpf(Boolean isValidCpf) { 48 | this.isValidCpf = isValidCpf; 49 | } 50 | 51 | public Customer() { 52 | this.isValidCpf = false; 53 | } 54 | 55 | public Customer(String id, String name, String cpf, Address address, Boolean isValidCpf) { 56 | this.id = id; 57 | this.name = name; 58 | this.cpf = cpf; 59 | this.address = address; 60 | this.isValidCpf = isValidCpf; 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /cleanarch-from-scratch/src/main/java/com/asilva/springbootcleanarchitecture/core/usecase/DeleteCustomerByIdUseCase.java: -------------------------------------------------------------------------------- 1 | package com.asilva.springbootcleanarchitecture.core.usecase; 2 | 3 | public interface DeleteCustomerByIdUseCase { 4 | 5 | public void delete(final String id); 6 | 7 | } 8 | -------------------------------------------------------------------------------- /cleanarch-from-scratch/src/main/java/com/asilva/springbootcleanarchitecture/core/usecase/FindCustomerByIdUseCase.java: -------------------------------------------------------------------------------- 1 | package com.asilva.springbootcleanarchitecture.core.usecase; 2 | 3 | import com.asilva.springbootcleanarchitecture.core.domain.Customer; 4 | 5 | public interface FindCustomerByIdUseCase { 6 | 7 | public Customer findById(final String id); 8 | 9 | } 10 | -------------------------------------------------------------------------------- /cleanarch-from-scratch/src/main/java/com/asilva/springbootcleanarchitecture/core/usecase/InsertCustomerUseCase.java: -------------------------------------------------------------------------------- 1 | package com.asilva.springbootcleanarchitecture.core.usecase; 2 | 3 | import com.asilva.springbootcleanarchitecture.core.domain.Customer; 4 | 5 | public interface InsertCustomerUseCase { 6 | 7 | public void insert(Customer customer, String cep); 8 | 9 | } 10 | -------------------------------------------------------------------------------- /cleanarch-from-scratch/src/main/java/com/asilva/springbootcleanarchitecture/core/usecase/UpdateCustomerByIdUseCase.java: -------------------------------------------------------------------------------- 1 | package com.asilva.springbootcleanarchitecture.core.usecase; 2 | 3 | import com.asilva.springbootcleanarchitecture.core.domain.Customer; 4 | 5 | public interface UpdateCustomerByIdUseCase { 6 | 7 | public void update(final String cep, final Customer customer); 8 | 9 | } 10 | -------------------------------------------------------------------------------- /cleanarch-from-scratch/src/main/java/com/asilva/springbootcleanarchitecture/core/usecase/impl/DeleteCustomerByIdUseCaseImpl.java: -------------------------------------------------------------------------------- 1 | package com.asilva.springbootcleanarchitecture.core.usecase.impl; 2 | 3 | import com.asilva.springbootcleanarchitecture.core.dataprovider.DeleteCustomerById; 4 | import com.asilva.springbootcleanarchitecture.core.usecase.DeleteCustomerByIdUseCase; 5 | import com.asilva.springbootcleanarchitecture.core.usecase.FindCustomerByIdUseCase; 6 | 7 | public class DeleteCustomerByIdUseCaseImpl implements DeleteCustomerByIdUseCase { 8 | 9 | private final FindCustomerByIdUseCase findCustomerByIdUseCase; 10 | private final DeleteCustomerById deleteCustomerById; 11 | 12 | public DeleteCustomerByIdUseCaseImpl( 13 | final FindCustomerByIdUseCase findCustomerByIdUseCase, 14 | final DeleteCustomerById deleteCustomerById) { 15 | this.findCustomerByIdUseCase = findCustomerByIdUseCase; 16 | this.deleteCustomerById = deleteCustomerById; 17 | } 18 | 19 | @Override 20 | public void delete(String id) { 21 | findCustomerByIdUseCase.findById(id); 22 | deleteCustomerById.delete(id); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /cleanarch-from-scratch/src/main/java/com/asilva/springbootcleanarchitecture/core/usecase/impl/FindCustomerByIdUseCaseImpl.java: -------------------------------------------------------------------------------- 1 | package com.asilva.springbootcleanarchitecture.core.usecase.impl; 2 | 3 | import com.asilva.springbootcleanarchitecture.core.dataprovider.FindCustomerById; 4 | import com.asilva.springbootcleanarchitecture.core.domain.Customer; 5 | import com.asilva.springbootcleanarchitecture.core.usecase.FindCustomerByIdUseCase; 6 | 7 | public class FindCustomerByIdUseCaseImpl implements FindCustomerByIdUseCase { 8 | 9 | private final FindCustomerById findCustomerById; 10 | 11 | public FindCustomerByIdUseCaseImpl(final FindCustomerById findCustomerById) { 12 | this.findCustomerById = findCustomerById; 13 | } 14 | 15 | @Override 16 | public Customer findById(String id) { 17 | return findCustomerById.findById(id) 18 | .orElseThrow(() -> new RuntimeException("Customer not found")); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /cleanarch-from-scratch/src/main/java/com/asilva/springbootcleanarchitecture/core/usecase/impl/InsertCustomerUseCaseImpl.java: -------------------------------------------------------------------------------- 1 | package com.asilva.springbootcleanarchitecture.core.usecase.impl; 2 | 3 | import com.asilva.springbootcleanarchitecture.core.dataprovider.FindAddressByZipCode; 4 | import com.asilva.springbootcleanarchitecture.core.dataprovider.InsertCustomer; 5 | import com.asilva.springbootcleanarchitecture.core.dataprovider.SendCpForValidation; 6 | import com.asilva.springbootcleanarchitecture.core.domain.Address; 7 | import com.asilva.springbootcleanarchitecture.core.domain.Customer; 8 | import com.asilva.springbootcleanarchitecture.core.usecase.InsertCustomerUseCase; 9 | 10 | public class InsertCustomerUseCaseImpl implements InsertCustomerUseCase { 11 | 12 | private final FindAddressByZipCode findAddressByZipCode; 13 | private final InsertCustomer insertCustomer; 14 | private final SendCpForValidation sendCpForValidation; 15 | 16 | public InsertCustomerUseCaseImpl(FindAddressByZipCode findAddressByZipCode, 17 | InsertCustomer insertCustomer, 18 | SendCpForValidation sendCpForValidation) { 19 | this.findAddressByZipCode = findAddressByZipCode; 20 | this.insertCustomer = insertCustomer; 21 | this.sendCpForValidation = sendCpForValidation; 22 | } 23 | 24 | @Override 25 | public void insert(Customer customer, String cep) { 26 | Address address = findAddressByZipCode.find(cep); 27 | customer.setAddress(address); 28 | insertCustomer.insert(customer, cep); 29 | sendCpForValidation.send(customer.getCpf()); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /cleanarch-from-scratch/src/main/java/com/asilva/springbootcleanarchitecture/core/usecase/impl/UpdateCustomerByIdUseCaseImpl.java: -------------------------------------------------------------------------------- 1 | package com.asilva.springbootcleanarchitecture.core.usecase.impl; 2 | 3 | import com.asilva.springbootcleanarchitecture.core.dataprovider.FindAddressByZipCode; 4 | import com.asilva.springbootcleanarchitecture.core.dataprovider.UpdateCustomerById; 5 | import com.asilva.springbootcleanarchitecture.core.domain.Address; 6 | import com.asilva.springbootcleanarchitecture.core.domain.Customer; 7 | import com.asilva.springbootcleanarchitecture.core.usecase.FindCustomerByIdUseCase; 8 | import com.asilva.springbootcleanarchitecture.core.usecase.UpdateCustomerByIdUseCase; 9 | 10 | public class UpdateCustomerByIdUseCaseImpl implements UpdateCustomerByIdUseCase { 11 | 12 | private final FindCustomerByIdUseCase findCustomerByIdUseCase; 13 | private final FindAddressByZipCode findAddressByZipCode; 14 | private final UpdateCustomerById updateCustomer; 15 | 16 | public UpdateCustomerByIdUseCaseImpl( 17 | final FindCustomerByIdUseCase findCustomerByIdUseCase, 18 | final FindAddressByZipCode findAddressByZipCode, 19 | final UpdateCustomerById updateCustomer) { 20 | this.findCustomerByIdUseCase = findCustomerByIdUseCase; 21 | this.findAddressByZipCode = findAddressByZipCode; 22 | this.updateCustomer = updateCustomer; 23 | } 24 | 25 | @Override 26 | public void update(String cep, Customer customer) { 27 | Customer foundCustomer = findCustomerByIdUseCase.findById(customer.getId()); 28 | Address foundAddress = findAddressByZipCode.find(cep); 29 | 30 | foundCustomer.setAddress(foundAddress); 31 | foundCustomer.setName(customer.getName()); 32 | foundCustomer.setCpf(customer.getCpf()); 33 | 34 | updateCustomer.update(foundCustomer); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /cleanarch-from-scratch/src/main/java/com/asilva/springbootcleanarchitecture/dataprovider/client/FindAddressByZipCodeClient.java: -------------------------------------------------------------------------------- 1 | package com.asilva.springbootcleanarchitecture.dataprovider.client; 2 | 3 | import org.springframework.cloud.openfeign.FeignClient; 4 | import org.springframework.web.bind.annotation.GetMapping; 5 | import org.springframework.web.bind.annotation.PathVariable; 6 | 7 | import com.asilva.springbootcleanarchitecture.entrypoint.dto.response.AddressResponseDto; 8 | 9 | @FeignClient(name = "findAddressByZipCodeClient", url = "${asilva.client.address.url}") 10 | public interface FindAddressByZipCodeClient { 11 | 12 | @GetMapping(value = "/{zipCode}/json") 13 | public AddressResponseDto find(@PathVariable("zipCode") String zipCode); 14 | 15 | } 16 | -------------------------------------------------------------------------------- /cleanarch-from-scratch/src/main/java/com/asilva/springbootcleanarchitecture/dataprovider/client/mapper/AddressResponseMapper.java: -------------------------------------------------------------------------------- 1 | package com.asilva.springbootcleanarchitecture.dataprovider.client.mapper; 2 | 3 | import org.mapstruct.Mapper; 4 | 5 | import com.asilva.springbootcleanarchitecture.core.domain.Address; 6 | import com.asilva.springbootcleanarchitecture.entrypoint.dto.response.AddressResponseDto; 7 | 8 | @Mapper(componentModel = "spring") 9 | public interface AddressResponseMapper { 10 | 11 | public Address toDomain(final AddressResponseDto addressResponse); 12 | 13 | } 14 | -------------------------------------------------------------------------------- /cleanarch-from-scratch/src/main/java/com/asilva/springbootcleanarchitecture/dataprovider/impl/DeleteCustomerByIdImpl.java: -------------------------------------------------------------------------------- 1 | package com.asilva.springbootcleanarchitecture.dataprovider.impl; 2 | 3 | import org.springframework.stereotype.Component; 4 | 5 | import com.asilva.springbootcleanarchitecture.core.dataprovider.DeleteCustomerById; 6 | import com.asilva.springbootcleanarchitecture.dataprovider.persistence.repository.CustomerRepository; 7 | 8 | import lombok.RequiredArgsConstructor; 9 | 10 | @Component 11 | @RequiredArgsConstructor 12 | public class DeleteCustomerByIdImpl implements DeleteCustomerById { 13 | 14 | private final CustomerRepository customerRepository; 15 | 16 | @Override 17 | public void delete(String id) { 18 | customerRepository.deleteById(id); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /cleanarch-from-scratch/src/main/java/com/asilva/springbootcleanarchitecture/dataprovider/impl/FindAddressByZipCodeImpl.java: -------------------------------------------------------------------------------- 1 | package com.asilva.springbootcleanarchitecture.dataprovider.impl; 2 | 3 | import org.springframework.stereotype.Component; 4 | 5 | import com.asilva.springbootcleanarchitecture.core.dataprovider.FindAddressByZipCode; 6 | import com.asilva.springbootcleanarchitecture.core.domain.Address; 7 | import com.asilva.springbootcleanarchitecture.dataprovider.client.FindAddressByZipCodeClient; 8 | import com.asilva.springbootcleanarchitecture.dataprovider.client.mapper.AddressResponseMapper; 9 | import com.asilva.springbootcleanarchitecture.entrypoint.dto.response.AddressResponseDto; 10 | 11 | import lombok.RequiredArgsConstructor; 12 | 13 | @Component 14 | @RequiredArgsConstructor 15 | public class FindAddressByZipCodeImpl implements FindAddressByZipCode { 16 | 17 | private final FindAddressByZipCodeClient findAddressByZipCodeClient; 18 | private final AddressResponseMapper addressResponseMapper; 19 | 20 | @Override 21 | public Address find(String zipCode) { 22 | AddressResponseDto addressResponse = findAddressByZipCodeClient.find(zipCode); 23 | return addressResponseMapper.toDomain(addressResponse); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /cleanarch-from-scratch/src/main/java/com/asilva/springbootcleanarchitecture/dataprovider/impl/FindCustomerByIdImpl.java: -------------------------------------------------------------------------------- 1 | package com.asilva.springbootcleanarchitecture.dataprovider.impl; 2 | 3 | import java.util.Optional; 4 | 5 | import org.springframework.stereotype.Component; 6 | 7 | import com.asilva.springbootcleanarchitecture.core.dataprovider.FindCustomerById; 8 | import com.asilva.springbootcleanarchitecture.core.domain.Customer; 9 | import com.asilva.springbootcleanarchitecture.dataprovider.persistence.entity.CustomerEntity; 10 | import com.asilva.springbootcleanarchitecture.dataprovider.persistence.mapper.CustomerEntityMapper; 11 | import com.asilva.springbootcleanarchitecture.dataprovider.persistence.repository.CustomerRepository; 12 | 13 | import lombok.RequiredArgsConstructor; 14 | 15 | @Component 16 | @RequiredArgsConstructor 17 | public class FindCustomerByIdImpl implements FindCustomerById { 18 | 19 | private final CustomerRepository customerRepository; 20 | private final CustomerEntityMapper customerEntityMapper; 21 | 22 | @Override 23 | public Optional findById(String id) { 24 | Optional customer = customerRepository.findById(id); 25 | return customer.map(customerEntityMapper::toDomain); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /cleanarch-from-scratch/src/main/java/com/asilva/springbootcleanarchitecture/dataprovider/impl/InsertCustomerImpl.java: -------------------------------------------------------------------------------- 1 | package com.asilva.springbootcleanarchitecture.dataprovider.impl; 2 | 3 | import org.springframework.stereotype.Component; 4 | 5 | import com.asilva.springbootcleanarchitecture.core.dataprovider.InsertCustomer; 6 | import com.asilva.springbootcleanarchitecture.core.domain.Customer; 7 | import com.asilva.springbootcleanarchitecture.dataprovider.persistence.entity.CustomerEntity; 8 | import com.asilva.springbootcleanarchitecture.dataprovider.persistence.mapper.CustomerEntityMapper; 9 | import com.asilva.springbootcleanarchitecture.dataprovider.persistence.repository.CustomerRepository; 10 | 11 | import lombok.RequiredArgsConstructor; 12 | 13 | @Component 14 | @RequiredArgsConstructor 15 | public class InsertCustomerImpl implements InsertCustomer { 16 | 17 | private final CustomerRepository customerRepository; 18 | private final CustomerEntityMapper customerEntityMapper; 19 | 20 | @Override 21 | public void insert(Customer customer, String cep) { 22 | CustomerEntity customerEntity = customerEntityMapper.toEntity(customer); 23 | customerRepository.save(customerEntity); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /cleanarch-from-scratch/src/main/java/com/asilva/springbootcleanarchitecture/dataprovider/impl/SendCpfForValidationImpl.java: -------------------------------------------------------------------------------- 1 | package com.asilva.springbootcleanarchitecture.dataprovider.impl; 2 | 3 | import org.springframework.kafka.core.KafkaTemplate; 4 | import org.springframework.stereotype.Component; 5 | 6 | import com.asilva.springbootcleanarchitecture.core.dataprovider.SendCpForValidation; 7 | 8 | import lombok.RequiredArgsConstructor; 9 | 10 | @Component 11 | @RequiredArgsConstructor 12 | public class SendCpfForValidationImpl implements SendCpForValidation { 13 | 14 | private final KafkaTemplate kafkaTemplate; 15 | 16 | @Override 17 | public void send(String cpf) { 18 | kafkaTemplate.send("cpf-validation", cpf); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /cleanarch-from-scratch/src/main/java/com/asilva/springbootcleanarchitecture/dataprovider/impl/UpdateCustomerByIdImpl.java: -------------------------------------------------------------------------------- 1 | package com.asilva.springbootcleanarchitecture.dataprovider.impl; 2 | 3 | import org.springframework.stereotype.Component; 4 | 5 | import com.asilva.springbootcleanarchitecture.core.dataprovider.UpdateCustomerById; 6 | import com.asilva.springbootcleanarchitecture.core.domain.Customer; 7 | import com.asilva.springbootcleanarchitecture.dataprovider.persistence.entity.CustomerEntity; 8 | import com.asilva.springbootcleanarchitecture.dataprovider.persistence.mapper.CustomerEntityMapper; 9 | import com.asilva.springbootcleanarchitecture.dataprovider.persistence.repository.CustomerRepository; 10 | 11 | import lombok.RequiredArgsConstructor; 12 | 13 | @Component 14 | @RequiredArgsConstructor 15 | public class UpdateCustomerByIdImpl implements UpdateCustomerById { 16 | 17 | private final CustomerRepository customerRepository; 18 | private final CustomerEntityMapper customerEntityMapper; 19 | 20 | @Override 21 | public void update(Customer customer) { 22 | CustomerEntity customerEntity = customerEntityMapper.toEntity(customer); 23 | customerRepository.save(customerEntity); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /cleanarch-from-scratch/src/main/java/com/asilva/springbootcleanarchitecture/dataprovider/persistence/entity/AddressEntity.java: -------------------------------------------------------------------------------- 1 | package com.asilva.springbootcleanarchitecture.dataprovider.persistence.entity; 2 | 3 | import org.springframework.data.annotation.Id; 4 | import org.springframework.data.mongodb.core.mapping.Document; 5 | 6 | import lombok.Data; 7 | 8 | @Data 9 | @Document(collection = "addresses") 10 | public class AddressEntity { 11 | 12 | @Id 13 | private String id; 14 | private String street; 15 | private String city; 16 | private String state; 17 | 18 | } 19 | -------------------------------------------------------------------------------- /cleanarch-from-scratch/src/main/java/com/asilva/springbootcleanarchitecture/dataprovider/persistence/entity/CustomerEntity.java: -------------------------------------------------------------------------------- 1 | package com.asilva.springbootcleanarchitecture.dataprovider.persistence.entity; 2 | 3 | import org.springframework.data.annotation.Id; 4 | import org.springframework.data.mongodb.core.mapping.Document; 5 | 6 | import lombok.Data; 7 | 8 | @Data 9 | @Document(collection = "customers") 10 | public class CustomerEntity { 11 | 12 | @Id 13 | private String id; 14 | private String name; 15 | private String cpf; 16 | private AddressEntity address; 17 | private Boolean isValidCpf; 18 | 19 | } 20 | -------------------------------------------------------------------------------- /cleanarch-from-scratch/src/main/java/com/asilva/springbootcleanarchitecture/dataprovider/persistence/mapper/CustomerEntityMapper.java: -------------------------------------------------------------------------------- 1 | package com.asilva.springbootcleanarchitecture.dataprovider.persistence.mapper; 2 | 3 | import org.mapstruct.Mapper; 4 | import org.mapstruct.Mapping; 5 | 6 | import com.asilva.springbootcleanarchitecture.core.domain.Customer; 7 | import com.asilva.springbootcleanarchitecture.dataprovider.persistence.entity.CustomerEntity; 8 | 9 | @Mapper(componentModel = "spring") 10 | public interface CustomerEntityMapper { 11 | 12 | @Mapping(target = "id", ignore = true) 13 | public CustomerEntity toEntity(final Customer address); 14 | 15 | public Customer toDomain(final CustomerEntity addressEntity); 16 | 17 | } 18 | -------------------------------------------------------------------------------- /cleanarch-from-scratch/src/main/java/com/asilva/springbootcleanarchitecture/dataprovider/persistence/repository/CustomerRepository.java: -------------------------------------------------------------------------------- 1 | package com.asilva.springbootcleanarchitecture.dataprovider.persistence.repository; 2 | 3 | import org.springframework.data.mongodb.repository.MongoRepository; 4 | import org.springframework.stereotype.Repository; 5 | 6 | import com.asilva.springbootcleanarchitecture.dataprovider.persistence.entity.CustomerEntity; 7 | 8 | @Repository 9 | public interface CustomerRepository extends MongoRepository { 10 | 11 | } 12 | -------------------------------------------------------------------------------- /cleanarch-from-scratch/src/main/java/com/asilva/springbootcleanarchitecture/entrypoint/consumer/ReceiveValidatedCpfConsumer.java: -------------------------------------------------------------------------------- 1 | package com.asilva.springbootcleanarchitecture.entrypoint.consumer; 2 | 3 | import org.springframework.kafka.annotation.KafkaListener; 4 | import org.springframework.stereotype.Component; 5 | 6 | import com.asilva.springbootcleanarchitecture.core.domain.Customer; 7 | import com.asilva.springbootcleanarchitecture.core.usecase.UpdateCustomerByIdUseCase; 8 | import com.asilva.springbootcleanarchitecture.entrypoint.consumer.mapper.CustomerMessageMapper; 9 | import com.asilva.springbootcleanarchitecture.entrypoint.consumer.message.CustomerMessage; 10 | 11 | import lombok.RequiredArgsConstructor; 12 | 13 | @Component 14 | @RequiredArgsConstructor 15 | public class ReceiveValidatedCpfConsumer { 16 | 17 | private final UpdateCustomerByIdUseCase updateCustomerByIdUseCase; 18 | private final CustomerMessageMapper customerMessageMapper; 19 | 20 | @KafkaListener(topics = "cpf-validation", groupId = "cleanarch") 21 | public void receive(CustomerMessage customerMessage) { 22 | Customer customer = customerMessageMapper.toDomain(customerMessage); 23 | updateCustomerByIdUseCase.update(customerMessage.getZipCode(), customer); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /cleanarch-from-scratch/src/main/java/com/asilva/springbootcleanarchitecture/entrypoint/consumer/mapper/CustomerMessageMapper.java: -------------------------------------------------------------------------------- 1 | package com.asilva.springbootcleanarchitecture.entrypoint.consumer.mapper; 2 | 3 | import org.mapstruct.Mapper; 4 | import org.mapstruct.Mapping; 5 | 6 | import com.asilva.springbootcleanarchitecture.core.domain.Customer; 7 | import com.asilva.springbootcleanarchitecture.entrypoint.consumer.message.CustomerMessage; 8 | 9 | @Mapper(componentModel = "spring") 10 | public interface CustomerMessageMapper { 11 | 12 | @Mapping(target = "address", ignore = true) 13 | Customer toDomain(final CustomerMessage customerMessage); 14 | 15 | } 16 | -------------------------------------------------------------------------------- /cleanarch-from-scratch/src/main/java/com/asilva/springbootcleanarchitecture/entrypoint/consumer/message/CustomerMessage.java: -------------------------------------------------------------------------------- 1 | package com.asilva.springbootcleanarchitecture.entrypoint.consumer.message; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | @Data 8 | @AllArgsConstructor 9 | @NoArgsConstructor 10 | public class CustomerMessage { 11 | 12 | private String id; 13 | private String name; 14 | private String zipCode; 15 | private String cpf; 16 | private Boolean isValidCpf; 17 | 18 | } 19 | -------------------------------------------------------------------------------- /cleanarch-from-scratch/src/main/java/com/asilva/springbootcleanarchitecture/entrypoint/controller/CustomerController.java: -------------------------------------------------------------------------------- 1 | package com.asilva.springbootcleanarchitecture.entrypoint.controller; 2 | 3 | import org.springframework.http.HttpStatus; 4 | import org.springframework.http.ResponseEntity; 5 | import org.springframework.web.bind.annotation.DeleteMapping; 6 | import org.springframework.web.bind.annotation.GetMapping; 7 | import org.springframework.web.bind.annotation.PathVariable; 8 | import org.springframework.web.bind.annotation.PostMapping; 9 | import org.springframework.web.bind.annotation.PutMapping; 10 | import org.springframework.web.bind.annotation.RequestBody; 11 | import org.springframework.web.bind.annotation.RequestMapping; 12 | import org.springframework.web.bind.annotation.RestController; 13 | 14 | import com.asilva.springbootcleanarchitecture.core.domain.Customer; 15 | import com.asilva.springbootcleanarchitecture.core.usecase.DeleteCustomerByIdUseCase; 16 | import com.asilva.springbootcleanarchitecture.core.usecase.FindCustomerByIdUseCase; 17 | import com.asilva.springbootcleanarchitecture.core.usecase.InsertCustomerUseCase; 18 | import com.asilva.springbootcleanarchitecture.core.usecase.UpdateCustomerByIdUseCase; 19 | import com.asilva.springbootcleanarchitecture.entrypoint.controller.mapper.CustomerMapper; 20 | import com.asilva.springbootcleanarchitecture.entrypoint.dto.request.CustomerRequestDto; 21 | import com.asilva.springbootcleanarchitecture.entrypoint.dto.response.CustomerResponseDto; 22 | 23 | import jakarta.validation.Valid; 24 | import lombok.RequiredArgsConstructor; 25 | 26 | @RestController 27 | @RequestMapping("/api/v1/customers") 28 | @RequiredArgsConstructor 29 | public class CustomerController { 30 | 31 | private final InsertCustomerUseCase insertCustomerUseCase; 32 | private final FindCustomerByIdUseCase findCustomerByIdUseCase; 33 | private final UpdateCustomerByIdUseCase updateCustomerUseCase; 34 | private final DeleteCustomerByIdUseCase deleteCustomerByIdUseCase; 35 | private final CustomerMapper customerMapper; 36 | 37 | @PostMapping 38 | public ResponseEntity insert(@Valid @RequestBody CustomerRequestDto request) { 39 | Customer customer = customerMapper.toDomain(request); 40 | insertCustomerUseCase.insert(customer, request.getZipCode()); 41 | 42 | return ResponseEntity.ok().build(); 43 | } 44 | 45 | @GetMapping("/{id}") 46 | public ResponseEntity findById(@PathVariable("id") String id) { 47 | Customer customer = findCustomerByIdUseCase.findById(id); 48 | CustomerResponseDto response = customerMapper.toResponse(customer); 49 | 50 | return ResponseEntity.status(HttpStatus.OK).body(response); 51 | } 52 | 53 | @PutMapping("/{id}") 54 | public ResponseEntity update( 55 | @PathVariable("id") String id, 56 | @Valid @RequestBody CustomerRequestDto request) { 57 | Customer customer = customerMapper.toDomain(request); 58 | 59 | customer.setId(id); 60 | updateCustomerUseCase.update(request.getZipCode(), customer); 61 | 62 | return ResponseEntity.ok().build(); 63 | } 64 | 65 | @DeleteMapping("/{id}") 66 | 67 | public ResponseEntity delete(@PathVariable("id") String id) { 68 | deleteCustomerByIdUseCase.delete(id); 69 | 70 | return ResponseEntity.ok().build(); 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /cleanarch-from-scratch/src/main/java/com/asilva/springbootcleanarchitecture/entrypoint/controller/mapper/CustomerMapper.java: -------------------------------------------------------------------------------- 1 | package com.asilva.springbootcleanarchitecture.entrypoint.controller.mapper; 2 | 3 | import org.mapstruct.Mapper; 4 | import org.mapstruct.Mapping; 5 | import org.mapstruct.Mappings; 6 | 7 | import com.asilva.springbootcleanarchitecture.core.domain.Customer; 8 | import com.asilva.springbootcleanarchitecture.entrypoint.dto.request.CustomerRequestDto; 9 | import com.asilva.springbootcleanarchitecture.entrypoint.dto.response.CustomerResponseDto; 10 | 11 | @Mapper(componentModel = "spring") 12 | public interface CustomerMapper { 13 | 14 | @Mappings({ 15 | @Mapping(target = "id", ignore = true), 16 | @Mapping(target = "address", ignore = true), 17 | @Mapping(target = "isValidCpf", ignore = true), 18 | }) 19 | public Customer toDomain(final CustomerRequestDto customer); 20 | 21 | public CustomerResponseDto toResponse(final Customer customer); 22 | 23 | } 24 | -------------------------------------------------------------------------------- /cleanarch-from-scratch/src/main/java/com/asilva/springbootcleanarchitecture/entrypoint/dto/request/CustomerRequestDto.java: -------------------------------------------------------------------------------- 1 | package com.asilva.springbootcleanarchitecture.entrypoint.dto.request; 2 | 3 | import jakarta.validation.constraints.NotBlank; 4 | import jakarta.validation.constraints.Size; 5 | import lombok.Data; 6 | 7 | @Data 8 | public class CustomerRequestDto { 9 | 10 | @NotBlank(message = "Name is required") 11 | private String name; 12 | 13 | @NotBlank(message = "CPF is required") 14 | @Size(min = 11, max = 11, message = "CPF must be 11 characters") 15 | private String cpf; 16 | 17 | @NotBlank(message = "Street is required") 18 | private String zipCode; 19 | 20 | } 21 | -------------------------------------------------------------------------------- /cleanarch-from-scratch/src/main/java/com/asilva/springbootcleanarchitecture/entrypoint/dto/response/AddressResponseDto.java: -------------------------------------------------------------------------------- 1 | package com.asilva.springbootcleanarchitecture.entrypoint.dto.response; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class AddressResponseDto { 7 | 8 | private String street; 9 | private String city; 10 | private String state; 11 | 12 | } 13 | -------------------------------------------------------------------------------- /cleanarch-from-scratch/src/main/java/com/asilva/springbootcleanarchitecture/entrypoint/dto/response/CustomerResponseDto.java: -------------------------------------------------------------------------------- 1 | package com.asilva.springbootcleanarchitecture.entrypoint.dto.response; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class CustomerResponseDto { 7 | 8 | private String id; 9 | private String name; 10 | private String cpf; 11 | private Boolean isValidCpf; 12 | private AddressResponseDto address; 13 | 14 | } 15 | -------------------------------------------------------------------------------- /cleanarch-from-scratch/src/main/resources/META-INF/additional-spring-configuration-metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "properties": [ 3 | { 4 | "name": "asilva.client.address.url", 5 | "type": "java.lang.String", 6 | "description": "A description for 'asilva.client.address.url'" 7 | } 8 | ] 9 | } -------------------------------------------------------------------------------- /cleanarch-from-scratch/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | asilva.client.address.url=http://localhost:8082/addresses 2 | 3 | spring.data.mongodb.uri=mongodb://localhost:27017/clean_arch 4 | 5 | springdoc.api-docs.enabled=true 6 | springdoc.api-docs.path=/v3/api-docs 7 | 8 | springdoc.swagger-ui.enabled=true 9 | springdoc.swagger-ui.path=/ -------------------------------------------------------------------------------- /cleanarch-from-scratch/src/test/java/com/asilva/springbootcleanarchitecture/SpringBootCleanArchitectureApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.asilva.springbootcleanarchitecture; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class SpringBootCleanArchitectureApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /docs/cleanarch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AthirsonSilva/spring-boot-clean-architecture/d97d91cc563badad10ea2b8c9981783135db7a28/docs/cleanarch.png --------------------------------------------------------------------------------