Skip to content

Commit 4096acf

Browse files
committed
v2.1 - added kafka message broker support
1 parent 6db2a9b commit 4096acf

File tree

13 files changed

+227
-74
lines changed

13 files changed

+227
-74
lines changed

README.md

Lines changed: 70 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,90 +1,96 @@
11
# Spring Boot Template
22

33
## Description
4-
A template for Spring Boot projects. This template includes the following features:
5-
- Spring Boot 3.2.3 (latest 3.x LTS, updated for security and new features)
6-
- Java 21 (latest LTS, required for Spring Boot 3.2+)
7-
- Spring Security with JWT authentication and authorization also with refresh token
8-
- Swagger UI for API documentation and testing (http://localhost:8080/api/v1/swagger-ui/index.html) with authentication and authorization support
9-
- Spring Data JPA with PostgreSQL database integration
10-
- Exception handling mechanism with custom exceptions and exception handlers using `@ControllerAdvice`
11-
- Base entity and base DTO for common fields
12-
- Base service for common business logic
13-
- Custom `@PreAuthorize` annotation for authorization
14-
- Lombok for reducing boilerplate code
15-
- MapStruct for mapping DTOs to entities and vice versa
16-
- **Docker Compose support for easy containerized development**
17-
- Redis integration for caching and session management (configurable via environment variables)
18-
19-
> **Note:** This template is up-to-date with Spring Boot 3.2.3 and Java 21. Ensure your local environment supports Java 21 for development and builds.
20-
21-
## How to run
4+
A robust template for Spring Boot projects with modern best practices and production-ready integrations.
5+
6+
![Docker Compose](https://imgur.com/qWLYDrg.png)
7+
![Swagger UI](https://imgur.com/7Vug2XR.png)
8+
9+
### Key Features
10+
- **Spring Boot 3.2.3** (Java 21 LTS)
11+
- **JWT Authentication & Authorization** (with refresh tokens)
12+
- **Swagger UI** for API documentation and testing ([http://localhost:8080/api/v1/swagger-ui/index.html](http://localhost:8080/api/v1/swagger-ui/index.html))
13+
- **PostgreSQL** integration (via Docker Compose)
14+
- **Redis** for caching and session management
15+
- **Kafka & Zookeeper** for message brokering (via Docker Compose)
16+
- **Role-based Security** with Spring Security
17+
- **Rate Limiting**
18+
- **Exception Handling** with `@ControllerAdvice`
19+
- **Lombok** and **MapStruct** for reduced boilerplate
20+
- **Environment Isolation**: Easily run multiple environments (dev, test, prod) in parallel using Docker Compose project names and override files
21+
- **Configurable Logging** (including Kafka)
22+
- **Unit & Integration Tests** for key services and controllers
23+
24+
> **Note:** Java 21 is required. Ensure your local environment supports Java 21 for development and builds.
25+
26+
---
27+
28+
## How to Run
2229

2330
### Using Docker Compose (Recommended)
2431
1. Clone this repository
25-
2. Make sure Docker and Docker Compose are installed on your system
26-
3. Run the following command in the project root:
32+
2. Ensure Docker and Docker Compose are installed
33+
3. For a default environment, run:
2734
```sh
2835
docker compose up -d
2936
```
30-
4. The application will be available at [http://localhost:8080/api/v1/swagger-ui/index.html](http://localhost:8080/api/v1/swagger-ui/index.html)
31-
5. PostgreSQL will be available at port 5432 (default credentials are set in `docker-compose.yml` and can be overridden with environment variables)
37+
4. For multiple environments (e.g., test and prod) in parallel:
38+
```sh
39+
docker-compose -f docker-compose.yml -f docker-compose.test.override.yml --env-file env.test -p myapp_test up -d
40+
docker-compose -f docker-compose.yml -f docker-compose.prod.override.yml --env-file env.prod -p myapp_prod up -d
41+
```
42+
- This will create isolated containers, networks, and volumes for each environment.
43+
- Make sure to set different ports in override files to avoid conflicts.
44+
5. The application will be available at [http://localhost:8080/api/v1/swagger-ui/index.html](http://localhost:8080/api/v1/swagger-ui/index.html) (or the port you set).
3245

3346
### Using Maven (Local Development)
3447
1. Clone this repository
35-
2. Create a PostgreSQL database or use the provided Docker Compose setup
36-
3. Change the database configuration in `application.properties` if needed
37-
4. Make sure you have redis and postgresql running locally or use the Docker Compose setup
38-
4. Run the application with:
48+
2. Set up PostgreSQL and Redis (or use Docker Compose)
49+
3. Adjust `application.properties` as needed
50+
4. Run:
3951
```sh
4052
mvn spring-boot:run
4153
```
42-
5. Open [http://localhost:8080/api/v1/swagger-ui/index.html](http://localhost:8080/api/v1/swagger-ui/index.html) in your browser
54+
5. Open [http://localhost:8080/api/v1/swagger-ui/index.html](http://localhost:8080/api/v1/swagger-ui/index.html)
4355

44-
### API Usage
45-
1. Register a new user
46-
2. Login with the registered user
47-
3. Copy the JWT token from the response
48-
4. Click the `Authorize` button in the Swagger UI
49-
5. Paste the JWT token in the `Value` field without the `Bearer` prefix
50-
6. Click the `Authorize` button
51-
7. Now you can test the API endpoints
52-
8. To test the refresh token endpoint, click the `Authorize` button again and paste the refresh token in the `Value` field without the `Bearer` prefix
56+
---
5357

54-
![Docker Compose](https://imgur.com/qWLYDrg.png)
55-
![Maven Run](https://imgur.com/lGSrRLL.png)
56-
![Swagger UI](https://imgur.com/7Vug2XR.png)
57-
58-
## Environment Variables
59-
- You can override default database credentials and other settings using environment variables in `docker-compose.yml` or by creating a `.env` file in the project root.
58+
## Kafka Messaging
59+
- Kafka and Zookeeper are included in Docker Compose for local development.
60+
- REST endpoint `/kafka/send` allows sending messages to Kafka, with optional scheduling for future delivery.
61+
- Kafka consumer logs received messages.
62+
- Kafka logging is configurable in `application.properties`.
6063

61-
### Environment Variables for Redis
64+
---
6265

63-
You can configure Redis connection using environment variables (recommended for Docker Compose):
66+
## Environment Configuration
67+
- Use `.env` files for environment-specific variables.
68+
- Use Docker Compose override files for port and resource separation.
69+
- Example for test environment:
70+
```sh
71+
docker-compose -f docker-compose.yml -f docker-compose.test.override.yml --env-file env.test -p myapp_test up -d
72+
```
6473

65-
- `SPRING_DATA_REDIS_HOST` (default: `redis`)
66-
- `SPRING_DATA_REDIS_PORT` (default: `6379`)
74+
---
6775

68-
Example in `docker-compose.yml`:
76+
## API Usage
77+
1. Register a new user
78+
2. Login and obtain a JWT token
79+
3. Use the token in Swagger UI via the `Authorize` button
80+
4. Access secured endpoints (e.g., Kafka messaging requires ADMIN role)
6981

70-
```yaml
71-
app:
72-
# ...existing code...
73-
environment:
74-
SPRING_DATA_REDIS_HOST: redis
75-
SPRING_DATA_REDIS_PORT: 6379
76-
# ...other env vars...
77-
```
82+
---
7883

79-
Redis will be available at port 6379 by default.
84+
## Project Abilities Summary
85+
- Modular REST API with security, rate limiting, and exception handling
86+
- PostgreSQL, Redis, Kafka, and Zookeeper integration (all containerized)
87+
- Role-based access control
88+
- Swagger/OpenAPI documentation
89+
- Environment isolation for dev, test, and prod
90+
- Configurable logging
91+
- Unit and integration tests
8092

81-
## Database Configuration
93+
---
8294

83-
- To connect to your PostgreSQL database:
84-
- Host: `db`
85-
- Port: `5432`
86-
- Username: as set in `docker-compose.yml` (default: `development`)
87-
- Password: as set in `docker-compose.yml` (default: `T3st!ng`)
95+
For more details, see the code and comments, or open an issue for questions!
8896

89-
## Contribute to this project
90-
If you want to contribute to this project, please create a pull request.

docker-compose.yml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,32 @@ services:
4141
networks:
4242
- bridge_network
4343

44+
zookeeper:
45+
image: confluentinc/cp-zookeeper:7.5.0
46+
container_name: zookeeper
47+
environment:
48+
ZOOKEEPER_CLIENT_PORT: 2181
49+
ZOOKEEPER_TICK_TIME: 2000
50+
ports:
51+
- "2181:2181"
52+
networks:
53+
- bridge_network
54+
55+
kafka:
56+
image: confluentinc/cp-kafka:7.5.0
57+
container_name: kafka
58+
depends_on:
59+
- zookeeper
60+
ports:
61+
- "9092:9092"
62+
environment:
63+
KAFKA_BROKER_ID: 1
64+
KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
65+
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:9092
66+
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
67+
networks:
68+
- bridge_network
69+
4470
volumes:
4571
postgres_data:
4672

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
<groupId>com.tzesh</groupId>
1313
<artifactId>spring-boot-template</artifactId>
14-
<version>2.0</version>
14+
<version>2.1</version>
1515
<name>spring-boot-template</name>
1616
<description>spring-boot-template</description>
1717

spring-boot-template-api/pom.xml

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,19 @@
55
<parent>
66
<groupId>com.tzesh</groupId>
77
<artifactId>spring-boot-template</artifactId>
8-
<version>2.0</version>
8+
<version>2.1</version>
99
<relativePath>../pom.xml</relativePath>
1010
</parent>
1111

1212
<artifactId>spring-boot-template-api</artifactId>
13-
<version>2.0</version>
13+
<version>2.1</version>
1414
<name>spring-boot-template-api</name>
1515
<description>spring-boot-template-api</description>
1616
<dependencies>
1717
<dependency>
1818
<groupId>com.tzesh</groupId>
1919
<artifactId>spring-boot-template-core</artifactId>
20-
<version>2.0</version>
20+
<version>2.1</version>
2121
<scope>compile</scope>
2222
</dependency>
2323
<dependency>
@@ -35,6 +35,11 @@
3535
<artifactId>redisson</artifactId>
3636
<version>3.27.2</version>
3737
</dependency>
38+
<dependency>
39+
<groupId>org.springframework.kafka</groupId>
40+
<artifactId>spring-kafka</artifactId>
41+
<version>3.1.2</version>
42+
</dependency>
3843
</dependencies>
3944
<packaging>jar</packaging>
4045

spring-boot-template-api/src/main/java/com/tzesh/springtemplate/config/OpenApiConfig.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@
2121
email = "[email protected]",
2222
url = "https://ugurdindar.com"
2323
),
24-
description = "Spring Boot Template for RESTful API that uses JWT for authentication and authorization, PostgreSQL for database, Hibernate for ORM, and Lombok for boilerplate code generation and MapStruct for mapping DTOs to entities and vice versa.",
24+
description = "Production-ready Spring Boot template for secure REST APIs with JWT, PostgreSQL, Hibernate, Redis, Kafka, Zookeeper, MapStruct, Lombok, and Docker Compose. Fast, modern, and cloud-ready.",
2525
title = "Spring Boot Template",
26-
version = "0.3.0",
26+
version = "2.1.0",
2727
license = @License(
2828
name = "GitHub Repository",
2929
url = "https://github.com/tzesh/SpringBootTemplate"

spring-boot-template-api/src/main/java/com/tzesh/springtemplate/controller/auth/AuthenticationController.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
@RequestMapping("/auth")
3030
@RequiredArgsConstructor
3131
@Validated
32-
@Tag(name = "1. Authentication Controller", description = "Authentication operations for users")
32+
@Tag(name = "Authentication Controller", description = "Authentication operations for users")
3333
public class AuthenticationController {
3434

3535
private final AuthenticationService authenticationService;
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package com.tzesh.springtemplate.controller.kafka;
2+
3+
import com.tzesh.springtemplate.service.KafkaProducerService;
4+
import org.springframework.beans.factory.annotation.Autowired;
5+
import org.springframework.web.bind.annotation.*;
6+
import org.springframework.security.access.prepost.PreAuthorize;
7+
import io.swagger.v3.oas.annotations.Operation;
8+
import io.swagger.v3.oas.annotations.tags.Tag;
9+
import io.swagger.v3.oas.annotations.Parameter;
10+
import io.swagger.v3.oas.annotations.responses.ApiResponse;
11+
import io.swagger.v3.oas.annotations.responses.ApiResponses;
12+
13+
@Tag(name = "Kafka Controller", description = "Endpoints for testing Kafka integration.")
14+
@RestController
15+
@RequestMapping("/kafka")
16+
public class KafkaController {
17+
@Autowired
18+
private KafkaProducerService kafkaProducerService;
19+
20+
@Operation(summary = "Send a message to Kafka", description = "Sends a message to the 'test-topic' topic in Kafka. Requires ADMIN role.")
21+
@ApiResponses(value = {
22+
@ApiResponse(responseCode = "200", description = "Message sent successfully"),
23+
@ApiResponse(responseCode = "403", description = "Forbidden - Only ADMIN role allowed"),
24+
@ApiResponse(responseCode = "401", description = "Unauthorized")
25+
})
26+
@PostMapping("/send")
27+
@PreAuthorize("hasRole('ADMIN')")
28+
public String sendMessage(@Parameter(description = "Message to send to Kafka", required = true)
29+
@RequestParam String message) {
30+
kafkaProducerService.sendMessage(message);
31+
return "Message sent to Kafka: " + message;
32+
}
33+
}
34+

spring-boot-template-api/src/main/java/com/tzesh/springtemplate/controller/user/UserController.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
*/
2626
@RestController
2727
@RequestMapping("/users")
28-
@Tag(name = "2. User Controller", description = "User operations")
28+
@Tag(name = "User Controller", description = "User operations")
2929
@RequiredArgsConstructor
3030
@Validated
3131
public class UserController {
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package com.tzesh.springtemplate.service;
2+
3+
import org.apache.kafka.clients.consumer.ConsumerRecord;
4+
import org.slf4j.Logger;
5+
import org.slf4j.LoggerFactory;
6+
import org.springframework.kafka.annotation.KafkaListener;
7+
import org.springframework.stereotype.Service;
8+
9+
@Service
10+
public class KafkaConsumerService {
11+
private static final Logger logger = LoggerFactory.getLogger(KafkaConsumerService.class);
12+
13+
@KafkaListener(topics = "test-topic", groupId = "spring-template-group")
14+
public void listen(ConsumerRecord<String, String> record) {
15+
logger.info("Received message: {}", record.value());
16+
}
17+
}
18+
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package com.tzesh.springtemplate.service;
2+
3+
import org.springframework.beans.factory.annotation.Autowired;
4+
import org.springframework.kafka.core.KafkaTemplate;
5+
import org.springframework.stereotype.Service;
6+
7+
@Service
8+
public class KafkaProducerService {
9+
private static final String TOPIC = "test-topic";
10+
11+
@Autowired
12+
private KafkaTemplate<String, String> kafkaTemplate;
13+
14+
public void sendMessage(String message) {
15+
kafkaTemplate.send(TOPIC, message);
16+
}
17+
}
18+

0 commit comments

Comments
 (0)