├── .docker
├── .dockerignore
├── docker-compose.yaml
├── dockerfile
├── localstack.sh
├── logstash.conf
└── postgres-admin.json
├── .editorconfig
├── .gitattributes
├── .github
└── workflows
│ └── build.yaml
├── .gitignore
├── license.md
├── readme.md
└── source
├── .run
├── Application.run.xml
└── Tests.run.xml
├── lombok.config
├── pom.xml
└── src
├── main
├── java
│ └── com
│ │ └── company
│ │ └── architecture
│ │ ├── Application.java
│ │ ├── animal
│ │ ├── CreateAnimalInput.java
│ │ ├── CreateAnimalOutput.java
│ │ └── CreateAnimalUseCase.java
│ │ ├── api
│ │ ├── JsonPlaceholderClient.java
│ │ ├── JsonPlaceholderClientFallback.java
│ │ ├── JsonPlaceholderService.java
│ │ └── Todo.java
│ │ ├── auth
│ │ ├── Auth.java
│ │ ├── AuthConfiguration.java
│ │ ├── AuthController.java
│ │ ├── AuthFilter.java
│ │ ├── AuthService.java
│ │ ├── Authority.java
│ │ ├── JwtConfiguration.java
│ │ └── JwtService.java
│ │ ├── aws
│ │ ├── AwsConfiguration.java
│ │ ├── AwsController.java
│ │ ├── AwsS3Service.java
│ │ ├── AwsService.java
│ │ └── AwsSqsService.java
│ │ ├── book
│ │ ├── BookController.java
│ │ └── create
│ │ │ ├── CreateBookHandler.java
│ │ │ └── CreateBookRequest.java
│ │ ├── car
│ │ ├── Car.java
│ │ ├── CarController.java
│ │ └── CarService.java
│ │ ├── category
│ │ ├── Category.java
│ │ ├── CategoryCacheService.java
│ │ ├── CategoryRepository.java
│ │ └── CategoryService.java
│ │ ├── game
│ │ ├── Game.java
│ │ ├── GameRepository.java
│ │ └── GameService.java
│ │ ├── group
│ │ ├── GroupService.java
│ │ └── Person.java
│ │ ├── invoice
│ │ ├── InvoiceController.java
│ │ ├── InvoiceRepository.java
│ │ ├── InvoiceService.java
│ │ ├── InvoiceStatus.java
│ │ ├── dtos
│ │ │ ├── AddInvoiceDto.java
│ │ │ ├── AddInvoiceItemDto.java
│ │ │ ├── InvoiceDto.java
│ │ │ └── InvoiceItemDto.java
│ │ └── entities
│ │ │ ├── BaseEntity.java
│ │ │ ├── Invoice.java
│ │ │ └── InvoiceItem.java
│ │ ├── location
│ │ ├── City.java
│ │ ├── Country.java
│ │ ├── FlatLocation.java
│ │ ├── Location.java
│ │ ├── LocationService.java
│ │ └── State.java
│ │ ├── movie
│ │ ├── Movie.java
│ │ ├── MovieConfiguration.java
│ │ ├── MovieController.java
│ │ ├── MovieOutbox.java
│ │ ├── MovieOutboxRepository.java
│ │ ├── MovieOutboxService.java
│ │ ├── MovieRepository.java
│ │ └── MovieService.java
│ │ ├── notification
│ │ ├── Notification.java
│ │ ├── NotificationController.java
│ │ └── NotificationService.java
│ │ ├── payment
│ │ ├── CreditCardPaymentStrategy.java
│ │ ├── DebitCardPaymentStrategy.java
│ │ ├── PaymentMethod.java
│ │ ├── PaymentService.java
│ │ └── PaymentStrategy.java
│ │ ├── product
│ │ ├── Product.java
│ │ ├── ProductController.java
│ │ ├── ProductRepository.java
│ │ ├── ProductService.java
│ │ └── dtos
│ │ │ ├── AddProductDto.java
│ │ │ ├── GetProductDto.java
│ │ │ ├── ProductDto.java
│ │ │ ├── UpdatePriceProductDto.java
│ │ │ └── UpdateProductDto.java
│ │ ├── shared
│ │ ├── configurations
│ │ │ ├── ExceptionConfiguration.java
│ │ │ └── JacksonConfiguration.java
│ │ ├── dtos
│ │ │ └── PageableDto.java
│ │ ├── mediator
│ │ │ ├── Handler.java
│ │ │ └── Mediator.java
│ │ ├── runners
│ │ │ └── MongoRunner.java
│ │ ├── services
│ │ │ ├── MapperService.java
│ │ │ ├── MessageService.java
│ │ │ └── ValidatorService.java
│ │ ├── swagger
│ │ │ ├── BaseApiResponses.java
│ │ │ ├── DefaultApiResponses.java
│ │ │ ├── GetApiResponses.java
│ │ │ ├── PostApiResponses.java
│ │ │ └── SwaggerConfiguration.java
│ │ └── usecase
│ │ │ ├── Input.java
│ │ │ ├── Output.java
│ │ │ └── UseCase.java
│ │ └── user
│ │ ├── User.java
│ │ ├── UserController.java
│ │ ├── UserRepository.java
│ │ ├── UserService.java
│ │ └── dtos
│ │ ├── AddUserDto.java
│ │ ├── GetUserDto.java
│ │ ├── UpdateUserDto.java
│ │ └── UserDto.java
└── resources
│ ├── application.yml
│ ├── logback-spring.xml
│ ├── messages.properties
│ └── messages_pt_BR.properties
└── test
└── java
└── com
└── company
└── architecture
├── ControllerTest.java
├── IntegrationTest.java
├── KafkaTest.java
├── LocalStackTest.java
├── MongoTest.java
├── PostgreTest.java
├── animal
└── CreateAnimalUseCaseTest.java
├── api
└── JsonPlaceholderServiceTest.java
├── auth
├── AuthIntegrationTest.java
└── JwtServiceTest.java
├── aws
├── AwsIntegrationTest.java
├── AwsS3ServiceTest.java
├── AwsServiceTest.java
└── AwsSqsServiceTest.java
├── book
├── BookControllerTest.java
├── BookIntegrationTest.java
├── CreateBookHandlerMediatorTest.java
└── CreateBookHandlerTest.java
├── car
└── CarServiceTest.java
├── category
└── CategoryServiceTest.java
├── game
├── GameServiceTest.java
└── MockGameServiceTest.java
├── group
└── GroupServiceTest.java
├── invoice
├── InvoiceIntegrationTest.java
└── InvoiceRepositoryTest.java
├── location
└── LocationServiceTest.java
├── movie
└── MovieIntegrationTest.java
├── notification
└── NotificationTest.java
├── payment
└── PaymentServiceTest.java
├── product
├── ProductControllerTest.java
├── ProductIntegrationTest.java
└── ProductRepositoryTest.java
├── shared
├── Color.java
├── Data.java
├── Dto.java
├── Entity.java
└── services
│ ├── MapperServiceTest.java
│ ├── MessageServiceTest.java
│ └── ValidatorServiceTest.java
└── user
├── UserControllerTest.java
└── UserIntegrationTest.java
/.docker/.dockerignore:
--------------------------------------------------------------------------------
1 | *.bat
2 | *.dockerignore
3 | *.editorconfig
4 | *.gitattributes
5 | *.gitignore
6 | *.iml
7 | *.md
8 | *.yml
9 | .git/
10 | .github/
11 | .idea/
12 | .vscode/
13 | target/
14 |
--------------------------------------------------------------------------------
/.docker/docker-compose.yaml:
--------------------------------------------------------------------------------
1 | # docker compose up --detach --build --force-recreate --remove-orphans
2 |
3 | name: java
4 | services:
5 | application:
6 | image: application
7 | container_name: application
8 | depends_on:
9 | - kafka
10 | - localstack
11 | - mongo
12 | - postgres
13 | - elk-logstash
14 | build:
15 | context: ..
16 | dockerfile: .docker/dockerfile
17 | ports:
18 | - "8090:8080"
19 | environment:
20 | #SPRING_PROFILES_ACTIVE: production
21 | SPRING_DATASOURCE_URL: jdbc:postgresql://postgres:5432/database
22 | SPRING_DATASOURCE_USERNAME: admin
23 | SPRING_DATASOURCE_PASSWORD: password
24 | SPRING_DATA_MONGODB_URI: mongodb://admin:password@mongo:27017/database?authSource=admin
25 | SPRING_KAFKA_BOOTSTRAP_SERVERS: kafka:9094
26 | AWS_ENDPOINT: http://localstack:4566
27 | AWS_REGION: us-east-1
28 | AWS_ACCESS_KEY_ID: test
29 | AWS_SECRET_ACCESS_KEY: test
30 | ELK_LOGSTASH: elk-logstash:5000
31 | FEIGN_CLIENTS_JSONPLACEHOLDER_URL: https://jsonplaceholder.typicode.com
32 | elk-elasticsearch:
33 | image: docker.elastic.co/elasticsearch/elasticsearch:9.0.0
34 | container_name: elk-elasticsearch
35 | ports:
36 | - "9200:9200"
37 | environment:
38 | - discovery.type=single-node
39 | - xpack.security.enabled=false
40 | - ES_JAVA_OPTS=-Xms512m -Xmx512m
41 | elk-logstash:
42 | image: docker.elastic.co/logstash/logstash:9.0.0
43 | container_name: elk-logstash
44 | depends_on:
45 | - elk-elasticsearch
46 | ports:
47 | - "5000:5000"
48 | volumes:
49 | - ./logstash.conf:/usr/share/logstash/pipeline/logstash.conf:ro
50 | elk-kibana:
51 | image: docker.elastic.co/kibana/kibana:9.0.0
52 | container_name: elk-kibana
53 | depends_on:
54 | - elk-elasticsearch
55 | ports:
56 | - "5601:5601"
57 | environment:
58 | - ELASTICSEARCH_HOSTS=http://elk-elasticsearch:9200
59 | elk-apm-server:
60 | image: docker.elastic.co/apm/apm-server:9.0.0
61 | container_name: elk-apm
62 | depends_on:
63 | - elk-elasticsearch
64 | - elk-kibana
65 | ports:
66 | - "8200:8200"
67 | environment:
68 | - output.elasticsearch.hosts=["http://elk-elasticsearch:9200"]
69 | - apm-server.host=0.0.0.0:8200
70 | - apm-server.auth.anonymous.enabled=true
71 | - apm-server.kibana.enabled=true
72 | - apm-server.kibana.host=http://elk-kibana:5601
73 | kafka:
74 | image: apache/kafka
75 | container_name: kafka
76 | ports:
77 | - "9092:9092"
78 | environment:
79 | KAFKA_NODE_ID: 1
80 | KAFKA_PROCESS_ROLES: broker,controller
81 | KAFKA_LISTENERS: CONTROLLER://:9093,PLAINTEXT://:9094,EXTERNAL://:9092
82 | KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:9094,EXTERNAL://localhost:9092
83 | KAFKA_CONTROLLER_LISTENER_NAMES: CONTROLLER
84 | KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT,EXTERNAL:PLAINTEXT
85 | KAFKA_CONTROLLER_QUORUM_VOTERS: 1@kafka:9093
86 | KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
87 | KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 1
88 | KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 1
89 | kafka-admin:
90 | image: obsidiandynamics/kafdrop
91 | container_name: kafka-admin
92 | depends_on:
93 | - kafka
94 | ports:
95 | - "9000:9000"
96 | environment:
97 | KAFKA_BROKERCONNECT: kafka:9094
98 | localstack:
99 | image: localstack/localstack
100 | container_name: localstack
101 | ports:
102 | - "4566:4566"
103 | volumes:
104 | - /var/run/docker.sock:/var/run/docker.sock
105 | - ./localstack.sh:/etc/localstack/init/ready.d/localstack.sh
106 | environment:
107 | - SERVICES=sqs,sqs-query,s3
108 | mongo:
109 | image: mongo
110 | container_name: mongo
111 | ports:
112 | - "27017:27017"
113 | volumes:
114 | - mongo:/data/db
115 | environment:
116 | MONGO_INITDB_ROOT_USERNAME: admin
117 | MONGO_INITDB_ROOT_PASSWORD: password
118 | mongo-admin:
119 | image: mongo-express
120 | container_name: mongo-admin
121 | depends_on:
122 | - mongo
123 | ports:
124 | - "27018:8081"
125 | environment:
126 | ME_CONFIG_MONGODB_URL: mongodb://admin:password@mongo:27017
127 | ME_CONFIG_MONGODB_ADMINUSERNAME: admin
128 | ME_CONFIG_MONGODB_ADMINPASSWORD: password
129 | ME_CONFIG_BASICAUTH: false
130 | postgres:
131 | image: postgres
132 | container_name: postgres
133 | ports:
134 | - "5432:5432"
135 | volumes:
136 | - postgres:/var/lib/postgresql/data
137 | environment:
138 | POSTGRES_DB: database
139 | POSTGRES_USER: admin
140 | POSTGRES_PASSWORD: password
141 | postgres-admin:
142 | image: dpage/pgadmin4
143 | container_name: postgres-admin
144 | depends_on:
145 | - postgres
146 | ports:
147 | - "5433:80"
148 | volumes:
149 | - ./postgres-admin.json:/pgadmin4/servers.json
150 | - postgres-admin:/var/lib/pgadmin
151 | environment:
152 | PGADMIN_DEFAULT_EMAIL: admin@admin.com
153 | PGADMIN_DEFAULT_PASSWORD: password
154 | PGADMIN_CONFIG_SERVER_MODE: "False"
155 | PGADMIN_CONFIG_MASTER_PASSWORD_REQUIRED: "False"
156 | volumes:
157 | mongo:
158 | postgres:
159 | postgres-admin:
160 |
--------------------------------------------------------------------------------
/.docker/dockerfile:
--------------------------------------------------------------------------------
1 | FROM eclipse-temurin:24-jdk-alpine AS build
2 | RUN apk add --no-cache maven
3 | WORKDIR /source
4 | COPY source/pom.xml .
5 | RUN mvn dependency:go-offline -X
6 | COPY source .
7 | RUN mvn clean package -DskipTests -X
8 |
9 | FROM eclipse-temurin:24-jre-alpine
10 | WORKDIR /app
11 | COPY --from=build /source/target/*.jar app.jar
12 | EXPOSE 8090
13 | ENTRYPOINT ["java", "-jar", "app.jar"]
14 |
--------------------------------------------------------------------------------
/.docker/localstack.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | awslocal sqs create-queue --queue-name queue
3 | awslocal s3 mb s3://bucket
4 |
--------------------------------------------------------------------------------
/.docker/logstash.conf:
--------------------------------------------------------------------------------
1 | input {
2 | tcp {
3 | port => 5000
4 | codec => json
5 | }
6 | }
7 |
8 | output {
9 | elasticsearch {
10 | hosts => ["http://elk-elasticsearch:9200"]
11 | index => "application-%{+YYYY-MM-dd}"
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/.docker/postgres-admin.json:
--------------------------------------------------------------------------------
1 | {
2 | "Servers": {
3 | "Database": {
4 | "Group": "Servers",
5 | "Name": "Docker",
6 | "Host": "postgres",
7 | "Port": 5432,
8 | "MaintenanceDB": "postgres",
9 | "Username": "admin",
10 | "Password": "password",
11 | "SSLMode": "prefer",
12 | "Favorite": true
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | [*]
2 | charset = utf-8
3 | end_of_line = lf
4 | indent_size = 4
5 | indent_style = space
6 | insert_final_newline = true
7 | max_line_length = 500
8 | tab_width = 4
9 | trim_trailing_whitespace = true
10 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto
2 | *.java diff=java
3 |
--------------------------------------------------------------------------------
/.github/workflows/build.yaml:
--------------------------------------------------------------------------------
1 | name: build
2 | on:
3 | push:
4 | branches: [main]
5 | jobs:
6 | build:
7 | runs-on: ubuntu-latest
8 | steps:
9 | - name: Checkout
10 | uses: actions/checkout@v4
11 |
12 | - name: Java Setup
13 | uses: actions/setup-java@v4
14 | with:
15 | java-version: 24
16 | distribution: temurin
17 | cache: maven
18 |
19 | - name: Java Publish
20 | run: mvn -B clean package --file source/pom.xml
21 |
22 | - name: Artifact Upload
23 | uses: actions/upload-artifact@v4
24 | with:
25 | name: app
26 | path: source/target/*.jar
27 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.bat
2 | *.iml
3 | .idea
4 | .vscode
5 | target
--------------------------------------------------------------------------------
/license.md:
--------------------------------------------------------------------------------
1 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
2 |
3 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
4 |
5 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
6 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # JAVA
2 |
3 | 
4 |
5 | Java, Spring Boot, Docker, Testcontainers, PostgreSQL, MongoDB, Kafka, LocalStack, AWS (SQS, S3), JWT, Swagger, Patterns (Mediator, Observer, Outbox, Strategy).
6 |
7 | ## TECHNOLOGIES
8 |
9 | - [Java](https://dev.java)
10 | - [Spring Boot](https://spring.io/projects/spring-boot)
11 | - [Docker](https://www.docker.com/get-started)
12 | - [Testcontainers](https://java.testcontainers.org)
13 | - [PostgreSQL](https://www.postgresql.org/)
14 | - [MongoDB](https://www.mongodb.com/docs/manual)
15 | - [Kafka](https://kafka.apache.org)
16 | - [LocalStack](https://localstack.cloud)
17 | - [AWS SQS](https://aws.amazon.com/sqs)
18 | - [AWS S3](https://aws.amazon.com/s3)
19 | - [JWT](https://jwt.io)
20 | - [Swagger](https://swagger.io)
21 |
22 | ## RUN
23 |
24 |
25 | IntelliJ IDEA
26 |
27 | #### Prerequisites
28 |
29 | * [Docker](https://www.docker.com/get-started)
30 | * [Java JDK](https://www.oracle.com/java/technologies/downloads)
31 | * [IntelliJ IDEA](https://www.jetbrains.com/idea/download)
32 |
33 | #### Steps
34 |
35 | 1. Execute **docker compose up --detach --build --force-recreate --remove-orphans** in **docker** directory.
36 | 2. Open **source** directory in **IntelliJ IDEA**.
37 | 3. Select **Application.java** class.
38 | 4. Click **Run** or **Debug**.
39 | 5. Open .
40 |
41 |
42 |
43 |
44 | Docker
45 |
46 | #### Prerequisites
47 |
48 | * [Docker](https://www.docker.com/get-started)
49 |
50 | #### Steps
51 |
52 | 1. Execute **docker compose up --detach --build --force-recreate --remove-orphans** in **docker** directory.
53 | 2. Open .
54 |
55 |
56 |
57 | ## EXAMPLES
58 |
59 | - **Authentication and Authorization:** [Main](https://github.com/rafaelfgx/Java/tree/main/source/src/main/java/com/company/architecture/auth) | [Tests](https://github.com/rafaelfgx/Java/tree/main/source/src/test/java/com/company/architecture/auth)
60 | - **Cache:** [Main](https://github.com/rafaelfgx/Java/tree/main/source/src/main/java/com/company/architecture/category) | [Tests](https://github.com/rafaelfgx/Java/tree/main/source/src/test/java/com/company/architecture/category)
61 | - **Feign:** [Main](https://github.com/rafaelfgx/Java/tree/main/source/src/main/java/com/company/architecture/api) | [Tests](https://github.com/rafaelfgx/Java/tree/main/source/src/test/java/com/company/architecture/api)
62 | - **Kafka:** [Main](https://github.com/rafaelfgx/Java/tree/main/source/src/main/java/com/company/architecture/notification) | [Tests](https://github.com/rafaelfgx/Java/tree/main/source/src/test/java/com/company/architecture/notification)
63 | - **Mocks:** [Main](https://github.com/rafaelfgx/Java/tree/main/source/src/main/java/com/company/architecture/game) | [Tests](https://github.com/rafaelfgx/Java/tree/main/source/src/test/java/com/company/architecture/game)
64 | - **Amazon Web Services:** [Main](https://github.com/rafaelfgx/Java/tree/main/source/src/main/java/com/company/architecture/aws) | [Tests](https://github.com/rafaelfgx/Java/tree/main/source/src/test/java/com/company/architecture/aws)
65 | - **Databases - MongoDB:** [Main](https://github.com/rafaelfgx/Java/tree/main/source/src/main/java/com/company/architecture/product) | [Tests](https://github.com/rafaelfgx/Java/tree/main/source/src/test/java/com/company/architecture/product)
66 | - **Databases - PostgreSQL:** [Main](https://github.com/rafaelfgx/Java/tree/main/source/src/main/java/com/company/architecture/invoice) | [Tests](https://github.com/rafaelfgx/Java/tree/main/source/src/test/java/com/company/architecture/invoice)
67 | - **Patterns - Mediator:** [Main](https://github.com/rafaelfgx/Java/tree/main/source/src/main/java/com/company/architecture/book) | [Tests](https://github.com/rafaelfgx/Java/tree/main/source/src/test/java/com/company/architecture/book)
68 | - **Patterns - Observer:** [Main](https://github.com/rafaelfgx/Java/tree/main/source/src/main/java/com/company/architecture/car) | [Tests](https://github.com/rafaelfgx/Java/tree/main/source/src/test/java/com/company/architecture/car)
69 | - **Patterns - Outbox:** [Main](https://github.com/rafaelfgx/Java/tree/main/source/src/main/java/com/company/architecture/movie) | [Tests](https://github.com/rafaelfgx/Java/tree/main/source/src/test/java/com/company/architecture/movie)
70 | - **Patterns - Strategy:** [Main](https://github.com/rafaelfgx/Java/tree/main/source/src/main/java/com/company/architecture/payment) | [Tests](https://github.com/rafaelfgx/Java/tree/main/source/src/test/java/com/company/architecture/payment)
71 | - **Patterns - UseCase:** [Main](https://github.com/rafaelfgx/Java/tree/main/source/src/main/java/com/company/architecture/animal) | [Tests](https://github.com/rafaelfgx/Java/tree/main/source/src/test/java/com/company/architecture/animal)
72 | - **Logic - Business Rules:** [Main](https://github.com/rafaelfgx/Java/tree/main/source/src/main/java/com/company/architecture/user) | [Tests](https://github.com/rafaelfgx/Java/tree/main/source/src/test/java/com/company/architecture/user)
73 | - **Logic - Flat Object to Nested Object:** [Main](https://github.com/rafaelfgx/Java/tree/main/source/src/main/java/com/company/architecture/location) | [Tests](https://github.com/rafaelfgx/Java/tree/main/source/src/test/java/com/company/architecture/location)
74 | - **Logic - Groups:** [Main](https://github.com/rafaelfgx/Java/tree/main/source/src/main/java/com/company/architecture/group) | [Tests](https://github.com/rafaelfgx/Java/tree/main/source/src/test/java/com/company/architecture/group)
75 |
--------------------------------------------------------------------------------
/source/.run/Application.run.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/source/.run/Tests.run.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/source/lombok.config:
--------------------------------------------------------------------------------
1 | lombok.addLombokGeneratedAnnotation = true
2 |
--------------------------------------------------------------------------------
/source/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 | com.company
5 | architecture
6 | architecture
7 | 1.0.0
8 |
9 | 24
10 | 24
11 | 24
12 | full
13 | UTF-8
14 | UTF-8
15 |
16 |
17 | org.springframework.boot
18 | spring-boot-starter-parent
19 | 3.5.0
20 |
21 |
22 |
23 |
24 | org.springframework.cloud
25 | spring-cloud-dependencies
26 | 2025.0.0
27 | pom
28 | import
29 |
30 |
31 | io.awspring.cloud
32 | spring-cloud-aws-dependencies
33 | 3.3.1
34 | pom
35 | import
36 |
37 |
38 |
39 |
40 |
41 | org.springframework.boot
42 | spring-boot-starter-actuator
43 |
44 |
45 | org.springframework.boot
46 | spring-boot-starter-data-jpa
47 |
48 |
49 | org.springframework.boot
50 | spring-boot-starter-data-mongodb
51 |
52 |
53 | org.springframework.boot
54 | spring-boot-starter-oauth2-authorization-server
55 |
56 |
57 | org.springframework.boot
58 | spring-boot-starter-security
59 |
60 |
61 | org.springframework.boot
62 | spring-boot-starter-test
63 | test
64 |
65 |
66 | org.springframework.boot
67 | spring-boot-starter-validation
68 |
69 |
70 | org.springframework.boot
71 | spring-boot-starter-web
72 |
73 |
74 | org.springframework.boot
75 | spring-boot-testcontainers
76 | test
77 |
78 |
79 | org.springframework.kafka
80 | spring-kafka
81 |
82 |
83 | org.springframework.cloud
84 | spring-cloud-starter-openfeign
85 |
86 |
87 | io.awspring.cloud
88 | spring-cloud-aws-starter
89 |
90 |
91 | io.awspring.cloud
92 | spring-cloud-aws-starter-s3
93 |
94 |
95 | io.awspring.cloud
96 | spring-cloud-aws-starter-sqs
97 |
98 |
99 | net.logstash.logback
100 | logstash-logback-encoder
101 | 8.1
102 |
103 |
104 | org.postgresql
105 | postgresql
106 | 42.7.6
107 | runtime
108 |
109 |
110 | org.projectlombok
111 | lombok
112 | provided
113 |
114 |
115 | org.springdoc
116 | springdoc-openapi-starter-webmvc-ui
117 | 2.8.8
118 |
119 |
120 | org.testcontainers
121 | junit-jupiter
122 | test
123 |
124 |
125 | org.testcontainers
126 | kafka
127 | test
128 |
129 |
130 | org.testcontainers
131 | localstack
132 | test
133 |
134 |
135 | org.testcontainers
136 | mongodb
137 | test
138 |
139 |
140 | org.testcontainers
141 | postgresql
142 | test
143 |
144 |
145 |
146 |
147 |
148 | org.springframework.boot
149 | spring-boot-maven-plugin
150 |
151 |
152 |
153 |
154 |
--------------------------------------------------------------------------------
/source/src/main/java/com/company/architecture/Application.java:
--------------------------------------------------------------------------------
1 | package com.company.architecture;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 | import org.springframework.cache.annotation.EnableCaching;
6 | import org.springframework.cloud.openfeign.EnableFeignClients;
7 | import org.springframework.kafka.annotation.EnableKafka;
8 | import org.springframework.scheduling.annotation.EnableScheduling;
9 |
10 | @EnableCaching
11 | @EnableFeignClients
12 | @EnableKafka
13 | @EnableScheduling
14 | @SpringBootApplication
15 | public class Application {
16 | public static void main(final String[] args) {
17 | SpringApplication.run(Application.class, args);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/source/src/main/java/com/company/architecture/animal/CreateAnimalInput.java:
--------------------------------------------------------------------------------
1 | package com.company.architecture.animal;
2 |
3 | import com.company.architecture.shared.usecase.Input;
4 |
5 | public record CreateAnimalInput(String name) implements Input {
6 | }
7 |
--------------------------------------------------------------------------------
/source/src/main/java/com/company/architecture/animal/CreateAnimalOutput.java:
--------------------------------------------------------------------------------
1 | package com.company.architecture.animal;
2 |
3 | import com.company.architecture.shared.usecase.Output;
4 |
5 | import java.util.UUID;
6 |
7 | public record CreateAnimalOutput(UUID id) implements Output {
8 | }
9 |
--------------------------------------------------------------------------------
/source/src/main/java/com/company/architecture/animal/CreateAnimalUseCase.java:
--------------------------------------------------------------------------------
1 | package com.company.architecture.animal;
2 |
3 | import com.company.architecture.shared.usecase.UseCase;
4 |
5 | import java.util.UUID;
6 |
7 | public class CreateAnimalUseCase implements UseCase {
8 | @Override
9 | public CreateAnimalOutput execute(CreateAnimalInput input) {
10 | return new CreateAnimalOutput(UUID.randomUUID());
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/source/src/main/java/com/company/architecture/api/JsonPlaceholderClient.java:
--------------------------------------------------------------------------------
1 | package com.company.architecture.api;
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 java.util.List;
8 |
9 | @FeignClient(name = "jsonplaceholder", url = "${feign.clients.jsonplaceholder.url}", fallback = JsonPlaceholderClientFallback.class)
10 | public interface JsonPlaceholderClient {
11 | @GetMapping("todos")
12 | List getTodos();
13 |
14 | @GetMapping("todos/{id}")
15 | Todo getTodo(@PathVariable int id);
16 | }
17 |
--------------------------------------------------------------------------------
/source/src/main/java/com/company/architecture/api/JsonPlaceholderClientFallback.java:
--------------------------------------------------------------------------------
1 | package com.company.architecture.api;
2 |
3 | import lombok.extern.slf4j.Slf4j;
4 | import org.springframework.stereotype.Component;
5 |
6 | import java.util.List;
7 |
8 | @Component
9 | @Slf4j
10 | public class JsonPlaceholderClientFallback implements JsonPlaceholderClient {
11 | @Override
12 | public List getTodos() {
13 | log.info("[JsonPlaceholderClientFallback].[getTodos]");
14 | return List.of();
15 | }
16 |
17 | @Override
18 | public Todo getTodo(int id) {
19 | log.info("[JsonPlaceholderClientFallback].[getTodo]");
20 | return null;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/source/src/main/java/com/company/architecture/api/JsonPlaceholderService.java:
--------------------------------------------------------------------------------
1 | package com.company.architecture.api;
2 |
3 | import lombok.RequiredArgsConstructor;
4 | import org.springframework.stereotype.Service;
5 |
6 | import java.util.List;
7 |
8 | @Service
9 | @RequiredArgsConstructor
10 | public class JsonPlaceholderService {
11 | private final JsonPlaceholderClient jsonPlaceholderClient;
12 |
13 | public List getTodos() {
14 | return jsonPlaceholderClient.getTodos();
15 | }
16 |
17 | public Todo getTodo(final int id) {
18 | return jsonPlaceholderClient.getTodo(id);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/source/src/main/java/com/company/architecture/api/Todo.java:
--------------------------------------------------------------------------------
1 | package com.company.architecture.api;
2 |
3 | public record Todo(int userId, int id, String title, boolean completed) {
4 | }
5 |
--------------------------------------------------------------------------------
/source/src/main/java/com/company/architecture/auth/Auth.java:
--------------------------------------------------------------------------------
1 | package com.company.architecture.auth;
2 |
3 | import jakarta.validation.constraints.NotBlank;
4 |
5 | public record Auth(@NotBlank String username, @NotBlank String password) {
6 | }
7 |
--------------------------------------------------------------------------------
/source/src/main/java/com/company/architecture/auth/AuthConfiguration.java:
--------------------------------------------------------------------------------
1 | package com.company.architecture.auth;
2 |
3 | import lombok.RequiredArgsConstructor;
4 | import org.springframework.context.annotation.Bean;
5 | import org.springframework.context.annotation.Configuration;
6 | import org.springframework.http.HttpMethod;
7 | import org.springframework.security.authentication.AuthenticationManager;
8 | import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
9 | import org.springframework.security.config.annotation.web.builders.HttpSecurity;
10 | import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
11 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
12 | import org.springframework.security.crypto.password.PasswordEncoder;
13 | import org.springframework.security.web.SecurityFilterChain;
14 | import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
15 |
16 | @Configuration
17 | @RequiredArgsConstructor
18 | public class AuthConfiguration {
19 | private final AuthFilter authFilter;
20 |
21 | @Bean
22 | AuthenticationManager authenticationManager(final AuthenticationConfiguration configuration) throws Exception {
23 | return configuration.getAuthenticationManager();
24 | }
25 |
26 | @Bean
27 | PasswordEncoder passwordEncoder() {
28 | return new BCryptPasswordEncoder();
29 | }
30 |
31 | @Bean
32 | SecurityFilterChain securityFilterChain(final HttpSecurity security) throws Exception {
33 | return security
34 | .csrf(AbstractHttpConfigurer::disable)
35 | .addFilterBefore(authFilter, UsernamePasswordAuthenticationFilter.class)
36 | .authorizeHttpRequests(registry -> registry
37 | .requestMatchers("/", "/actuator/**", "/v3/api-docs/**", "/swagger-ui/**", "/auth").permitAll()
38 | .requestMatchers(HttpMethod.DELETE).hasAuthority(Authority.ADMINISTRATOR.name())
39 | .anyRequest().authenticated()
40 | )
41 | .build();
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/source/src/main/java/com/company/architecture/auth/AuthController.java:
--------------------------------------------------------------------------------
1 | package com.company.architecture.auth;
2 |
3 | import com.company.architecture.shared.swagger.PostApiResponses;
4 | import io.swagger.v3.oas.annotations.Operation;
5 | import io.swagger.v3.oas.annotations.tags.Tag;
6 | import jakarta.validation.Valid;
7 | import lombok.RequiredArgsConstructor;
8 | import org.springframework.http.HttpStatus;
9 | import org.springframework.web.bind.annotation.*;
10 |
11 | @Tag(name = "Auth")
12 | @RequiredArgsConstructor
13 | @RestController
14 | @RequestMapping("/auth")
15 | public class AuthController {
16 | private final AuthService authService;
17 |
18 | @Operation(summary = "Auth")
19 | @PostApiResponses
20 | @PostMapping
21 | @ResponseStatus(HttpStatus.OK)
22 | public String auth(@RequestBody @Valid final Auth auth) {
23 | return authService.auth(auth);
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/source/src/main/java/com/company/architecture/auth/AuthFilter.java:
--------------------------------------------------------------------------------
1 | package com.company.architecture.auth;
2 |
3 | import jakarta.annotation.Nonnull;
4 | import jakarta.servlet.FilterChain;
5 | import jakarta.servlet.ServletException;
6 | import jakarta.servlet.http.HttpServletRequest;
7 | import jakarta.servlet.http.HttpServletResponse;
8 | import lombok.RequiredArgsConstructor;
9 | import org.apache.commons.lang3.StringUtils;
10 | import org.springframework.http.HttpHeaders;
11 | import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
12 | import org.springframework.security.core.context.SecurityContextHolder;
13 | import org.springframework.stereotype.Component;
14 | import org.springframework.web.filter.OncePerRequestFilter;
15 |
16 | import java.io.IOException;
17 |
18 | @Component
19 | @RequiredArgsConstructor
20 | public class AuthFilter extends OncePerRequestFilter {
21 | private final JwtService jwtService;
22 |
23 | @Override
24 | protected void doFilterInternal(final HttpServletRequest request, final @Nonnull HttpServletResponse response, final @Nonnull FilterChain filterChain) throws ServletException, IOException {
25 | final var jwt = StringUtils.removeStart(StringUtils.defaultString(request.getHeader(HttpHeaders.AUTHORIZATION)), "Bearer").trim();
26 |
27 | if (jwtService.verify(jwt)) {
28 | SecurityContextHolder.getContext().setAuthentication(UsernamePasswordAuthenticationToken.authenticated(jwtService.getSubject(jwt), null, jwtService.getAuthorities(jwt)));
29 | }
30 |
31 | filterChain.doFilter(request, response);
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/source/src/main/java/com/company/architecture/auth/AuthService.java:
--------------------------------------------------------------------------------
1 | package com.company.architecture.auth;
2 |
3 | import com.company.architecture.user.UserRepository;
4 | import lombok.RequiredArgsConstructor;
5 | import org.springframework.http.HttpStatus;
6 | import org.springframework.security.crypto.password.PasswordEncoder;
7 | import org.springframework.stereotype.Service;
8 | import org.springframework.web.server.ResponseStatusException;
9 |
10 | @Service
11 | @RequiredArgsConstructor
12 | public class AuthService {
13 | private final PasswordEncoder passwordEncoder;
14 | private final JwtService jwtService;
15 | private final UserRepository userRepository;
16 |
17 | public String auth(final Auth auth) {
18 | return userRepository
19 | .findByUsername(auth.username())
20 | .filter(user -> passwordEncoder.matches(auth.password(), user.getPassword()))
21 | .map(jwtService::create)
22 | .orElseThrow(() -> new ResponseStatusException(HttpStatus.UNAUTHORIZED, "auth.unauthorized"));
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/source/src/main/java/com/company/architecture/auth/Authority.java:
--------------------------------------------------------------------------------
1 | package com.company.architecture.auth;
2 |
3 | public enum Authority {
4 | DEFAULT,
5 | ADMINISTRATOR
6 | }
7 |
--------------------------------------------------------------------------------
/source/src/main/java/com/company/architecture/auth/JwtConfiguration.java:
--------------------------------------------------------------------------------
1 | package com.company.architecture.auth;
2 |
3 | import com.nimbusds.jose.jwk.JWKSet;
4 | import com.nimbusds.jose.jwk.RSAKey;
5 | import com.nimbusds.jose.jwk.source.ImmutableJWKSet;
6 | import lombok.RequiredArgsConstructor;
7 | import org.springframework.context.annotation.Bean;
8 | import org.springframework.context.annotation.Configuration;
9 | import org.springframework.security.oauth2.jwt.JwtDecoder;
10 | import org.springframework.security.oauth2.jwt.JwtEncoder;
11 | import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
12 | import org.springframework.security.oauth2.jwt.NimbusJwtEncoder;
13 |
14 | import java.security.KeyPair;
15 | import java.security.KeyPairGenerator;
16 | import java.security.NoSuchAlgorithmException;
17 | import java.security.interfaces.RSAPrivateKey;
18 | import java.security.interfaces.RSAPublicKey;
19 |
20 | @Configuration
21 | @RequiredArgsConstructor
22 | public class JwtConfiguration {
23 | @Bean
24 | KeyPair keyPair() throws NoSuchAlgorithmException {
25 | return KeyPairGenerator.getInstance("RSA").generateKeyPair();
26 | }
27 |
28 | @Bean
29 | RSAKey key(KeyPair keyPair) {
30 | return new RSAKey.Builder((RSAPublicKey) keyPair.getPublic()).privateKey((RSAPrivateKey) keyPair.getPrivate()).build();
31 | }
32 |
33 | @Bean
34 | JwtEncoder jwtEncoder(RSAKey key) {
35 | return new NimbusJwtEncoder(new ImmutableJWKSet<>(new JWKSet(key)));
36 | }
37 |
38 | @Bean
39 | JwtDecoder jwtDecoder(KeyPair keyPair) {
40 | return NimbusJwtDecoder.withPublicKey((RSAPublicKey) keyPair.getPublic()).build();
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/source/src/main/java/com/company/architecture/auth/JwtService.java:
--------------------------------------------------------------------------------
1 | package com.company.architecture.auth;
2 |
3 | import com.company.architecture.user.User;
4 | import lombok.RequiredArgsConstructor;
5 | import org.springframework.security.core.GrantedAuthority;
6 | import org.springframework.security.core.authority.AuthorityUtils;
7 | import org.springframework.security.oauth2.jwt.JwtClaimsSet;
8 | import org.springframework.security.oauth2.jwt.JwtDecoder;
9 | import org.springframework.security.oauth2.jwt.JwtEncoder;
10 | import org.springframework.security.oauth2.jwt.JwtEncoderParameters;
11 | import org.springframework.stereotype.Service;
12 |
13 | import java.util.List;
14 |
15 | @Service
16 | @RequiredArgsConstructor
17 | public class JwtService {
18 | private final JwtEncoder encoder;
19 | private final JwtDecoder decoder;
20 |
21 | public String create(final User user) {
22 | final var authorities = user.getAuthorities().stream().map(Enum::name).toArray(String[]::new);
23 | final var claims = JwtClaimsSet.builder().subject(user.getId().toString()).claim("authorities", authorities).build();
24 | return this.encoder.encode(JwtEncoderParameters.from(claims)).getTokenValue();
25 | }
26 |
27 | public boolean verify(final String jwt) {
28 | try {
29 | decoder.decode(jwt);
30 | return true;
31 | } catch (Exception exception) {
32 | return false;
33 | }
34 | }
35 |
36 | public String getSubject(final String jwt) {
37 | return decoder.decode(jwt).getSubject();
38 | }
39 |
40 | public List getAuthorities(final String jwt) {
41 | return AuthorityUtils.createAuthorityList(decoder.decode(jwt).getClaimAsStringList("authorities"));
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/source/src/main/java/com/company/architecture/aws/AwsConfiguration.java:
--------------------------------------------------------------------------------
1 | package com.company.architecture.aws;
2 |
3 | import lombok.RequiredArgsConstructor;
4 | import org.springframework.context.annotation.Bean;
5 | import org.springframework.context.annotation.Configuration;
6 | import org.springframework.core.env.Environment;
7 | import software.amazon.awssdk.services.s3.S3Client;
8 | import software.amazon.awssdk.services.s3.S3Configuration;
9 | import software.amazon.awssdk.services.sqs.SqsAsyncClient;
10 |
11 | import java.net.URI;
12 | import java.util.Objects;
13 |
14 | @RequiredArgsConstructor
15 | @Configuration
16 | public class AwsConfiguration {
17 | private final Environment environment;
18 | protected static final String BUCKET = "bucket";
19 | protected static final String QUEUE = "queue";
20 |
21 | @Bean
22 | public S3Client s3Client() {
23 | final var client = S3Client.builder().endpointOverride(endpoint()).serviceConfiguration(S3Configuration.builder().pathStyleAccessEnabled(true).build()).build();
24 | client.createBucket(builder -> builder.bucket(BUCKET));
25 | return client;
26 | }
27 |
28 | @Bean
29 | public SqsAsyncClient sqsAsyncClient() {
30 | final var client = SqsAsyncClient.builder().endpointOverride(endpoint()).build();
31 | client.createQueue(builder -> builder.queueName(QUEUE));
32 | return client;
33 | }
34 |
35 | private URI endpoint() {
36 | return URI.create(Objects.requireNonNullElse(environment.getProperty("aws.endpoint"), environment.getProperty("AWS_ENDPOINT")));
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/source/src/main/java/com/company/architecture/aws/AwsController.java:
--------------------------------------------------------------------------------
1 | package com.company.architecture.aws;
2 |
3 | import com.company.architecture.shared.swagger.GetApiResponses;
4 | import com.company.architecture.shared.swagger.PostApiResponses;
5 | import io.swagger.v3.oas.annotations.Operation;
6 | import io.swagger.v3.oas.annotations.tags.Tag;
7 | import lombok.RequiredArgsConstructor;
8 | import org.springframework.core.io.Resource;
9 | import org.springframework.http.HttpHeaders;
10 | import org.springframework.http.MediaType;
11 | import org.springframework.http.ResponseEntity;
12 | import org.springframework.web.bind.annotation.*;
13 | import org.springframework.web.multipart.MultipartFile;
14 |
15 | import java.io.IOException;
16 |
17 | @Tag(name = "AWS")
18 | @RequiredArgsConstructor
19 | @RestController
20 | @RequestMapping("/aws")
21 | public class AwsController {
22 | private final AwsService awsService;
23 |
24 | @Operation(summary = "Send")
25 | @PostApiResponses
26 | @PostMapping("queues/send")
27 | public void send(@RequestBody final String message) {
28 | awsService.send(message);
29 | }
30 |
31 | @Operation(summary = "Upload")
32 | @PostApiResponses
33 | @PostMapping(value = "files/upload", consumes = {MediaType.MULTIPART_FORM_DATA_VALUE})
34 | public String upload(@RequestParam MultipartFile file) throws IOException {
35 | return awsService.upload(file).getFilename();
36 | }
37 |
38 | @Operation(summary = "Download")
39 | @GetApiResponses
40 | @GetMapping("files/download/{key}")
41 | public ResponseEntity get(@PathVariable final String key) {
42 | final var headers = new HttpHeaders();
43 | headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
44 | return ResponseEntity.ok().headers(headers).body(awsService.download(key));
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/source/src/main/java/com/company/architecture/aws/AwsS3Service.java:
--------------------------------------------------------------------------------
1 | package com.company.architecture.aws;
2 |
3 | import io.awspring.cloud.s3.ObjectMetadata;
4 | import io.awspring.cloud.s3.S3Resource;
5 | import io.awspring.cloud.s3.S3Template;
6 | import lombok.RequiredArgsConstructor;
7 | import org.springframework.stereotype.Service;
8 | import org.springframework.web.multipart.MultipartFile;
9 |
10 | import java.io.ByteArrayInputStream;
11 | import java.io.IOException;
12 | import java.nio.file.Files;
13 | import java.nio.file.Paths;
14 |
15 | @Service
16 | @RequiredArgsConstructor
17 | public class AwsS3Service {
18 | private final S3Template s3Template;
19 |
20 | public S3Resource store(final String bucket, final String key, final Object object) {
21 | return s3Template.store(bucket, key, object);
22 | }
23 |
24 | public T read(final String bucket, final String key, final Class clazz) {
25 | return s3Template.read(bucket, key, clazz);
26 | }
27 |
28 | public S3Resource upload(final String bucket, final MultipartFile file) throws IOException {
29 | return upload(bucket, file.getOriginalFilename(), file.getBytes());
30 | }
31 |
32 | public S3Resource upload(final String bucket, final String key, final byte[] bytes) throws IOException {
33 | return s3Template.upload(bucket, key, new ByteArrayInputStream(bytes), ObjectMetadata.builder().contentType(Files.probeContentType(Paths.get(key))).build());
34 | }
35 |
36 | public S3Resource download(final String bucket, final String key) {
37 | return s3Template.download(bucket, key);
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/source/src/main/java/com/company/architecture/aws/AwsService.java:
--------------------------------------------------------------------------------
1 | package com.company.architecture.aws;
2 |
3 | import io.awspring.cloud.s3.S3Resource;
4 | import io.awspring.cloud.sqs.annotation.SqsListener;
5 | import lombok.RequiredArgsConstructor;
6 | import lombok.extern.slf4j.Slf4j;
7 | import org.springframework.stereotype.Service;
8 | import org.springframework.web.multipart.MultipartFile;
9 |
10 | import java.io.IOException;
11 |
12 | @Slf4j
13 | @Service
14 | @RequiredArgsConstructor
15 | public class AwsService {
16 | private final AwsSqsService awsSqsService;
17 | private final AwsS3Service awsS3Service;
18 |
19 | @SqsListener(AwsConfiguration.QUEUE)
20 | public void listen(final Object object) {
21 | log.info("[AwsSqsService].[listen]: {}", object);
22 | }
23 |
24 | public void send(final Object object) {
25 | awsSqsService.send(AwsConfiguration.QUEUE, object);
26 | }
27 |
28 | public S3Resource upload(final MultipartFile file) throws IOException {
29 | return awsS3Service.upload(AwsConfiguration.BUCKET, file.getOriginalFilename(), file.getBytes());
30 | }
31 |
32 | public S3Resource download(final String key) {
33 | return awsS3Service.download(AwsConfiguration.BUCKET, key);
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/source/src/main/java/com/company/architecture/aws/AwsSqsService.java:
--------------------------------------------------------------------------------
1 | package com.company.architecture.aws;
2 |
3 | import io.awspring.cloud.sqs.operations.SqsTemplate;
4 | import lombok.RequiredArgsConstructor;
5 | import lombok.extern.slf4j.Slf4j;
6 | import org.springframework.stereotype.Service;
7 |
8 | @Slf4j
9 | @Service
10 | @RequiredArgsConstructor
11 | public class AwsSqsService {
12 | private final SqsTemplate sqsTemplate;
13 |
14 | public void send(final String queue, final Object object) {
15 | sqsTemplate.send(queue, object);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/source/src/main/java/com/company/architecture/book/BookController.java:
--------------------------------------------------------------------------------
1 | package com.company.architecture.book;
2 |
3 | import com.company.architecture.book.create.CreateBookRequest;
4 | import com.company.architecture.shared.mediator.Mediator;
5 | import com.company.architecture.shared.swagger.PostApiResponses;
6 | import io.swagger.v3.oas.annotations.Operation;
7 | import io.swagger.v3.oas.annotations.tags.Tag;
8 | import jakarta.validation.Valid;
9 | import lombok.RequiredArgsConstructor;
10 | import org.springframework.http.ResponseEntity;
11 | import org.springframework.web.bind.annotation.PostMapping;
12 | import org.springframework.web.bind.annotation.RequestBody;
13 | import org.springframework.web.bind.annotation.RequestMapping;
14 | import org.springframework.web.bind.annotation.RestController;
15 |
16 | import java.util.UUID;
17 |
18 | @Tag(name = "Books")
19 | @RequiredArgsConstructor
20 | @RestController
21 | @RequestMapping("/books")
22 | public class BookController {
23 | private final Mediator mediator;
24 |
25 | @Operation(summary = "Create")
26 | @PostApiResponses
27 | @PostMapping
28 | public ResponseEntity create(@RequestBody @Valid final CreateBookRequest request) {
29 | return mediator.handle(request, UUID.class);
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/source/src/main/java/com/company/architecture/book/create/CreateBookHandler.java:
--------------------------------------------------------------------------------
1 | package com.company.architecture.book.create;
2 |
3 | import com.company.architecture.shared.mediator.Handler;
4 | import org.springframework.http.HttpStatus;
5 | import org.springframework.http.ResponseEntity;
6 | import org.springframework.stereotype.Component;
7 |
8 | import java.util.UUID;
9 |
10 | @Component
11 | public class CreateBookHandler implements Handler {
12 | @Override
13 | public ResponseEntity handle(CreateBookRequest request) {
14 | return new ResponseEntity<>(UUID.randomUUID(), HttpStatus.CREATED);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/source/src/main/java/com/company/architecture/book/create/CreateBookRequest.java:
--------------------------------------------------------------------------------
1 | package com.company.architecture.book.create;
2 |
3 | import jakarta.validation.constraints.NotBlank;
4 |
5 | public record CreateBookRequest(@NotBlank String title, @NotBlank String author) {
6 | }
7 |
--------------------------------------------------------------------------------
/source/src/main/java/com/company/architecture/car/Car.java:
--------------------------------------------------------------------------------
1 | package com.company.architecture.car;
2 |
3 | import jakarta.validation.constraints.NotBlank;
4 |
5 | public record Car(@NotBlank String brand, @NotBlank String model) {
6 | }
7 |
--------------------------------------------------------------------------------
/source/src/main/java/com/company/architecture/car/CarController.java:
--------------------------------------------------------------------------------
1 | package com.company.architecture.car;
2 |
3 | import com.company.architecture.shared.swagger.PostApiResponses;
4 | import io.swagger.v3.oas.annotations.Operation;
5 | import io.swagger.v3.oas.annotations.tags.Tag;
6 | import jakarta.validation.Valid;
7 | import lombok.RequiredArgsConstructor;
8 | import org.springframework.http.HttpStatus;
9 | import org.springframework.web.bind.annotation.*;
10 |
11 | @Tag(name = "Cars")
12 | @RequiredArgsConstructor
13 | @RestController
14 | @RequestMapping("/cars")
15 | public class CarController {
16 | private final CarService carService;
17 |
18 | @Operation(summary = "Add")
19 | @PostApiResponses
20 | @PostMapping
21 | @ResponseStatus(HttpStatus.OK)
22 | public void add(@RequestBody @Valid final Car car) {
23 | carService.add(car);
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/source/src/main/java/com/company/architecture/car/CarService.java:
--------------------------------------------------------------------------------
1 | package com.company.architecture.car;
2 |
3 | import lombok.RequiredArgsConstructor;
4 | import lombok.extern.slf4j.Slf4j;
5 | import org.springframework.context.ApplicationEventPublisher;
6 | import org.springframework.context.event.EventListener;
7 | import org.springframework.stereotype.Service;
8 |
9 | @Slf4j
10 | @Service
11 | @RequiredArgsConstructor
12 | public class CarService {
13 | private final ApplicationEventPublisher applicationEventPublisher;
14 |
15 | public void add(Car car) {
16 | log.info("[CarService].[add]: {}", car);
17 | applicationEventPublisher.publishEvent(car);
18 | }
19 |
20 | @EventListener
21 | public void listen(Car car) {
22 | log.info("[CarService].[listen]: {}", car);
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/source/src/main/java/com/company/architecture/category/Category.java:
--------------------------------------------------------------------------------
1 | package com.company.architecture.category;
2 |
3 | import jakarta.validation.constraints.NotBlank;
4 |
5 | public record Category(@NotBlank String name) {
6 | }
7 |
--------------------------------------------------------------------------------
/source/src/main/java/com/company/architecture/category/CategoryCacheService.java:
--------------------------------------------------------------------------------
1 | package com.company.architecture.category;
2 |
3 | import lombok.RequiredArgsConstructor;
4 | import lombok.extern.slf4j.Slf4j;
5 | import org.springframework.cache.annotation.CacheEvict;
6 | import org.springframework.cache.annotation.Cacheable;
7 | import org.springframework.scheduling.annotation.Scheduled;
8 | import org.springframework.stereotype.Service;
9 |
10 | import java.util.List;
11 | import java.util.concurrent.TimeUnit;
12 |
13 | @Slf4j
14 | @Service
15 | @RequiredArgsConstructor
16 | public class CategoryCacheService {
17 | private static final String KEY = "Category";
18 | private final CategoryRepository categoryRepository;
19 |
20 | @Cacheable(KEY)
21 | public List list() {
22 | log.info("[CategoryCacheService].[list]: {}", KEY);
23 | return categoryRepository.list();
24 | }
25 |
26 | @CacheEvict(allEntries = true, cacheNames = {KEY})
27 | @Scheduled(fixedRateString = "1", timeUnit = TimeUnit.HOURS)
28 | public void cacheEvict() {
29 | log.info("[CategoryCacheService].[cacheEvict]: {}", KEY);
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/source/src/main/java/com/company/architecture/category/CategoryRepository.java:
--------------------------------------------------------------------------------
1 | package com.company.architecture.category;
2 |
3 | import org.springframework.stereotype.Repository;
4 |
5 | import java.util.List;
6 |
7 | @Repository
8 | public class CategoryRepository {
9 | public List list() {
10 | return List.of(new Category("Category"));
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/source/src/main/java/com/company/architecture/category/CategoryService.java:
--------------------------------------------------------------------------------
1 | package com.company.architecture.category;
2 |
3 | import lombok.RequiredArgsConstructor;
4 | import org.springframework.stereotype.Service;
5 |
6 | import java.util.List;
7 |
8 | @Service
9 | @RequiredArgsConstructor
10 | public class CategoryService {
11 | private final CategoryCacheService categoryCacheService;
12 |
13 | public List list() {
14 | return categoryCacheService.list();
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/source/src/main/java/com/company/architecture/game/Game.java:
--------------------------------------------------------------------------------
1 | package com.company.architecture.game;
2 |
3 | import jakarta.validation.constraints.NotBlank;
4 |
5 | public record Game(@NotBlank String title) {
6 | }
7 |
--------------------------------------------------------------------------------
/source/src/main/java/com/company/architecture/game/GameRepository.java:
--------------------------------------------------------------------------------
1 | package com.company.architecture.game;
2 |
3 | import org.springframework.stereotype.Repository;
4 |
5 | import java.util.List;
6 |
7 | @Repository
8 | public class GameRepository {
9 | private static final List games = List.of(new Game("Game A"), new Game("Game B"));
10 |
11 | public List list(final Game game) {
12 | return games.stream().filter(item -> item.title().contains(game.title())).toList();
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/source/src/main/java/com/company/architecture/game/GameService.java:
--------------------------------------------------------------------------------
1 | package com.company.architecture.game;
2 |
3 | import lombok.RequiredArgsConstructor;
4 | import org.springframework.stereotype.Service;
5 |
6 | import java.util.List;
7 |
8 | @Service
9 | @RequiredArgsConstructor
10 | public class GameService {
11 | private final GameRepository gameRepository;
12 |
13 | public List list(final Game game) {
14 | return gameRepository.list(game);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/source/src/main/java/com/company/architecture/group/GroupService.java:
--------------------------------------------------------------------------------
1 | package com.company.architecture.group;
2 |
3 | import lombok.RequiredArgsConstructor;
4 | import org.springframework.beans.BeanWrapperImpl;
5 | import org.springframework.stereotype.Service;
6 |
7 | import java.util.Arrays;
8 | import java.util.HashMap;
9 | import java.util.Map;
10 | import java.util.Objects;
11 |
12 | @Service
13 | @RequiredArgsConstructor
14 | public class GroupService {
15 | public Map