├── .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 | 
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
--------------------------------------------------------------------------------